Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ipv6_allowed_for_dual_stack field for lambda function #34045

Merged
3 changes: 3 additions & 0 deletions .changelog/34045.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_lambda_function: Added `ipv6_allowed_for_dual_stack` field to support outbound ipv6 traffic on dual-stack subnets
DanielRieske marked this conversation as resolved.
Show resolved Hide resolved
```
122 changes: 122 additions & 0 deletions internal/acctest/acctest.go
Original file line number Diff line number Diff line change
Expand Up @@ -2229,6 +2229,128 @@ resource "aws_security_group" "sg_for_lambda" {
`, policyName, roleName, sgName))
}

func ConfigLambdaDualStackBase(policyName, roleName, sgName string, subnetCount int) string {
return ConfigCompose(ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
data "aws_partition" "current" {}

resource "aws_iam_role_policy" "iam_policy_for_lambda" {
name = "%s"
role = aws_iam_role.iam_for_lambda.id

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:${data.aws_partition.current.partition}:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"SNS:Publish"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments"
],
"Resource": [
"*"
]
}
]
}
EOF
}

resource "aws_iam_role" "iam_for_lambda" {
name = "%s"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_vpc" "vpc_for_lambda" {
cidr_block = "10.0.0.0/16"

assign_generated_ipv6_cidr_block = true

tags = {
Name = "terraform-testacc-lambda-function"
}
}

resource "aws_subnet" "subnet_for_lambda" {
count = %[4]d

vpc_id = aws_vpc.vpc_for_lambda.id
availability_zone = data.aws_availability_zones.available.names[count.index]
cidr_block = cidrsubnet(aws_vpc.vpc_for_lambda.cidr_block, 8, count.index)

ipv6_cidr_block = cidrsubnet(aws_vpc.vpc_for_lambda.ipv6_cidr_block, 8, count.index)
assign_ipv6_address_on_creation = true

tags = {
Name = "tf-acc-lambda-function-1"
}
}

resource "aws_security_group" "sg_for_lambda" {
name = "%[3]s"
description = "Allow all inbound traffic for lambda test"
vpc_id = aws_vpc.vpc_for_lambda.id

ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
`, policyName, roleName, sgName, subnetCount))
}

func ConfigVPCWithSubnets(rName string, subnetCount int) string {
return ConfigCompose(
ConfigAvailableAZsNoOptInDefaultExclude(),
Expand Down
1 change: 1 addition & 0 deletions internal/service/lambda/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func flattenVPCConfigResponse(s *types.VpcConfigResponse) []map[string]interface
return nil
}

settings["ipv6_allowed_for_dual_stack"] = aws.BoolValue(s.Ipv6AllowedForDualStack)
settings["subnet_ids"] = flex.FlattenStringValueSet(s.SubnetIds)
settings["security_group_ids"] = flex.FlattenStringValueSet(s.SecurityGroupIds)
if s.VpcId != nil {
Expand Down
30 changes: 20 additions & 10 deletions internal/service/lambda/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,11 @@ func ResourceFunction() *schema.Resource {
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ipv6_allowed_for_dual_stack": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"security_group_ids": {
Type: schema.TypeSet,
Required: true,
Expand All @@ -397,15 +402,16 @@ func ResourceFunction() *schema.Resource {
// Suppress diffs if the VPC configuration is provided, but empty
// which is a valid Lambda function configuration. e.g.
// vpc_config {
// security_group_ids = []
// subnet_ids = []
// ipv6_allowed_for_dual_stack = false
// security_group_ids = []
// subnet_ids = []
// }
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if d.Id() == "" || old == "1" || new == "0" {
return false
}

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") {
return false
}

Expand Down Expand Up @@ -533,8 +539,9 @@ func resourceFunctionCreate(ctx context.Context, d *schema.ResourceData, meta in
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)),
}
}

Expand Down Expand Up @@ -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{},
}
}
}
Expand Down Expand Up @@ -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") ||
Expand Down
76 changes: 76 additions & 0 deletions internal/service/lambda/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -3869,6 +3906,45 @@ resource "aws_lambda_function" "test" {
`, rName))
}

func testAccFunctionConfig_ipv6AllowedForDualStackDisabled(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLambdaDualStackBase(rName, rName, rName, 1),
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[0].id]
security_group_ids = [aws_security_group.sg_for_lambda.id]
}
}
`, rName))
}

func testAccFunctionConfig_ipv6AllowedForDualStackEnabled(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLambdaDualStackBase(rName, rName, rName, 1),
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[0].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" {
Expand Down
3 changes: 2 additions & 1 deletion website/docs/r/lambda_function.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Loading