2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法

Last updated at Posted at 2019-07-08

cloudpack あら便利カレンダー 2019 の記事となります。誕生秘話 はこちら。

AWS CloudFormationのLambda-backedカスタムリソースを利用するとAWS CloudFormationで管理できないリソースも管理することができますが、Lambda-backedで作成したリソースの更新・削除するのにリソースのIDをどうやって取り回そうか悩みました。

下記は解決策の1案となりますが、他に良い方法があれば教えてほしいです!

前提

  • AWSアカウントがある
  • AWS CLIが利用できる
  • AWS Lambda、CloudFormationの作成権限がある

CloudFormationのテンプレート作成

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> touch cfn-template.yaml

Lambda-backedカスタムリソースを利用して何かしらのリソースを作成・更新・削除するテンプレートとなります。
ポイントとしてはCreateResource のひとつで完結できたら良かったのですが、CreateResource で作成したリソースのIDを自前で取り回せなかったので、更新と削除を別リソースUpdateResource で行うようにしました。うーん、めんどうです

cfn-template.yaml
Resources:
  CreateResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt CreateResourceFunction.Arn

  UpdateResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt UpdateResourceFunction.Arn
      ResourceId: !GetAtt CreateResource.Id

  CreateResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          def handler(event, context):
            if event['RequestType'] == 'Create':
              # なんかリソース作成
              response = {'Id': 'hoge'}
              print('create resources ' + response['Id'])
              cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
              return

            # 他のRequestTypeは無視
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
      Runtime: python3.7

  UpdateResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          def handler(event, context):
            Id = event['ResourceProperties']['ResourceId']
            if event['RequestType'] == 'Update':
              # なんかリソース更新
              print('update resources ' + Id)
              cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              return

            if event['RequestType'] == 'Delete':
              # なんかリソース削除
              print('delete resources ' + Id)
              cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
              return

            # 他のRequestTypeは無視
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
      Runtime: python3.7

  FunctionExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      Policies:
      - PolicyName: root
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: "arn:aws:logs:*:*:*"

動作確認

スタック作成

> aws cloudformation create-stack \
  --stack-name cfn-lambda-backed-test \
  --template-body file://cfn-template.yaml \
  --capabilities CAPABILITY_IAM

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268"
}

スタック作成して各リソースが作成できたらLambda関数のログを確認します。
各リソースと関数のPhysicalResourceId をパラメータにaws logs get-log-events コマンドで取得します。

> aws cloudformation list-stack-resources \
  --stack-name cfn-lambda-backed-test

