diff --git a/builtin/providers/aws/resource_aws_security_group.go b/builtin/providers/aws/resource_aws_security_group.go index 236abbd16..58bfc79ce 100644 --- a/builtin/providers/aws/resource_aws_security_group.go +++ b/builtin/providers/aws/resource_aws_security_group.go @@ -359,6 +359,8 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Security Group destroy: %v", d.Id()) + deleteLingeringLambdaENIs(conn, d) + return resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ GroupId: aws.String(d.Id()), @@ -975,3 +977,84 @@ func protocolForValue(v string) string { log.Printf("[WARN] Unable to determine valid protocol: no matching protocols found") return protocol } + +func deleteLingeringLambdaENIs(conn *ec2.EC2, d *schema.ResourceData) error { + filter1 := &ec2.Filter{ + Name: aws.String("group-id"), + Values: []*string{aws.String(d.Id())}, + } + filter2 := &ec2.Filter{ + Name: aws.String("description"), + Values: []*string{aws.String("AWS Lambda VPC ENI: *")}, + } + filter3 := &ec2.Filter{ + Name: aws.String("requester-id"), + Values: []*string{aws.String("*:awslambda_*")}, + } + params := &ec2.DescribeNetworkInterfacesInput{ + Filters: []*ec2.Filter{filter1, filter2, filter3}, + } + networkInterfaceResp, err := conn.DescribeNetworkInterfaces(params) + + if err != nil { + return err + } + + v := networkInterfaceResp.NetworkInterfaces + + for _, eni := range v { + if eni.Attachment != nil { + detachNetworkInterfaceParams := &ec2.DetachNetworkInterfaceInput{ + AttachmentId: eni.Attachment.AttachmentId, + } + _, detachNetworkInterfaceErr := conn.DetachNetworkInterface(detachNetworkInterfaceParams) + + if detachNetworkInterfaceErr != nil { + return detachNetworkInterfaceErr + } + + log.Printf("[DEBUG] Waiting for ENI (%s) to become dettached", *eni.NetworkInterfaceId) + stateConf := &resource.StateChangeConf{ + Pending: []string{"true"}, + Target: []string{"false"}, + Refresh: networkInterfaceAttachedRefreshFunc(conn, *eni.NetworkInterfaceId), + Timeout: 10 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "Error waiting for ENI (%s) to become dettached: %s", *eni.NetworkInterfaceId, err) + } + } + + deleteNetworkInterfaceParams := &ec2.DeleteNetworkInterfaceInput{ + NetworkInterfaceId: eni.NetworkInterfaceId, + } + _, deleteNetworkInterfaceErr := conn.DeleteNetworkInterface(deleteNetworkInterfaceParams) + + if deleteNetworkInterfaceErr != nil { + return deleteNetworkInterfaceErr + } + } + + return nil +} + +func networkInterfaceAttachedRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + + describe_network_interfaces_request := &ec2.DescribeNetworkInterfacesInput{ + NetworkInterfaceIds: []*string{aws.String(id)}, + } + describeResp, err := conn.DescribeNetworkInterfaces(describe_network_interfaces_request) + + if err != nil { + log.Printf("[ERROR] Could not find network interface %s. %s", id, err) + return nil, "", err + } + + eni := describeResp.NetworkInterfaces[0] + hasAttachment := strconv.FormatBool(eni.Attachment != nil) + log.Printf("[DEBUG] ENI %s has attachment state %s", id, hasAttachment) + return eni, hasAttachment, nil + } +}