前回、AWS CloudFormation(CFn)のLambda-BackedカスタムリソースでネストされたJSONを返しても!GetAttで参照できないことがわかったのですが、よくよく調べてみるとネストで返せそうだったのでさらに調べてみました。

AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない – Qiita
https://cloudpack.media/48318

結論

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

ってするとそれっぽくなります。(白目

調べたこと

CFnのGetAtt関数ドキュメントにあるテンプレート例でSourceSecurityGroupOwnerId: !GetAtt myELB.SourceSecurityGroup.GroupNamemyELBリソースに対してネストされてる(っぽい)値SourceSecurityGroup.GroupName を参照しているのをみつけました。

Fn::GetAtt – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html

LoadBalancerのドキュメントをみると戻り値として確かにSourceSecurityGroup.GroupNameは定義されています。

AWS::ElasticLoadBalancing::LoadBalancer – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb.html#aws-properties-ec2-elb-ref

通常のリソースの場合はネストされた情報が取り扱えるのでしょうか?
GetAttドキュメントの属性を確認すると、コンマ区切りで定義されている項目が確認できます。

Fn::GetAtt – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html#intrinsic-function-reference-getatt-attrib

Lambda-Backedカスタムリソースでネストされてるっぽくしてみる

Lambda-Backedカスタムリソースでも同じように参照できるレスポンスデータの返し方を考えてみました。

テンプレート定義

Lambda関数でフラットなJSONを返しますが、名前をfoo.hogeとします(安直)。

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": "hoge"
            }
            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
  foo:
    Value: !GetAtt CustomResource.foo.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/cea96c90-97b7-11e9-b384-0e75601403f8"
}


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

{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/cea96c90-97b7-11e9-b384-0e75601403f8",
            "StackName": "cfn-response-test",
            "CreationTime": "2019-06-26T02:12:11.362Z",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "hoge",
                    "OutputValue": "hoge"
                },
                {
                    "OutputKey": "foo",
                    "OutputValue": "hoge"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

無事に!GetAtt CustomResource.foo.hogeで値が参照できました。
一応関数のログを確認しておきます。

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

{
    "StackResourceSummaries": [
        {
            "LogicalResourceId": "CustomResource",
            "PhysicalResourceId": "2019/06/26/[$LATEST]0f6645081f974de7a40c44ac5a6b1493",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-26T02:12:38.029Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "CustomResourceFunction",
            "PhysicalResourceId": "cfn-response-test-CustomResourceFunction-RUGERLT0O78L",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-26T02:12:33.223Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        (略)
    ]
}


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

START RequestId: 9fb7a4fa-44b4-49ee-a5b7-bcbc57d40961 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]0f6645081f974de7a40c44ac5a6b1493", "PhysicalResourceId": "2019/06/26/[$LATEST]0f6645081f974de7a40c44ac5a6b1493", "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-response-test/cea96c90-97b7-11e9-b384-0e75601403f8", "RequestId": "bc048c80-c2f9-42ab-a596-6e031d8299f3", "LogicalResourceId": "CustomResource", "NoEcho": false, "Data": {"hoge": "hoge", "foo.hoge": "hoge"}}
        Status code: OK
        END RequestId: 9fb7a4fa-44b4-49ee-a5b7-bcbc57d40961
        REPORT RequestId: 9fb7a4fa-44b4-49ee-a5b7-bcbc57d40961  Duration: 351.55 ms     Billed Duration: 400 ms         Memory Size: 128 MB   Max Memory Used: 24 MB

フラットですがネストされてるっぽく参照できるDataを返していることが確認できます。

まとめ

微妙に納得できない感じですが、テンプレートでどうしてもリソース情報のネスト構造を維持したまま参照したい場合にLambda-Backedカスタムリソース側で吸収するのに使えそうです。

参考

AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない – Qiita
https://cloudpack.media/48318

Fn::GetAtt – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html

AWS::ElasticLoadBalancing::LoadBalancer – AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb.html#aws-properties-ec2-elb-ref

元記事はこちら

AWS CloudFormationのLambda-BackedカスタムリソースでネストされてるっぽいJSONを返す方法