share facebook facebook twitter menu hatena pocket slack

2019.07.30 TUE

AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない

甲斐 甲

WRITTEN BY 甲斐 甲

AWS CloudFormationn(CFn)のLambda-Backedカスタムリソースを利用していてレスポンスとしてネストされたJSONを返そうとしたらハマったので調べてみました。

ハマった内容

Lambda-Backedカスタムリソースで

import cfnresponse
def handler(event, context):
  data = {
    "hoge": "hoge",
    "foo": {
      "hoge": "foohoge"
    }
  }
  cfnresponse.send(event, context, cfnresponse.SUCCESS, data)

のようにCFnへレスポンスを返してCFnテンプレートで

Outputs:
  hoge:
    Value: !GetAtt CustomResource.hoge
  foo:
    Value: !GetAtt CustomResource.foo.hoge

と、カスタムリソースの値を参照すると!GetAtt CustomResource.foo.hogeでエラーになりました。
エラー内容はCustomResource attribute error: Vendor response doesn't contain foo.hoge key

解決策

Lambda-BackedカスタムリソースでJSONを返す場合はネストせずフラットなJSONを返しましょう。

以下検証内容となります。

検証内容

前提

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

エラーにならないケース

テンプレート

Lambda関数ではネストされたJSONを返しますが、!GetAtt ではネストされた値を参照しないパターンです。
これでスタック作成してもエラーになりません。

cfn-template.yaml

Resources:
  CustomResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt CustomResourceFunction.Arn

  CustomResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          def handler(event, context):
            data = {
              "hoge": "hoge",
              "foo": {
                "hoge": "foohoge"
              }
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, data)
      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:*:*:*"

Outputs:
  hoge:
    Value: !GetAtt CustomResource.hoge

スタック作成して確認する

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

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a"
}


> aws cloudformation describe-stacks \
  --stack-name cfn-response-test

{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "StackName": "cfn-response-test",
            "CreationTime": "2019-06-26T01:47:20.177Z",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "hoge",
                    "OutputValue": "hoge"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

正常に作成できました。Lambda関数のログを確認しておきます。

> aws cloudformation list-stack-resources \
  --stack-name cfn-response-test

{
    "StackResourceSummaries": [
        {
            "LogicalResourceId": "CustomResource",
            "PhysicalResourceId": "2019/06/26/[$LATEST]f518c197bb8541f0b651a151bb201560",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-26T01:47:45.532Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "CustomResourceFunction",
            "PhysicalResourceId": "cfn-response-test-CustomResourceFunction-HI0ITG58NF7G",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-26T01:47:39.855Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        (略)
    ]
}


> aws logs get-log-events \
  --log-group-name /aws/lambda/cfn-response-test-CustomResourceFunction-HI0ITG58NF7G \
  --log-stream-name '2019/06/26/[$LATEST]f518c197bb8541f0b651a151bb201560' \
  --output=text \
  --query "events[*].message"

START RequestId: db73aff3-d14a-4ff1-92cc-6335d23279aa 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/26/[$LATEST]f518c197bb8541f0b651a151bb201560", "PhysicalResourceId": "2019/06/26/[$LATEST]f518c197bb8541f0b651a151bb201560", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a", "RequestId": "c734ee9f-7963-4626-bf6e-40d7e24a99ab", "LogicalResourceId": "CustomResource", "NoEcho": false, "Data": {"hoge": "hoge", "foo": {"hoge": "foohoge"}}}
        Status code: OK
        END RequestId: db73aff3-d14a-4ff1-92cc-6335d23279aa
        REPORT RequestId: db73aff3-d14a-4ff1-92cc-6335d23279aa  Duration: 310.53 ms     Billed Duration: 400 ms         Memory Size: 128 MB   Max Memory Used: 24 MB

レスポンス内容のDataでネストされたJSONを返していることが確認できます。

エラーにならないケース

Outputsの定義を変更して確認します。

テンプレート(抜粋)

cfn-template.yaml_抜粋

Outputs:
  hoge:
    Value: !GetAtt CustomResource.hoge
  foo:
    Value: !GetAtt CustomResource.foo.hoge

スタック更新して確認する

> aws cloudformation update-stack \
  --stack-name cfn-response-test \
  --template-body file://cfn-template.yaml \
  --capabilities CAPABILITY_IAM

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a"
}


> aws cloudformation describe-stacks \
  --stack-name cfn-response-test

