diff --git a/.changelog/34045.txt b/.changelog/34045.txt new file mode 100644 index 00000000000..2a48dd03b3a --- /dev/null +++ b/.changelog/34045.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_lambda_function: Add `vpc_config.ipv6_allowed_for_dual_stack` argument +``` diff --git a/internal/acctest/acctest.go b/internal/acctest/acctest.go index 9f151c73a12..35fda5149a7 100644 --- a/internal/acctest/acctest.go +++ b/internal/acctest/acctest.go @@ -2107,7 +2107,7 @@ func ConfigLambdaBase(policyName, roleName, sgName string) string { data "aws_partition" "current" {} resource "aws_iam_role_policy" "iam_policy_for_lambda" { - name = "%s" + name = %[1]q role = aws_iam_role.iam_for_lambda.id policy = < 0 && v.([]interface{})[0] != nil { tfMap := v.([]interface{})[0].(map[string]interface{}) input.VpcConfig = &types.VpcConfig{ - SecurityGroupIds: flex.ExpandStringValueSet(tfMap["security_group_ids"].(*schema.Set)), - SubnetIds: flex.ExpandStringValueSet(tfMap["subnet_ids"].(*schema.Set)), + Ipv6AllowedForDualStack: aws.Bool(tfMap["ipv6_allowed_for_dual_stack"].(bool)), + SecurityGroupIds: flex.ExpandStringValueSet(tfMap["security_group_ids"].(*schema.Set)), + SubnetIds: flex.ExpandStringValueSet(tfMap["subnet_ids"].(*schema.Set)), } } @@ -849,17 +856,19 @@ func resourceFunctionUpdate(ctx context.Context, d *schema.ResourceData, meta in } } - if d.HasChanges("vpc_config.0.security_group_ids", "vpc_config.0.subnet_ids") { + if d.HasChanges("vpc_config.0.security_group_ids", "vpc_config.0.subnet_ids", "vpc_config.0.ipv6_allowed_for_dual_stack") { if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { tfMap := v.([]interface{})[0].(map[string]interface{}) input.VpcConfig = &types.VpcConfig{ - SecurityGroupIds: flex.ExpandStringValueSet(tfMap["security_group_ids"].(*schema.Set)), - SubnetIds: flex.ExpandStringValueSet(tfMap["subnet_ids"].(*schema.Set)), + Ipv6AllowedForDualStack: aws.Bool(tfMap["ipv6_allowed_for_dual_stack"].(bool)), + SecurityGroupIds: flex.ExpandStringValueSet(tfMap["security_group_ids"].(*schema.Set)), + SubnetIds: flex.ExpandStringValueSet(tfMap["subnet_ids"].(*schema.Set)), } } else { input.VpcConfig = &types.VpcConfig{ - SecurityGroupIds: []string{}, - SubnetIds: []string{}, + Ipv6AllowedForDualStack: aws.Bool(false), + SecurityGroupIds: []string{}, + SubnetIds: []string{}, } } } @@ -1250,6 +1259,7 @@ func needsFunctionConfigUpdate(d verify.ResourceDiffer) bool { d.HasChange("dead_letter_config") || d.HasChange("snap_start") || d.HasChange("tracing_config") || + d.HasChange("vpc_config.0.ipv6_allowed_for_dual_stack") || d.HasChange("vpc_config.0.security_group_ids") || d.HasChange("vpc_config.0.subnet_ids") || d.HasChange("runtime") || diff --git a/internal/service/lambda/function_test.go b/internal/service/lambda/function_test.go index 6142bcabf19..db21f4d079f 100644 --- a/internal/service/lambda/function_test.go +++ b/internal/service/lambda/function_test.go @@ -1824,7 +1824,7 @@ func TestAccLambdaFunction_localUpdate(t *testing.T) { Config: testAccFunctionConfig_local(path, rName), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="), + testAccCheckSourceCodeHash(&conf, "MbW0T1Pcy1QPtrFC9dT7hUfircj1NXss2uXgakqzAbk="), ), }, { @@ -1843,7 +1843,7 @@ func TestAccLambdaFunction_localUpdate(t *testing.T) { Config: testAccFunctionConfig_local(path, rName), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "0tdaP9H9hsk9c2CycSwOG/sa/x5JyAmSYunA/ce99Pg="), + testAccCheckSourceCodeHash(&conf, "7qn3LZOWCpWK5nm49qjw+VrbPQHfdu2ZrDjBsSUveKM="), func(s *terraform.State) error { return testAccCheckAttributeIsDateAfter(s, resourceName, "last_modified", timeBeforeUpdate) }, @@ -1890,7 +1890,7 @@ func TestAccLambdaFunction_LocalUpdate_nameOnly(t *testing.T) { Config: testAccFunctionConfig_localNameOnly(path, rName), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="), + testAccCheckSourceCodeHash(&conf, "MbW0T1Pcy1QPtrFC9dT7hUfircj1NXss2uXgakqzAbk="), ), }, { @@ -1908,7 +1908,7 @@ func TestAccLambdaFunction_LocalUpdate_nameOnly(t *testing.T) { Config: testAccFunctionConfig_localNameOnly(updatedPath, rName), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "0tdaP9H9hsk9c2CycSwOG/sa/x5JyAmSYunA/ce99Pg="), + testAccCheckSourceCodeHash(&conf, "7qn3LZOWCpWK5nm49qjw+VrbPQHfdu2ZrDjBsSUveKM="), ), }, }, @@ -1945,7 +1945,7 @@ func TestAccLambdaFunction_S3Update_basic(t *testing.T) { Config: testAccFunctionConfig_s3(key, path, rName), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="), + testAccCheckSourceCodeHash(&conf, "MbW0T1Pcy1QPtrFC9dT7hUfircj1NXss2uXgakqzAbk="), ), }, { @@ -1964,7 +1964,7 @@ func TestAccLambdaFunction_S3Update_basic(t *testing.T) { Config: testAccFunctionConfig_s3(key, path, rName), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "0tdaP9H9hsk9c2CycSwOG/sa/x5JyAmSYunA/ce99Pg="), + testAccCheckSourceCodeHash(&conf, "7qn3LZOWCpWK5nm49qjw+VrbPQHfdu2ZrDjBsSUveKM="), ), }, }, @@ -2001,7 +2001,7 @@ func TestAccLambdaFunction_S3Update_unversioned(t *testing.T) { Config: testAccFunctionConfig_s3UnversionedTPL(rName, key, path), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="), + testAccCheckSourceCodeHash(&conf, "MbW0T1Pcy1QPtrFC9dT7hUfircj1NXss2uXgakqzAbk="), ), }, { @@ -2020,7 +2020,7 @@ func TestAccLambdaFunction_S3Update_unversioned(t *testing.T) { Config: testAccFunctionConfig_s3UnversionedTPL(rName, key2, path), Check: resource.ComposeTestCheckFunc( testAccCheckFunctionExists(ctx, resourceName, &conf), - testAccCheckSourceCodeHash(&conf, "0tdaP9H9hsk9c2CycSwOG/sa/x5JyAmSYunA/ce99Pg="), + testAccCheckSourceCodeHash(&conf, "7qn3LZOWCpWK5nm49qjw+VrbPQHfdu2ZrDjBsSUveKM="), ), }, }, @@ -2161,6 +2161,43 @@ func TestAccLambdaFunction_Zip_validation(t *testing.T) { }) } +func TestAccLambdaFunction_ipv6AllowedForDualStack(t *testing.T) { + ctx := acctest.Context(t) + var conf lambda.GetFunctionOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lambda_function.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.LambdaEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFunctionDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFunctionConfig_ipv6AllowedForDualStackDisabled(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFunctionExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"filename", "publish"}, + }, + { + Config: testAccFunctionConfig_ipv6AllowedForDualStackEnabled(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFunctionExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "vpc_config.0.ipv6_allowed_for_dual_stack", "true"), + ), + }, + }, + }) +} + func TestAccLambdaFunction_skipDestroy(t *testing.T) { ctx := acctest.Context(t) var conf lambda.GetFunctionOutput @@ -3869,6 +3906,45 @@ resource "aws_lambda_function" "test" { `, rName)) } +func testAccFunctionConfig_ipv6AllowedForDualStackDisabled(rName string) string { + return acctest.ConfigCompose( + acctest.ConfigLambdaBase(rName, rName, rName), + fmt.Sprintf(` +resource "aws_lambda_function" "test" { + filename = "test-fixtures/lambdatest.zip" + function_name = %[1]q + role = aws_iam_role.iam_for_lambda.arn + handler = "exports.example" + runtime = "nodejs16.x" + + vpc_config { + subnet_ids = [aws_subnet.subnet_for_lambda.id] + security_group_ids = [aws_security_group.sg_for_lambda.id] + } +} +`, rName)) +} + +func testAccFunctionConfig_ipv6AllowedForDualStackEnabled(rName string) string { + return acctest.ConfigCompose( + acctest.ConfigLambdaBase(rName, rName, rName), + fmt.Sprintf(` +resource "aws_lambda_function" "test" { + filename = "test-fixtures/lambdatest.zip" + function_name = %[1]q + role = aws_iam_role.iam_for_lambda.arn + handler = "exports.example" + runtime = "nodejs16.x" + + vpc_config { + ipv6_allowed_for_dual_stack = true + subnet_ids = [aws_subnet.subnet_for_lambda.id] + security_group_ids = [aws_security_group.sg_for_lambda.id] + } +} +`, rName)) +} + func testAccFunctionConfig_skipDestroy(rName string) string { return acctest.ConfigCompose(acctest.ConfigLambdaBase(rName, rName, rName), fmt.Sprintf(` resource "aws_lambda_function" "test" { diff --git a/website/docs/r/lambda_function.html.markdown b/website/docs/r/lambda_function.html.markdown index 0df5d4a33cc..c4cbcca76ad 100644 --- a/website/docs/r/lambda_function.html.markdown +++ b/website/docs/r/lambda_function.html.markdown @@ -331,8 +331,9 @@ Snap start settings for low-latency startups. This feature is currently only sup For network connectivity to AWS resources in a VPC, specify a list of security groups and subnets in the VPC. When you connect a function to a VPC, it can only access resources and the internet through that VPC. See [VPC Settings][7]. -~> **NOTE:** If both `subnet_ids` and `security_group_ids` are empty then `vpc_config` is considered to be empty or unset. +~> **NOTE:** If `subnet_ids`, `security_group_ids` and `ipv6_allowed_for_dual_stack` are empty then `vpc_config` is considered to be empty or unset. +* `ipv6_allowed_for_dual_stack` - (Optional) Allows outbound IPv6 traffic on VPC functions that are connected to dual-stack subnets. Default is `false`. * `security_group_ids` - (Required) List of security group IDs associated with the Lambda function. * `subnet_ids` - (Required) List of subnet IDs associated with the Lambda function.