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
ではネストされた値を参照しないパターンです。
これでスタック作成してもエラーになりません。
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
の定義を変更して確認します。
テンプレート(抜粋)
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
としているのがダメなのかなと考えて参照方法を変更してみました。
テンプレート(抜粋)
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