From 79ab8513804387a6ac4aa0fe3ebf6e5c103b3565 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Tue, 27 Sep 2022 01:31:32 -0600 Subject: [PATCH 1/7] r/aws_elasticache_cluster: Add support for in-transit encryption Fixes: #26367 --- internal/service/elasticache/cluster.go | 11 +++++ internal/service/elasticache/cluster_test.go | 45 +++++++++++++++++++ .../service/elasticache/transit_encryption.go | 39 ++++++++++++++++ .../docs/r/elasticache_cluster.html.markdown | 1 + 4 files changed, 96 insertions(+) create mode 100644 internal/service/elasticache/transit_encryption.go diff --git a/internal/service/elasticache/cluster.go b/internal/service/elasticache/cluster.go index 33d65532c145..cedc018833f0 100644 --- a/internal/service/elasticache/cluster.go +++ b/internal/service/elasticache/cluster.go @@ -323,6 +323,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(), }, @@ -334,6 +340,7 @@ func ResourceCluster() *schema.Resource { CustomizeDiffValidateClusterNumCacheNodes, CustomizeDiffClusterMemcachedNodeType, CustomizeDiffValidateClusterMemcachedSnapshotIdentifier, + CustomizeDiffValidateTransitEncryptionEnabled, verify.SetTagsDiff, ), } @@ -434,6 +441,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 91c4cb3ab1ab..92d8096c5e27 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(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: 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 b683d44c4385..522d67d0f3ca 100644 --- a/website/docs/r/elasticache_cluster.html.markdown +++ b/website/docs/r/elasticache_cluster.html.markdown @@ -180,6 +180,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 From 16f42056a980fa8fa3ee703c70c3c83399c0ea75 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 6 Sep 2023 10:33:19 -0400 Subject: [PATCH 2/7] r/aws_elasticache_cluster(test): switch to regexache --- internal/service/elasticache/cluster_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/elasticache/cluster_test.go b/internal/service/elasticache/cluster_test.go index 7257aa32200a..4588c4b49039 100644 --- a/internal/service/elasticache/cluster_test.go +++ b/internal/service/elasticache/cluster_test.go @@ -1245,11 +1245,11 @@ func TestAccElastiCacheCluster_TransitEncryption(t *testing.T) { 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`), + ExpectError: regexache.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`), + 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"), From 5cf853f35667289e28923edacd8c737d2f82f8c3 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 6 Sep 2023 11:46:13 -0400 Subject: [PATCH 3/7] r/aws_elasticache_cluster(docs): tidy transit_encryption_enabled --- website/docs/r/elasticache_cluster.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/elasticache_cluster.html.markdown b/website/docs/r/elasticache_cluster.html.markdown index 57b5f52c157e..0c8cd878095d 100644 --- a/website/docs/r/elasticache_cluster.html.markdown +++ b/website/docs/r/elasticache_cluster.html.markdown @@ -181,7 +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). +* `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 From 54f9b777ff17505557306909f746ad229f79fcb1 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 6 Sep 2023 11:54:33 -0400 Subject: [PATCH 4/7] r/aws_elasticache_cluster: centralize customize diff func --- internal/service/elasticache/diff.go | 28 +++++++++++++ .../service/elasticache/transit_encryption.go | 39 ------------------- 2 files changed, 28 insertions(+), 39 deletions(-) delete mode 100644 internal/service/elasticache/transit_encryption.go diff --git a/internal/service/elasticache/diff.go b/internal/service/elasticache/diff.go index dfedae91963a..ebc58cd50f2d 100644 --- a/internal/service/elasticache/diff.go +++ b/internal/service/elasticache/diff.go @@ -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 { @@ -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 +} diff --git a/internal/service/elasticache/transit_encryption.go b/internal/service/elasticache/transit_encryption.go deleted file mode 100644 index 8f6b28ed4141..000000000000 --- a/internal/service/elasticache/transit_encryption.go +++ /dev/null @@ -1,39 +0,0 @@ -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 -} From 0215faec73741453a4d1e256879d158730489e4c Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 6 Sep 2023 14:42:59 -0400 Subject: [PATCH 5/7] r/aws_elasticache_cluster(test): extend test checks --- internal/service/elasticache/cluster_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/service/elasticache/cluster_test.go b/internal/service/elasticache/cluster_test.go index 4588c4b49039..6c88ea9a5116 100644 --- a/internal/service/elasticache/cluster_test.go +++ b/internal/service/elasticache/cluster_test.go @@ -1253,7 +1253,12 @@ func TestAccElastiCacheCluster_TransitEncryption(t *testing.T) { }, { Config: testAccClusterConfig_transitEncryption(rName, "memcached", "1.6.12"), - Check: testAccCheckClusterExists(ctx, resourceName, &cluster), + 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"), + ), }, }, }) From 9ee7b94f9ff826786add02a463fdeb745ebd3e65 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 7 Sep 2023 09:31:26 -0400 Subject: [PATCH 6/7] r/aws_elasticache_cluster: set transit_encryption_enabled on read --- internal/service/elasticache/cluster.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/elasticache/cluster.go b/internal/service/elasticache/cluster.go index 1cd1004e0da9..8622ff4324b3 100644 --- a/internal/service/elasticache/cluster.go +++ b/internal/service/elasticache/cluster.go @@ -554,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 } From 2fb6d64b440dc0fa52b4ba44d2664e937b1edc50 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 7 Sep 2023 09:37:04 -0400 Subject: [PATCH 7/7] chore: changelog --- .changelog/26987.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/26987.txt diff --git a/.changelog/26987.txt b/.changelog/26987.txt new file mode 100644 index 000000000000..b9ce5f7b025a --- /dev/null +++ b/.changelog/26987.txt @@ -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 +```