diff --git a/internal/service/elasticache/cluster.go b/internal/service/elasticache/cluster.go index a03b5e384998..8d6e5c6a0287 100644 --- a/internal/service/elasticache/cluster.go +++ b/internal/service/elasticache/cluster.go @@ -332,8 +332,12 @@ func ResourceCluster() *schema.Resource { Computed: true, ForceNew: true, }, - names.AttrTags: tftags.TagsSchema(), - names.AttrTagsAll: tftags.TagsSchemaComputed(), + "transit_encryption_enabled": { + Type: schema.TypeBool, + ForceNew: true, + Optional: true, + Default: false, + }, }, CustomizeDiff: customdiff.Sequence( @@ -343,6 +347,7 @@ func ResourceCluster() *schema.Resource { CustomizeDiffValidateClusterNumCacheNodes, CustomizeDiffClusterMemcachedNodeType, CustomizeDiffValidateClusterMemcachedSnapshotIdentifier, + CustomizeDiffValidateTransitEncryptionEnabled, verify.SetTagsDiff, ), } @@ -447,6 +452,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)) } diff --git a/internal/service/elasticache/cluster_test.go b/internal/service/elasticache/cluster_test.go index 4e7dd5d4d6d8..fe7923c00438 100644 --- a/internal/service/elasticache/cluster_test.go +++ b/internal/service/elasticache/cluster_test.go @@ -1225,6 +1225,37 @@ 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(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: regexp.MustCompile(`Transit encryption is not supported for memcached version 1.6.11`), + }, + { + Config: testAccClusterConfig_transitEncryption(rName, "redis", "6.2"), + ExpectError: regexp.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: testAccCheckClusterExists(ctx, resourceName, &cluster), + }, + }, + }) +} + func TestAccElastiCacheCluster_outpost_memcached(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { @@ -2152,3 +2183,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) +} diff --git a/internal/service/elasticache/transit_encryption.go b/internal/service/elasticache/transit_encryption.go new file mode 100644 index 000000000000..8f6b28ed4141 --- /dev/null +++ b/internal/service/elasticache/transit_encryption.go @@ -0,0 +1,39 @@ +package elasticache + +import ( + "context" + "fmt" + + gversion "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var minMemcachedTransitEncryptionVersion = gversion.Must(gversion.NewVersion("1.6.12")) + +func CustomizeDiffValidateTransitEncryptionEnabled(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error { + engine := diff.Get("engine").(string) + + transitEncryptionEnabled, ok := diff.GetOk("transit_encryption_enabled") + if !ok || !transitEncryptionEnabled.(bool) { + return nil + } + + if engine == engineRedis { + return fmt.Errorf("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 +} diff --git a/website/docs/r/elasticache_cluster.html.markdown b/website/docs/r/elasticache_cluster.html.markdown index 2b711f45bb10..92c32c131af3 100644 --- a/website/docs/r/elasticache_cluster.html.markdown +++ b/website/docs/r/elasticache_cluster.html.markdown @@ -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 Memcached only, VPC Only) If true, enable encryption in transit. See the documentation for [ElastiCache in-transit encryption](https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/in-transit-encryption-mc.html). ## Attributes Reference