LoginSignup
1

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-07-18

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

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

AWS CloudFormationのLambda-BackedカスタムリソースでネストされたJSONを返しても参照できない - Qiita
https://qiita.com/kai_kou/items/61a2b3c69ae2af4f2e40

結論

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://qiita.com/kai_kou/items/61a2b3c69ae2af4f2e40

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

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
1