diff --git a/.changelog/28306.txt b/.changelog/28306.txt new file mode 100644 index 00000000000..e6c41848c12 --- /dev/null +++ b/.changelog/28306.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_elasticache_replication_group: Add `ip_discovery` and `network_type` arguments in support of IPv6 clusters. +``` \ No newline at end of file diff --git a/internal/service/elasticache/replication_group.go b/internal/service/elasticache/replication_group.go index 5c71075492d..ff1ce235d41 100644 --- a/internal/service/elasticache/replication_group.go +++ b/internal/service/elasticache/replication_group.go @@ -161,6 +161,12 @@ func ResourceReplicationGroup() *schema.Resource { "snapshot_name", }, }, + "ip_discovery": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(elasticache.IpDiscovery_Values(), false), + }, "log_delivery_configuration": { Type: schema.TypeSet, Optional: true, @@ -210,6 +216,13 @@ func ResourceReplicationGroup() *schema.Resource { Optional: true, Default: false, }, + "network_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(elasticache.NetworkType_Values(), false), + }, "node_type": { Type: schema.TypeString, Optional: true, @@ -461,6 +474,14 @@ func resourceReplicationGroupCreate(d *schema.ResourceData, meta interface{}) er params.CacheParameterGroupName = aws.String(v.(string)) } + if v, ok := d.GetOk("ip_discovery"); ok { + params.IpDiscovery = aws.String(v.(string)) + } + + if v, ok := d.GetOk("network_type"); ok { + params.NetworkType = aws.String(v.(string)) + } + if v, ok := d.GetOk("port"); ok { params.Port = aws.Int64(int64(v.(int))) } @@ -676,7 +697,8 @@ func resourceReplicationGroupRead(d *schema.ResourceData, meta interface{}) erro d.Set("replication_group_id", rgp.ReplicationGroupId) d.Set("arn", rgp.ARN) d.Set("data_tiering_enabled", aws.StringValue(rgp.DataTiering) == elasticache.DataTieringStatusEnabled) - + d.Set("ip_discovery", rgp.IpDiscovery) + d.Set("network_type", rgp.NetworkType) d.Set("log_delivery_configuration", flattenLogDeliveryConfigurations(rgp.LogDeliveryConfigurations)) d.Set("snapshot_window", rgp.SnapshotWindow) d.Set("snapshot_retention_limit", rgp.SnapshotRetentionLimit) @@ -816,6 +838,16 @@ func resourceReplicationGroupUpdate(d *schema.ResourceData, meta interface{}) er requestUpdate = true } + if d.HasChange("ip_discovery") { + params.IpDiscovery = aws.String(d.Get("ip_discovery").(string)) + requestUpdate = true + } + + if d.HasChange("network_type") { + params.IpDiscovery = aws.String(d.Get("network_type").(string)) + requestUpdate = true + } + if d.HasChange("automatic_failover_enabled") { params.AutomaticFailoverEnabled = aws.Bool(d.Get("automatic_failover_enabled").(bool)) requestUpdate = true diff --git a/internal/service/elasticache/replication_group_test.go b/internal/service/elasticache/replication_group_test.go index 4940d0cf4b8..4a049578cb9 100644 --- a/internal/service/elasticache/replication_group_test.go +++ b/internal/service/elasticache/replication_group_test.go @@ -66,6 +66,86 @@ func TestAccElastiCacheReplicationGroup_basic(t *testing.T) { }) } +func TestAccElastiCacheReplicationGroup_networkTypeIPv6(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var rg elasticache.ReplicationGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckReplicationGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccReplicationGroupConfig_ipDiscovery(rName, "ipv6", "ipv6"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(resourceName, &rg), + resource.TestCheckResourceAttr(resourceName, "engine", "redis"), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "elasticache", fmt.Sprintf("replicationgroup:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x"), + resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "engine_version", "6.2"), + resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexp.MustCompile(`^6\.[[:digit:]]+\.[[:digit:]]+$`)), + resource.TestCheckResourceAttr(resourceName, "ip_discovery", "ipv6"), + resource.TestCheckResourceAttr(resourceName, "network_type", "ipv6"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately"}, //not in the API + }, + }, + }) +} + +func TestAccElastiCacheReplicationGroup_networkTypeDualStack(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var rg elasticache.ReplicationGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elasticache_replication_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elasticache.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckReplicationGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccReplicationGroupConfig_ipDiscovery(rName, "ipv6", "dual_stack"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckReplicationGroupExists(resourceName, &rg), + resource.TestCheckResourceAttr(resourceName, "engine", "redis"), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "elasticache", fmt.Sprintf("replicationgroup:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "member_clusters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "parameter_group_name", "default.redis6.x"), + resource.TestCheckResourceAttr(resourceName, "cluster_enabled", "false"), + resource.TestCheckResourceAttr(resourceName, "engine_version", "6.2"), + resource.TestMatchResourceAttr(resourceName, "engine_version_actual", regexp.MustCompile(`^6\.[[:digit:]]+\.[[:digit:]]+$`)), + resource.TestCheckResourceAttr(resourceName, "ip_discovery", "ipv6"), + resource.TestCheckResourceAttr(resourceName, "network_type", "dual_stack"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately"}, //not in the API + }, + }, + }) +} + func TestAccElastiCacheReplicationGroup_basic_v5(t *testing.T) { if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -3796,6 +3876,44 @@ data "aws_elasticache_replication_group" "test" { `, rName, enableClusterMode, slowLogDeliveryEnabled, slowDeliveryDestination, slowDeliveryFormat, engineLogDeliveryEnabled, engineDeliveryDestination, engineLogDeliveryFormat) } +func testAccReplicationGroupConfig_ipDiscovery(rName, ipDiscovery, networkType string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnetsIPv6(rName, 1), fmt.Sprintf(` +resource "aws_elasticache_subnet_group" "test" { + name = %[1]q + description = %[1]q + subnet_ids = aws_subnet.test[*].id + } + +resource "aws_security_group" "test" { + name = %[1]q + description = %[1]q + vpc_id = aws_vpc.test.id + ingress { + from_port = -1 + to_port = -1 + protocol = "icmp" + cidr_blocks = ["0.0.0.0/0"] + } + tags = { + Name = %[1]q + } +} + +resource "aws_elasticache_replication_group" "test" { + replication_group_id = %[1]q + replication_group_description = "test description" + engine = "redis" + engine_version = "6.2" + node_type = "cache.t3.small" + ip_discovery = %[2]q + network_type = %[3]q + + subnet_group_name = aws_elasticache_subnet_group.test.name + security_group_ids = [aws_security_group.test.id] +} +`, rName, ipDiscovery, networkType)) +} + func resourceReplicationGroupDisableAutomaticFailover(conn *elasticache.ElastiCache, replicationGroupID string, timeout time.Duration) error { return resourceReplicationGroupModify(conn, timeout, &elasticache.ModifyReplicationGroupInput{ ReplicationGroupId: aws.String(replicationGroupID), diff --git a/website/docs/r/elasticache_replication_group.html.markdown b/website/docs/r/elasticache_replication_group.html.markdown index 25d3c9a3019..7eabcfd6b1d 100644 --- a/website/docs/r/elasticache_replication_group.html.markdown +++ b/website/docs/r/elasticache_replication_group.html.markdown @@ -190,10 +190,12 @@ The following arguments are optional: The actual engine version used is returned in the attribute `engine_version_actual`, see [Attributes Reference](#attributes-reference) below. * `final_snapshot_identifier` - (Optional) The name of your final node group (shard) snapshot. ElastiCache creates the snapshot from the primary node in the cluster. If omitted, no final snapshot will be made. * `global_replication_group_id` - (Optional) The ID of the global replication group to which this replication group should belong. If this parameter is specified, the replication group is added to the specified global replication group as a secondary replication group; otherwise, the replication group is not part of any global replication group. If `global_replication_group_id` is set, the `num_node_groups` parameter (or the `num_node_groups` parameter of the deprecated `cluster_mode` block) cannot be set. +* `ip_discovery` - (Optional) The IP version to advertise in the discovery protocol. Valid values are `ipv4` or `ipv6`. * `kms_key_id` - (Optional) The ARN of the key that you wish to use if encrypting at rest. If not supplied, uses service managed encryption. Can be specified only if `at_rest_encryption_enabled = true`. * `log_delivery_configuration` - (Optional, Redis only) Specifies the destination and format of Redis [SLOWLOG](https://redis.io/commands/slowlog) or Redis [Engine Log](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log). See the documentation on [Amazon ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Log_Delivery.html#Log_contents-engine-log). See [Log Delivery Configuration](#log-delivery-configuration) below for more details. * `maintenance_window` – (Optional) Specifies the weekly time range for when maintenance on the cache cluster is performed. The format is `ddd:hh24:mi-ddd:hh24:mi` (24H Clock UTC). The minimum maintenance window is a 60 minute period. Example: `sun:05:00-sun:09:00` * `multi_az_enabled` - (Optional) Specifies whether to enable Multi-AZ Support for the replication group. If `true`, `automatic_failover_enabled` must also be enabled. Defaults to `false`. +* `network_type` - (Optional) The IP versions for cache cluster connections. Valid values are `ipv4`, `ipv6` or `dual_stack`. * `node_type` - (Optional) Instance class to be used. See AWS documentation for information on [supported node types](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/CacheNodes.SupportedTypes.html) and [guidance on selecting node types](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/nodes-select-size.html). Required unless `global_replication_group_id` is set. Cannot be set if `global_replication_group_id` is set. * `notification_topic_arn` – (Optional) ARN of an SNS topic to send ElastiCache notifications to. Example: `arn:aws:sns:us-east-1:012345678999:my_sns_topic` * `number_cache_clusters` - (Optional, **Deprecated** use `num_cache_clusters` instead) Number of cache clusters (primary and replicas) this replication group will have. If Multi-AZ is enabled, the value of this parameter must be at least 2. Updates will occur before other modifications. Conflicts with `num_cache_clusters`, `num_node_groups`, or the deprecated `cluster_mode`. Defaults to `1`.