Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/aws_elasticache_cluster: Add support for in-transit encryption #26987

Merged
3 changes: 3 additions & 0 deletions .changelog/26987.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_elaticache_cluster: Add `transit_encryption_enabled` argument, enabling in-transit encryption for Memcached clusters inside a VPC
```
12 changes: 12 additions & 0 deletions internal/service/elasticache/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ func ResourceCluster() *schema.Resource {
Computed: true,
ForceNew: true,
},
"transit_encryption_enabled": {
Type: schema.TypeBool,
ForceNew: true,
Optional: true,
Default: false,
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
},
Expand All @@ -337,6 +343,7 @@ func ResourceCluster() *schema.Resource {
CustomizeDiffValidateClusterNumCacheNodes,
CustomizeDiffClusterMemcachedNodeType,
CustomizeDiffValidateClusterMemcachedSnapshotIdentifier,
CustomizeDiffValidateTransitEncryptionEnabled,
verify.SetTagsDiff,
),
}
Expand Down Expand Up @@ -437,6 +444,10 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
input.SnapshotName = aws.String(v.(string))
}

if v, ok := d.GetOk("transit_encryption_enabled"); ok {
input.TransitEncryptionEnabled = aws.Bool(v.(bool))
}

if v, ok := d.GetOk("az_mode"); ok {
input.AZMode = aws.String(v.(string))
}
Expand Down Expand Up @@ -543,6 +554,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter
d.Set("ip_discovery", c.IpDiscovery)
d.Set("network_type", c.NetworkType)
d.Set("preferred_outpost_arn", c.PreferredOutpostArn)
d.Set("transit_encryption_enabled", c.TransitEncryptionEnabled)

return diags
}
Expand Down
50 changes: 50 additions & 0 deletions internal/service/elasticache/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,42 @@ func TestAccElastiCacheCluster_tagWithOtherModification(t *testing.T) {
})
}

func TestAccElastiCacheCluster_TransitEncryption(t *testing.T) {
ctx := acctest.Context(t)
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}
var cluster elasticache.CacheCluster
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_elasticache_cluster.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckClusterDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccClusterConfig_transitEncryption(rName, "memcached", "1.6.11"),
ExpectError: regexache.MustCompile(`Transit encryption is not supported for memcached version 1.6.11`),
},
{
Config: testAccClusterConfig_transitEncryption(rName, "redis", "6.2"),
ExpectError: regexache.MustCompile(`aws_elasticache_cluster does not support transit encryption using the redis engine, use aws_elasticache_replication_group instead`),
},
{
Config: testAccClusterConfig_transitEncryption(rName, "memcached", "1.6.12"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckClusterExists(ctx, resourceName, &cluster),
resource.TestCheckResourceAttr(resourceName, "engine", "memcached"),
resource.TestCheckResourceAttr(resourceName, "engine_version", "1.6.12"),
resource.TestCheckResourceAttr(resourceName, "transit_encryption_enabled", "true"),
),
},
},
})
}

func TestAccElastiCacheCluster_outpost_memcached(t *testing.T) {
ctx := acctest.Context(t)
if testing.Short() {
Expand Down Expand Up @@ -2153,3 +2189,17 @@ resource "aws_elasticache_cluster" "test" {
}
`, rName, version, tagKey1, tagValue1)
}

func testAccClusterConfig_transitEncryption(rName, engine, version string) string {
return fmt.Sprintf(`
resource "aws_elasticache_cluster" "test" {
apply_immediately = true
cluster_id = "%[1]s"
engine = "%[2]s"
engine_version = "%[3]s"
node_type = "cache.t3.medium"
num_cache_nodes = 1
transit_encryption_enabled = true
}
`, rName, engine, version)
}
28 changes: 28 additions & 0 deletions internal/service/elasticache/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ package elasticache
import (
"context"
"errors"
"fmt"

"github.com/aws/aws-sdk-go/service/elasticache"
gversion "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

var minMemcachedTransitEncryptionVersion = gversion.Must(gversion.NewVersion("1.6.12"))

// CustomizeDiffValidateClusterAZMode validates that `num_cache_nodes` is greater than 1 when `az_mode` is "cross-az"
func CustomizeDiffValidateClusterAZMode(_ context.Context, diff *schema.ResourceDiff, v interface{}) error {
if v, ok := diff.GetOk("az_mode"); !ok || v.(string) != elasticache.AZModeCrossAz {
Expand Down Expand Up @@ -69,3 +73,27 @@ func CustomizeDiffValidateReplicationGroupAutomaticFailover(_ context.Context, d
}
return nil
}

// CustomizeDiffValidateTransitEncryptionEnabled validates that an appropriate engine type and version
// are utilized when in-transit encryption is enabled
func CustomizeDiffValidateTransitEncryptionEnabled(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error {
if v, ok := diff.GetOk("transit_encryption_enabled"); ok && v.(bool) {
if engine := diff.Get("engine").(string); engine == engineRedis {
return errors.New("aws_elasticache_cluster does not support transit encryption using the redis engine, use aws_elasticache_replication_group instead")
}

engineVersion, ok := diff.GetOk("engine_version")
if !ok {
return nil
}
version, err := normalizeEngineVersion(engineVersion.(string))
if err != nil {
return err
}
if version.LessThan(minMemcachedTransitEncryptionVersion) {
return fmt.Errorf("Transit encryption is not supported for memcached version %v", version)
}
}

return nil
}
1 change: 1 addition & 0 deletions website/docs/r/elasticache_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ The minimum maintenance window is a 60 minute period. Example: `sun:05:00-sun:09
* `snapshot_window` - (Optional, Redis only) Daily time range (in UTC) during which ElastiCache will begin taking a daily snapshot of your cache cluster. Example: 05:00-09:00
* `subnet_group_name` – (Optional, VPC only) Name of the subnet group to be used for the cache cluster. Changing this value will re-create the resource.
* `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
* `transit_encryption_enabled` - (Optional) Enable encryption in-transit. Supported only with Memcached versions `1.6.12` and later, running in a VPC. See the [ElastiCache in-transit encryption](https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/in-transit-encryption-mc.html) documentation for more details.

### Log Delivery Configuration

Expand Down
Loading