From 241a48e599e607228e53aefbc0bfdfa10b3614f2 Mon Sep 17 00:00:00 2001 From: tabito Date: Fri, 22 Aug 2025 02:11:25 +0900 Subject: [PATCH 1/6] Implement ipv6_allowed_for_dual_stack argument --- internal/service/synthetics/canary.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/service/synthetics/canary.go b/internal/service/synthetics/canary.go index 0dab5a4d20b6..b4b3913c4aa7 100644 --- a/internal/service/synthetics/canary.go +++ b/internal/service/synthetics/canary.go @@ -243,6 +243,10 @@ func ResourceCanary() *schema.Resource { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "ipv6_allowed_for_dual_stack": { + Type: schema.TypeBool, + Optional: true, + }, names.AttrSecurityGroupIDs: { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, @@ -746,6 +750,10 @@ func flattenCanaryVPCConfig(canaryVpcOutput *awstypes.VpcConfigOutput) []any { names.AttrVPCID: aws.ToString(canaryVpcOutput.VpcId), } + if canaryVpcOutput.Ipv6AllowedForDualStack != nil { + m["ipv6_allowed_for_dual_stack"] = aws.ToBool(canaryVpcOutput.Ipv6AllowedForDualStack) + } + return []any{m} } @@ -761,6 +769,10 @@ func expandCanaryVPCConfig(l []any) *awstypes.VpcConfigInput { SecurityGroupIds: flex.ExpandStringValueSet(m[names.AttrSecurityGroupIDs].(*schema.Set)), } + if v, ok := m["ipv6_allowed_for_dual_stack"]; ok { + codeConfig.Ipv6AllowedForDualStack = aws.Bool(v.(bool)) + } + return codeConfig } From c4e952abbfcd1fede1c0ab73e1aad81e7f8b22f7 Mon Sep 17 00:00:00 2001 From: tabito Date: Fri, 22 Aug 2025 02:11:58 +0900 Subject: [PATCH 2/6] Add an acceptance test for ipv6_allowed_for_dual_stack --- internal/service/synthetics/canary_test.go | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/internal/service/synthetics/canary_test.go b/internal/service/synthetics/canary_test.go index f01972f94439..72e00c33c1ad 100644 --- a/internal/service/synthetics/canary_test.go +++ b/internal/service/synthetics/canary_test.go @@ -506,6 +506,7 @@ func TestAccSyntheticsCanary_vpc(t *testing.T) { testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtFalse), resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), ), }, @@ -521,6 +522,7 @@ func TestAccSyntheticsCanary_vpc(t *testing.T) { testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtFalse), resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), ), }, @@ -530,6 +532,7 @@ func TestAccSyntheticsCanary_vpc(t *testing.T) { testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtFalse), resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), ), }, @@ -537,6 +540,72 @@ func TestAccSyntheticsCanary_vpc(t *testing.T) { }) } +func TestAccSyntheticsCanary_vpcIpv6AllowedForDualStack(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var conf awstypes.Canary + rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(8)) + resourceName := "aws_synthetics_canary.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.SyntheticsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCanaryDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCanaryExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtTrue), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"zip_file", "start_canary", "delete_lambda"}, + }, + { + Config: testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCanaryExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtFalse), + ), + }, + { + Config: testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCanaryExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtTrue), + ), + }, + { + Config: testAccCanaryConfig_vpcIpv6AllowedForDualStackUpdated(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCanaryExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "2"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", "aws_vpc.test", names.AttrID), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", acctest.CtFalse), + ), + }, + }, + }) +} + func TestAccSyntheticsCanary_tags(t *testing.T) { ctx := acctest.Context(t) var conf awstypes.Canary @@ -1263,6 +1332,66 @@ resource "aws_synthetics_canary" "test" { `, rName)) } +func testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName string, ipv6 bool) string { + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + acctest.ConfigVPCWithSubnetsIPv6(rName, 2), + testAccCanarySecurityGroupBaseConfig(rName, 2), + fmt.Sprintf(` +resource "aws_synthetics_canary" "test" { + name = %[1]q + artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" + execution_role_arn = aws_iam_role.test.arn + handler = "exports.handler" + zip_file = "test-fixtures/lambdatest.zip" + runtime_version = data.aws_synthetics_runtime_version.test.version_name + delete_lambda = true + + schedule { + expression = "rate(0 minute)" + } + + vpc_config { + subnet_ids = aws_subnet.test[*].id + security_group_ids = aws_security_group.test[*].id + + ipv6_allowed_for_dual_stack = %[2]t + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName, ipv6)) +} + +func testAccCanaryConfig_vpcIpv6AllowedForDualStackUpdated(rName string) string { + return acctest.ConfigCompose( + testAccCanaryConfig_base(rName), + acctest.ConfigVPCWithSubnetsIPv6(rName, 2), + testAccCanarySecurityGroupBaseConfig(rName, 2), + fmt.Sprintf(` +resource "aws_synthetics_canary" "test" { + name = %[1]q + artifact_s3_location = "s3://${aws_s3_bucket.test.bucket}/" + execution_role_arn = aws_iam_role.test.arn + handler = "exports.handler" + zip_file = "test-fixtures/lambdatest.zip" + runtime_version = data.aws_synthetics_runtime_version.test.version_name + delete_lambda = true + + schedule { + expression = "rate(0 minute)" + } + + vpc_config { + subnet_ids = aws_subnet.test[*].id + security_group_ids = aws_security_group.test[*].id + } + + depends_on = [aws_iam_role_policy_attachment.test] +} +`, rName)) +} + func testAccCanaryConfig_tags1(rName, tagKey1, tagValue1 string) string { return acctest.ConfigCompose( testAccCanaryConfig_base(rName), From 552cb00f41669dc6f11691e322d51b3f9f6c68d4 Mon Sep 17 00:00:00 2001 From: tabito Date: Fri, 22 Aug 2025 02:12:47 +0900 Subject: [PATCH 3/6] Update the documentation to include ipv6_allowed_for_dual_stack argument --- website/docs/r/synthetics_canary.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/synthetics_canary.html.markdown b/website/docs/r/synthetics_canary.html.markdown index de81abbd18cf..42c3da1d5f90 100644 --- a/website/docs/r/synthetics_canary.html.markdown +++ b/website/docs/r/synthetics_canary.html.markdown @@ -83,6 +83,7 @@ If this canary tests an endpoint in a VPC, this structure contains information a * `subnet_ids` - (Required) IDs of the subnets where this canary is to run. * `security_group_ids` - (Required) IDs of the security groups for this canary. +* `ipv6_allowed_for_dual_stack` - (Optional) If `true`, allow outbound IPv6 traffic on VPC canaries that are connected to dual-stack subnets. The default is `false`. ## Attribute Reference From 5a89601f7f4920a5e4640a919f59b3e3a5be9b81 Mon Sep 17 00:00:00 2001 From: tabito Date: Fri, 22 Aug 2025 02:22:00 +0900 Subject: [PATCH 4/6] add changelog --- .changelog/43989.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/43989.txt diff --git a/.changelog/43989.txt b/.changelog/43989.txt new file mode 100644 index 000000000000..06ec6652bcc9 --- /dev/null +++ b/.changelog/43989.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_synthetics_canary: Add `vpc_config.ipv6_allowed_for_dual_stack` argument +``` From a751d0de004ad95a62ff22641a095769fcc31708 Mon Sep 17 00:00:00 2001 From: tabito Date: Fri, 22 Aug 2025 08:22:48 +0900 Subject: [PATCH 5/6] Fix expected run_config.0.memory_in_mb values --- internal/service/synthetics/canary_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/synthetics/canary_test.go b/internal/service/synthetics/canary_test.go index 72e00c33c1ad..506236dc53ba 100644 --- a/internal/service/synthetics/canary_test.go +++ b/internal/service/synthetics/canary_test.go @@ -41,7 +41,7 @@ func TestAccSyntheticsCanary_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttrPair(resourceName, "runtime_version", runtimeVersionDataSourceName, "version_name"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, "0"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1500"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), resource.TestCheckResourceAttr(resourceName, "success_retention_period", "31"), @@ -73,7 +73,7 @@ func TestAccSyntheticsCanary_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttrPair(resourceName, "runtime_version", runtimeVersionDataSourceName, "version_name"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, "0"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1500"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), resource.TestCheckResourceAttr(resourceName, "success_retention_period", "31"), @@ -330,7 +330,7 @@ func TestAccSyntheticsCanary_s3(t *testing.T) { resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), resource.TestCheckResourceAttrPair(resourceName, "runtime_version", runtimeVersionDataSourceName, "version_name"), resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, "0"), - resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1500"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "840"), resource.TestCheckResourceAttr(resourceName, "run_config.0.active_tracing", acctest.CtFalse), resource.TestCheckResourceAttr(resourceName, "failure_retention_period", "31"), @@ -372,7 +372,7 @@ func TestAccSyntheticsCanary_run(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), - resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1000"), + resource.TestCheckResourceAttr(resourceName, "run_config.0.memory_in_mb", "1500"), resource.TestCheckResourceAttr(resourceName, "run_config.0.timeout_in_seconds", "60"), ), }, From 9683d7d94044dd2e8328b286186f7dba5988040b Mon Sep 17 00:00:00 2001 From: tabito Date: Fri, 22 Aug 2025 08:23:25 +0900 Subject: [PATCH 6/6] Fix the function names to satisfy the Caps rules --- internal/service/synthetics/canary_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/service/synthetics/canary_test.go b/internal/service/synthetics/canary_test.go index 506236dc53ba..9ae5f6732f4b 100644 --- a/internal/service/synthetics/canary_test.go +++ b/internal/service/synthetics/canary_test.go @@ -540,7 +540,7 @@ func TestAccSyntheticsCanary_vpc(t *testing.T) { }) } -func TestAccSyntheticsCanary_vpcIpv6AllowedForDualStack(t *testing.T) { +func TestAccSyntheticsCanary_vpcIPv6AllowedForDualStack(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { t.Skip("skipping long-running test in short mode") @@ -557,7 +557,7 @@ func TestAccSyntheticsCanary_vpcIpv6AllowedForDualStack(t *testing.T) { CheckDestroy: testAccCheckCanaryDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName, true), + Config: testAccCanaryConfig_vpcIPv6AllowedForDualStack(rName, true), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), @@ -573,7 +573,7 @@ func TestAccSyntheticsCanary_vpcIpv6AllowedForDualStack(t *testing.T) { ImportStateVerifyIgnore: []string{"zip_file", "start_canary", "delete_lambda"}, }, { - Config: testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName, false), + Config: testAccCanaryConfig_vpcIPv6AllowedForDualStack(rName, false), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), @@ -583,7 +583,7 @@ func TestAccSyntheticsCanary_vpcIpv6AllowedForDualStack(t *testing.T) { ), }, { - Config: testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName, true), + Config: testAccCanaryConfig_vpcIPv6AllowedForDualStack(rName, true), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), @@ -593,7 +593,7 @@ func TestAccSyntheticsCanary_vpcIpv6AllowedForDualStack(t *testing.T) { ), }, { - Config: testAccCanaryConfig_vpcIpv6AllowedForDualStackUpdated(rName), + Config: testAccCanaryConfig_vpcIPv6AllowedForDualStackUpdated(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckCanaryExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "2"), @@ -1332,7 +1332,7 @@ resource "aws_synthetics_canary" "test" { `, rName)) } -func testAccCanaryConfig_vpcIpv6AllowedForDualStack(rName string, ipv6 bool) string { +func testAccCanaryConfig_vpcIPv6AllowedForDualStack(rName string, ipv6 bool) string { return acctest.ConfigCompose( testAccCanaryConfig_base(rName), acctest.ConfigVPCWithSubnetsIPv6(rName, 2), @@ -1363,7 +1363,7 @@ resource "aws_synthetics_canary" "test" { `, rName, ipv6)) } -func testAccCanaryConfig_vpcIpv6AllowedForDualStackUpdated(rName string) string { +func testAccCanaryConfig_vpcIPv6AllowedForDualStackUpdated(rName string) string { return acctest.ConfigCompose( testAccCanaryConfig_base(rName), acctest.ConfigVPCWithSubnetsIPv6(rName, 2),