在cloudformation中指定删除堆栈顺序

时间:2017-05-05 15:17:06

标签: amazon-web-services amazon-cloudformation

我正在使用cloudformation创建AWS堆栈。我正在创建EBS卷作为Cloud Formation的一部分,并将它们附加到引导程序中的EC2。

创作部分工作正常。但是,在删除堆栈时,Cloudformation首先尝试删除EBS卷。由于卷已经附加,因此无法删除和删除剩余资源。

因此,除非手动删除或再次使用删除堆栈,否则EBS卷将保留在那里。

有没有办法可以在删除(仅限)cloudformation模板中的资源时指定订单。?

2 个答案:

答案 0 :(得分:1)

控制CloudFormation操作顺序的唯一机制是使用DependsOn。但这并不能解决这个特殊问题。

您在此处描述的问题是因为CloudFormation 不知道该卷已附加:您通过单独的机制附加它(正如您所描述的,使用EC2引导程序,我认为这是某种东西例如,就像EC2实例用户数据脚本上的aws命令一样。

相反,你可以做的是 CloudFormation为你附加音量。这样,CloudFormation 知道已经附加了卷,并且它知道它也必须分离卷。

为此,您需要使用AWS::EC2::VolumeAttachment类型的资源。一个YAML片段就像是:

Resources:
  MyInstance:
    Type: AWS::EC2::Instance
    Properties:
      ...
  MyVolume:
    Type: AWS::EC2::Volume
    Properties:
      ...
  MyVolumeAttachment:
    Type: AWS::EC2::VolumeAttachment
    Properties:
      Device: /dev/sdf
      InstanceId: !Ref MyInstance
      VolumeId: !Ref MyVolume

CFN上有相当多的*Attachment资源类型,出于这个目的:您让CFN将资源附加到您,您为参与附件的资源指定Ref,所以CFN知道“订单”(即,首先创建2个资源,然后附加它们,或者在删除时,它首先分离,然后删除两个资源),然后CFN可以为您处理整个过程。

答案 1 :(得分:0)

我通过使用自定义lambda函数终止实例解决了此问题。该函数取决于VolumeAttachment资源,并在CloudFormation尝试分离卷之前被调用。它在创建堆栈时不执行任何操作,但是在删除堆栈时终止实例:

Resources:
  ...
  TerminateInstance:
    Type: Custom::InstanceTermination
    DependsOn: VolumeAttachment1
    Properties:
      ServiceToken: !GetAtt TerminateInstanceFunction.Arn
      InstanceId: !Ref EC2Instance
  TerminateInstanceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt TerminateInstanceLambdaExecutionRole.Arn
      Runtime: nodejs4.3
      Timeout: 30
      Code:
        ZipFile: !Sub |
          var aws = require("aws-sdk");
          var response = require('cfn-response');

          exports.handler = function(event, context) {
              console.log("request received:\n" + JSON.stringify(event));

              var physicalId = event.PhysicalResourceId;

              function success(data) {
                  data = data || {}
                  console.log('SUCCESS:\n', data);
                  return response.send(event, context, response.SUCCESS, data, physicalId);
              }

              function failed(err) {
                  console.log('FAILED:\n', err);
                  return response.send(event, context, response.FAILED, err, physicalId);
              }

              // ignore non-delete requests
              if (event.RequestType !== 'Delete') {
                  console.log('Non-delete request is ignored');
                  return success();
              }

              var instanceId = event.ResourceProperties.InstanceId;
              if (!instanceId) {
                  return failed('InstanceId required');
              }

              var ec2 = new aws.EC2({region: event.ResourceProperties.Region});

              ec2.terminateInstances({InstanceIds: [instanceId]})
              .promise()
              .then((data) => {
                  console.log('"terminateInstances" Response:\n', JSON.stringify(data));
                  success();
              })
              .catch((err) => failed(err));
          };
  TerminateInstanceLambdaExecutionRole:
    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/AWSLambdaBasicExecutionRole
      - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
      Policies:
      - PolicyName: EC2Policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
              - 'ec2:TerminateInstances'
              Resource: ['*']