aws Lambda在删除堆栈时创建了ENI而不删除

时间:2016-12-23 10:29:29

标签: amazon-web-services amazon-cloudformation

CloudFormation创建Lambda函数。执行该功能时,由lambda自动配置ENI。在执行函数后,ENI似乎仍然存在,以加速后续的函数执行。 CloudFormation删除lambda函数。 EN仍然落后。尝试删除VPC CloudFormation stack时,由于ENI使用security group and subnet,堆栈删除失败。

在我的lambda role delete permission中。

  

"效果":"允许","行动":[" ec2:CreateNetworkInterface"," ec2:DeleteNetworkInterface&# 34;," ec2:DescribeNetworkInterfaces" ],"资源":" *"

我正在使用自定义资源从CloudFormation模板运行lambda,因此lambda将被称为堆栈创建和删除。 ENI将用于创建堆栈和删除堆栈。现在如何处理eni删除?

1 个答案:

答案 0 :(得分:20)

在VPC中使用Lambda函数时存在一个已知问题,如Configuring a Lambda Function to Access Resources in an Amazon VPC中所述:

  

Lambda函数执行和ENI删除之间存在延迟。如果您在执行函数后立即删除了该角色,则您有责任删除ENI。

文档没有详细说明此“延迟”的确切时间,但forum post by Richard@AWS表明它可以持续 6小时(!)。 (在我使用AWS CloudTrail的观察中,Lambda执行和ENI删除之间的延迟大约是一小时。)

在AWS进一步解决此问题之前,您可以通过在删除Lambda函数和删除关联的安全组和子网之间分离和删除剩余的ENI来解决此问题。这就是Terraform目前handles在其框架中issue的方式。

您可以通过将VPC / Subnet / SG层和Lambda功能层分离为两个不同的CloudFormation堆栈来手动执行此操作,也可以通过实施自定义资源以使用AWS SDK删除ENI来自动执行此操作。

这是一个完整的工作示例,它创建了一个VPC-Lambda自定义资源,在使用VPCDestroyENI自定义资源删除时清理其ENI:

Launch Stack

Description: Creates a VPC-Lambda Custom Resource, cleaning up ENIs when deleted.
Parameters:
  VPCId:
    Description: VPC Id
    Type: AWS::EC2::VPC::Id
  SubnetId:
    Description: Private Subnet Id
    Type: AWS::EC2::Subnet::Id
Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Lambda VPC security group
      VpcId: !Ref VPCId
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: "/"
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
      Policies:
      - PolicyName: DetachNetworkInterface
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['ec2:DetachNetworkInterface']
            Resource: '*'
  AppendTest:
    Type: Custom::Split
    DependsOn: VPCDestroyENI
    Properties:
      ServiceToken: !GetAtt AppendItemToListFunction.Arn
      List: [1, 2, 3]
      AppendedItem: 4
  AppendItemToListFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          exports.handler = function(event, context) {
             var responseData = {Value: event.ResourceProperties.List};
             responseData.Value.push(event.ResourceProperties.AppendedItem);
             response.send(event, context, response.SUCCESS, responseData);
          };
      Timeout: 30
      Runtime: nodejs4.3
      VpcConfig:
        SecurityGroupIds: [!Ref SecurityGroup]
        SubnetIds: [!Ref SubnetId]
  VPCDestroyENIFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          var AWS = require('aws-sdk');
          exports.handler = function(event, context) {
            console.log("REQUEST RECEIVED:\n", JSON.stringify(event));
            if (event.RequestType != 'Delete') {
              response.send(event, context, response.SUCCESS, {});
              return;
            }
            var ec2 = new AWS.EC2();
            var params = {
              Filters: [
                {
                  Name: 'group-id',
                  Values: event.ResourceProperties.SecurityGroups
                },
                {
                  Name: 'description',
                  Values: ['AWS Lambda VPC ENI: *']
                }
              ]
            };
            console.log("Deleting attachments!");
            // Detach all network-interface attachments
            ec2.describeNetworkInterfaces(params).promise().then(function(data) {
              console.log("Got Interfaces:\n", JSON.stringify(data));
              return Promise.all(data.NetworkInterfaces.map(function(networkInterface) {
                var networkInterfaceId = networkInterface.NetworkInterfaceId;
                var attachmentId = networkInterface.Attachment.AttachmentId;
                return ec2.detachNetworkInterface({AttachmentId: attachmentId}).promise().then(function(data) {
                  return ec2.waitFor('networkInterfaceAvailable', {NetworkInterfaceIds: [networkInterfaceId]}).promise();
                }).then(function(data) {
                  console.log("Detached Interface, deleting:\n", networkInterfaceId);
                  return ec2.deleteNetworkInterface({NetworkInterfaceId: networkInterfaceId}).promise();
                });
              }));
            }).then(function(data) {
              console.log("Success!");
              response.send(event, context, response.SUCCESS, {});
            }).catch(function(err) {
              console.log("Failure:\n", JSON.stringify(err));
              response.send(event, context, response.FAILED, {});
            });
          };
      Timeout: 300
      Runtime: nodejs4.3
  VPCDestroyENI:
    Type: Custom::VPCDestroyENI
    Properties:
      ServiceToken: !GetAtt VPCDestroyENIFunction.Arn
      SecurityGroups: [!Ref SecurityGroup]
Outputs:
  Output:
    Description: output
    Value: !Join [",", !GetAtt AppendTest.Value]

注意:要创建上例中所需的VPC和私有子网,您可以使用AWS Quick Start Amazon VPC Architecture template