{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "StackName": "cfn-response-test",
            "CreationTime": "2019-06-26T01:47:20.177Z",
            "LastUpdatedTime": "2019-06-26T01:50:44.408Z",
            "RollbackConfiguration": {},
            "StackStatus": "UPDATE_ROLLBACK_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "hoge",
                    "OutputValue": "hoge"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

UPDATE_ROLLBACK_COMPLETEとなり更新に失敗しました。
イベントを取得してエラー内容を確認します。

> aws cloudformation describe-stack-events \
  --stack-name cfn-response-test

{
    "StackEvents": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "EventId": "e01c4f90-97b4-11e9-bb14-12f81177eb92",
            "StackName": "cfn-response-test",
            "LogicalResourceId": "cfn-response-test",
            "PhysicalResourceId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-06-26T01:51:12.126Z",
            "ResourceStatus": "UPDATE_ROLLBACK_COMPLETE"
        },
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "EventId": "df9dd0c0-97b4-11e9-9cbb-0eaa79b17470",
            "StackName": "cfn-response-test",
            "LogicalResourceId": "cfn-response-test",
            "PhysicalResourceId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-06-26T01:51:11.295Z",
            "ResourceStatus": "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS"
        },
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "EventId": "d1776100-97b4-11e9-a0b3-0a20b68b404c",
            "StackName": "cfn-response-test",
            "LogicalResourceId": "cfn-response-test",
            "PhysicalResourceId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-06-26T01:50:47.544Z",
            "ResourceStatus": "UPDATE_ROLLBACK_IN_PROGRESS",
            "ResourceStatusReason": "CustomResource attribute error: Vendor response doesn't contain foo.hoge key in object arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a|CustomResource|c734ee9f-7963-4626-bf6e-40d7e24a99ab in S3 bucket cloudformation-custom-resource-storage-useast1"
        },
        (略)
    ]
}

CustomResource attribute error: Vendor response doesn't contain foo.hoge keyとエラー内容が確認できます。

参照方法を変えてみる

!GetAtt CustomResource.foo.hoge としているのがダメなのかなと考えて参照方法を変更してみました。

テンプレート(抜粋)

cfn-template.yaml_抜粋

Outputs:
  hoge:
    Value: !GetAtt CustomResource.hoge
  foo:
    Value: !GetAtt CustomResource.foo['hoge']

スタック更新して確認する

> aws cloudformation update-stack \
  --stack-name cfn-response-test \
  --template-body file://cfn-template.yaml \
  --capabilities CAPABILITY_IAM

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a"
}


> aws cloudformation describe-stacks \
  --stack-name cfn-response-test

{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "StackName": "cfn-response-test",
            "CreationTime": "2019-06-26T01:47:20.177Z",
            "LastUpdatedTime": "2019-06-26T01:54:45.226Z",
            "RollbackConfiguration": {},
            "StackStatus": "UPDATE_ROLLBACK_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "hoge",
                    "OutputValue": "hoge"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}


> aws cloudformation describe-stack-events \
  --stack-name cfn-response-test

{
    "StackEvents": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "EventId": "6f1d0b80-97b5-11e9-b714-0a2e8057061e",
            "StackName": "cfn-response-test",
            "LogicalResourceId": "cfn-response-test",
            "PhysicalResourceId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-06-26T01:55:12.038Z",
            "ResourceStatus": "UPDATE_ROLLBACK_COMPLETE"
        },
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "EventId": "6eb6f6b0-97b5-11e9-aaf5-0a437bda7b86",
            "StackName": "cfn-response-test",
            "LogicalResourceId": "cfn-response-test",
            "PhysicalResourceId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-06-26T01:55:11.375Z",
            "ResourceStatus": "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS"
        },
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "EventId": "609a23e0-97b5-11e9-a31e-0e366af4fd90",
            "StackName": "cfn-response-test",
            "LogicalResourceId": "cfn-response-test",
            "PhysicalResourceId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2019-06-26T01:54:47.692Z",
            "ResourceStatus": "UPDATE_ROLLBACK_IN_PROGRESS",
            "ResourceStatusReason": "CustomResource attribute error: Vendor response doesn't contain foo['hoge'] key in object arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/55d8cd90-97b4-11e9-aa0c-128a65397f5a|CustomResource|c734ee9f-7963-4626-bf6e-40d7e24a99ab in S3 bucket cloudformation-custom-resource-storage-useast1"
        },
        (略)
    ]
}

CustomResource attribute error: Vendor response doesn't contain foo['hoge'] keyとエラーになりました。

まとめ

Lambda-BackedカスタムリソースでネストされたJSONを返しても!GetAttでは参照できないことがわかりました。ドキュメントにはDataの型がType: JSON objectと定義されているのですが制限があるみたいです。。。

カスタムリソースの応答オブジェクト – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html

参考

カスタムリソースの応答オブジェクト – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html

元記事はこちら

AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない

甲斐 甲

甲斐 甲

2018/7にJOIN。 最近の好みはサーバレスです。なんでもとりあえず試します。

cloudpack

cloudpackは、Amazon EC2やAmazon S3をはじめとするAWSの各種プロダクトを利用する際の、導入・設計から運用保守を含んだフルマネージドのサービスを提供し、バックアップや24時間365日の監視/障害対応、技術的な問い合わせに対するサポートなどを行っております。
AWS上のインフラ構築およびAWSを活用したシステム開発など、案件のご相談はcloudpack.jpよりご連絡ください。