{
    "StackResourceSummaries": [
        {
            "LogicalResourceId": "CreateResource",
            "PhysicalResourceId": "2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-25T01:33:55.358Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "CreateResourceFunction",
            "PhysicalResourceId": "cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-25T01:33:49.819Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "FunctionExecutionRole",
            "PhysicalResourceId": "cfn-lambda-backed-test-FunctionExecutionRole-17PMYTJ3NS41Z",
            "ResourceType": "AWS::IAM::Role",
            "LastUpdatedTimestamp": "2019-06-25T01:33:46.370Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "UpdateResource",
            "PhysicalResourceId": "2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-25T01:34:01.068Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "UpdateResourceFunction",
            "PhysicalResourceId": "cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-25T01:33:49.516Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B \
  --log-stream-name '2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592' \
  --output=text \
  --query "events[*].message"

START RequestId: e8ad68ad-86be-4ea1-ad3f-736617882ce7 Version: $LATEST
        create resources hoge
        https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/(略)
        Response body:
        {"Status": "SUCCESS", "Reason": "See the details in CloudWatch Log Stream: 2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592", "PhysicalResourceId": "2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268", "RequestId": "1cb162f1-979f-4e6f-b277-dd7e729d23cc", "LogicalResourceId": "CreateResource", "NoEcho": false, "Data": {"Id": "hoge"}}
        Status code: OK
        END RequestId: e8ad68ad-86be-4ea1-ad3f-736617882ce7
        REPORT RequestId: e8ad68ad-86be-4ea1-ad3f-736617882ce7  Duration: 292.74 ms     Billed Duration: 300 ms         Memory Size: 128 MB   Max Memory Used: 29 MB


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0 \
  --log-stream-name '2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268' \
  --output=text \
  --query "events[*].message"

START RequestId: d593f33a-9231-4284-8157-97b5b799f70e Version: $LATEST
        https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/(略)
        Response body:
        {"Status": "SUCCESS", "Reason": "See the details in CloudWatch Log Stream: 2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268", "PhysicalResourceId": "2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268", "RequestId": "fe1b2ba8-0aff-44a6-ac1f-f46863594b0f", "LogicalResourceId": "UpdateResource", "NoEcho": false, "Data": {}}
        Status code: OK
        END RequestId: d593f33a-9231-4284-8157-97b5b799f70e
        REPORT RequestId: d593f33a-9231-4284-8157-97b5b799f70e  Duration: 242.26 ms     Billed Duration: 300 ms         Memory Size: 128 MB   Max Memory Used: 29 MB

スタック作成時にはCreateResource でリソースの作成、UpdateResource は呼び出しのみとなることが確認できました。

スタック削除

スタックを削除して動作を確認します。

> aws cloudformation delete-stack \
  --stack-name cfn-lambda-backed-test

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268"
}

スタック削除すると当然のことながらリソースが取得できなくなるので、ログストリーム名を取得してからログを確認します。

> aws logs describe-log-streams \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B \
  --output=text \
  --query "logStreams[*].logStreamName"

2019/06/25/[$LATEST]587b7bbfa5b74a38a54846ff44a9a592    2019/06/25/[$LATEST]8d6fac4288674ecbb7b6f7a577b8b932


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-CreateResourceFunction-LBG1WB6FV88B \
  --log-stream-name '2019/06/25/[$LATEST]8d6fac4288674ecbb7b6f7a577b8b932' \
  --output=text \
  --query "events[*].message"

START RequestId: 8a0c34a7-8c3a-47b5-9128-3a49748aca52 Version: $LATEST



> aws logs describe-log-streams \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0 \
  --output=text \
  --query "logStreams[*].logStreamName"

2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511    2019/06/25/[$LATEST]59ec7f3b07ec48f5b5feab288384b268


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-lambda-backed-test-UpdateResourceFunction-BMXD99ZLKXP0 \
  --log-stream-name '2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511' \
  --output=text \
  --query "events[*].message"

START RequestId: fcde5258-b444-4e33-aef6-d2dcff63e2c2 Version: $LATEST
        delete resources hoge
        https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/(略)
        Response body:
        {"Status": "SUCCESS", "Reason": "See the details in CloudWatch Log Stream: 2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511", "PhysicalResourceId": "2019/06/25/[$LATEST]0c2223e7635d4ee0b04c508e0fc76511", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/3bed13d0-96e9-11e9-90fb-122d883fe268", "RequestId": "5d390a57-55b6-4ddd-8820-0104c94ab705", "LogicalResourceId": "UpdateResource", "NoEcho": false, "Data": {}}
        Status code: OK
        END RequestId: fcde5258-b444-4e33-aef6-d2dcff63e2c2
        REPORT RequestId: fcde5258-b444-4e33-aef6-d2dcff63e2c2  Duration: 644.07 ms     Billed Duration: 700 ms         Memory Size: 128 MB   Max Memory Used: 56 MB

スタック削除時にはCreateResource は呼び出しのみ、UpdateResource でリソースの削除がされることが確認できました。

まとめ

若干定義が面倒になりますがAWS CloudFormationのLambda-backedカスタムリソースを利用してリソースを更新・削除できることが確認できました。

参考

Blue21: lambdaのログを aws-cli で見る
https://blue21neo.blogspot.com/2018/02/lambda-aws-cli.html

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?