diff --git a/.changelog/37624.txt b/.changelog/37624.txt new file mode 100644 index 00000000000..ab85b6bb0f2 --- /dev/null +++ b/.changelog/37624.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_lambda_function: Remove `replace_security_group_on_destroy` and `replacement_security_group_ids` deprecations, re-implement with alternate workflow +``` diff --git a/internal/service/ec2/findv2.go b/internal/service/ec2/findv2.go index f74e7251124..546e618e37f 100644 --- a/internal/service/ec2/findv2.go +++ b/internal/service/ec2/findv2.go @@ -298,6 +298,19 @@ func findSecurityGroupsV2(ctx context.Context, conn *ec2.Client, input *ec2.Desc return output, nil } +// FindSecurityGroupByNameAndVPCIDV2 looks up a security group by name, VPC ID. Returns a retry.NotFoundError if not found. +func FindSecurityGroupByNameAndVPCIDV2(ctx context.Context, conn *ec2.Client, name, vpcID string) (*awstypes.SecurityGroup, error) { + input := &ec2.DescribeSecurityGroupsInput{ + Filters: newAttributeFilterListV2( + map[string]string{ + "group-name": name, + "vpc-id": vpcID, + }, + ), + } + return findSecurityGroupV2(ctx, conn, input) +} + func findIPAMPoolAllocationsV2(ctx context.Context, conn *ec2.Client, input *ec2.GetIpamPoolAllocationsInput) ([]awstypes.IpamPoolAllocation, error) { var output []awstypes.IpamPoolAllocation diff --git a/internal/service/lambda/function.go b/internal/service/lambda/function.go index effb812ad32..dc1608d1788 100644 --- a/internal/service/lambda/function.go +++ b/internal/service/lambda/function.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/sdkv2" + tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -292,14 +293,10 @@ func resourceFunction() *schema.Resource { Computed: true, }, "replace_security_groups_on_destroy": { - Deprecated: "AWS no longer supports this operation. This attribute now has " + - "no effect and will be removed in a future major version.", Type: schema.TypeBool, Optional: true, }, "replacement_security_group_ids": { - Deprecated: "AWS no longer supports this operation. This attribute now has " + - "no effect and will be removed in a future major version.", Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, @@ -1045,6 +1042,12 @@ func resourceFunctionDelete(ctx context.Context, d *schema.ResourceData, meta in return diags } + if _, ok := d.GetOk("replace_security_groups_on_destroy"); ok { + if err := replaceSecurityGroupsOnDestroy(ctx, d, meta); err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + } + log.Printf("[INFO] Deleting Lambda Function: %s", d.Id()) _, err := tfresource.RetryWhenIsAErrorMessageContains[*awstypes.InvalidParameterValueException](ctx, d.Timeout(schema.TimeoutDelete), func() (interface{}, error) { return conn.DeleteFunction(ctx, &lambda.DeleteFunctionInput{ @@ -1120,6 +1123,67 @@ func findLatestFunctionVersionByName(ctx context.Context, conn *lambda.Client, n return output, nil } +// replaceSecurityGroupsOnDestroy sets the VPC configuration security groups +// prior to resource destruction +// +// This function is called when the replace_security_groups_on_destroy +// argument is set. If the replacement_security_group_ids attribute is set, +// those values will be used as replacements. Otherwise, the default +// security group is used. +// +// Configuring this option can decrease destroy times for the security +// groups included in the VPC configuration block during normal operation +// by freeing them from association with ENI's left behind after destruction +// of the function. +func replaceSecurityGroupsOnDestroy(ctx context.Context, d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).LambdaClient(ctx) + ec2Conn := meta.(*conns.AWSClient).EC2Client(ctx) + + var sgIDs []string + var vpcID string + if v, ok := d.GetOk(names.AttrVPCConfig); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + tfMap := v.([]interface{})[0].(map[string]interface{}) + sgIDs = flex.ExpandStringValueSet(tfMap[names.AttrSecurityGroupIDs].(*schema.Set)) + vpcID = tfMap[names.AttrVPCID].(string) + } else { // empty VPC config, nothing to do + return nil + } + + if len(sgIDs) == 0 { // no security groups, nothing to do + return nil + } + + var replacementSGIDs []string + if v, ok := d.GetOk("replacement_security_group_ids"); ok { + replacementSGIDs = flex.ExpandStringValueSet(v.(*schema.Set)) + } else { + defaultSG, err := tfec2.FindSecurityGroupByNameAndVPCIDV2(ctx, ec2Conn, "default", vpcID) + if err != nil || defaultSG == nil { + return fmt.Errorf("finding VPC (%s) default security group: %s", vpcID, err) + } + replacementSGIDs = []string{aws.ToString(defaultSG.GroupId)} + } + + input := &lambda.UpdateFunctionConfigurationInput{ + FunctionName: aws.String(d.Id()), + VpcConfig: &awstypes.VpcConfig{ + SecurityGroupIds: replacementSGIDs, + }, + } + + if _, err := retryFunctionOp(ctx, func() (*lambda.UpdateFunctionConfigurationOutput, error) { + return conn.UpdateFunctionConfiguration(ctx, input) + }); err != nil { + return fmt.Errorf("updating Lambda Function (%s) configuration: %s", d.Id(), err) + } + + if _, err := waitFunctionUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("waiting for Lambda Function (%s) configuration update: %s", d.Id(), err) + } + + return nil +} + func statusFunctionLastUpdateStatus(ctx context.Context, conn *lambda.Client, name string) retry.StateRefreshFunc { return func() (interface{}, string, error) { output, err := findFunctionByName(ctx, conn, name) diff --git a/website/docs/r/lambda_function.html.markdown b/website/docs/r/lambda_function.html.markdown index 29ab71f69f0..99b8bc0286e 100644 --- a/website/docs/r/lambda_function.html.markdown +++ b/website/docs/r/lambda_function.html.markdown @@ -280,8 +280,12 @@ The following arguments are optional: * `package_type` - (Optional) Lambda deployment package type. Valid values are `Zip` and `Image`. Defaults to `Zip`. * `publish` - (Optional) Whether to publish creation/change as new Lambda Function Version. Defaults to `false`. * `reserved_concurrent_executions` - (Optional) Amount of reserved concurrent executions for this lambda function. A value of `0` disables lambda from being triggered and `-1` removes any concurrency limitations. Defaults to Unreserved Concurrency Limits `-1`. See [Managing Concurrency][9] -* `replace_security_groups_on_destroy` - (Optional, **Deprecated**) **AWS no longer supports this operation. This attribute now has no effect and will be removed in a future major version.** Whether to replace the security groups on associated lambda network interfaces upon destruction. Removing these security groups from orphaned network interfaces can speed up security group deletion times by avoiding a dependency on AWS's internal cleanup operations. By default, the ENI security groups will be replaced with the `default` security group in the function's VPC. Set the `replacement_security_group_ids` attribute to use a custom list of security groups for replacement. -* `replacement_security_group_ids` - (Optional, **Deprecated**) List of security group IDs to assign to orphaned Lambda function network interfaces upon destruction. `replace_security_groups_on_destroy` must be set to `true` to use this attribute. +* `replace_security_groups_on_destroy` - (Optional) Whether to replace the security groups on the function's VPC configuration prior to destruction. +Removing these security group associations prior to function destruction can speed up security group deletion times of AWS's internal cleanup operations. +By default, the security groups will be replaced with the `default` security group in the function's configured VPC. +Set the `replacement_security_group_ids` attribute to use a custom list of security groups for replacement. +* `replacement_security_group_ids` - (Optional) List of security group IDs to assign to the function's VPC configuration prior to destruction. +`replace_security_groups_on_destroy` must be set to `true` to use this attribute. * `runtime` - (Optional) Identifier of the function's runtime. See [Runtimes][6] for valid values. * `s3_bucket` - (Optional) S3 bucket location containing the function's deployment package. This bucket must reside in the same AWS region where you are creating the Lambda function. Exactly one of `filename`, `image_uri`, or `s3_bucket` must be specified. When `s3_bucket` is set, `s3_key` is required. * `s3_key` - (Optional) S3 key of an object containing the function's deployment package. When `s3_bucket` is set, `s3_key` is required.