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を返しても参照できない」