LoginSignup
5
2

More than 3 years have passed since last update.

AWS CloudFormationのAWS Lambda-backedカスタムリソースで最新のAWS SDKを利用する

Last updated at Posted at 2019-07-01

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

AWS Lambda-backed カスタムリソースを利用するとAWS CloudFormationが対応していないリソースを管理することができて便利なのですが、AWS Lambdaで利用できるAWS SDK(ここではPythonのboto3)のバージョンが最新じゃない場合に困ることがあります。

AWS Lambda-backed カスタムリソース - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html

そんなときにどうしたら良いものか悩んでいたのですが、AWS Lambda Layersが利用できるみたいだったので試してみました。

前提

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

AWS Lambda Layersに最新のAWS SDKのLayerを作成する

AWS Lambda Layersで最新のAWS SDKを利用する方法は下記を参考にさせてもらいました。(感謝

Lambda Layers で最新の AWS SDK を使用する - Qiita
https://qiita.com/hayao_k/items/b9750cc8fa69d0ce91b0

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ

> mkdir python
> pip install -t ./python boto3
> zip -r python.zip ./python

> aws lambda publish-layer-version \
  --layer-name boto3 \
  --zip-file fileb://python.zip \
  --compatible-runtimes python3.7

{
    "Content": {
        "Location": "https://prod-04-2014-layers.s3.amazonaws.com/snapshots/(略)",
        "CodeSha256": "JZM5sEEyGBPgips+y+F0/X5rHXJIPkcLYeazyXiLkTk=",
        "CodeSize": 8572137
    },
    "LayerArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:boto3",
    "LayerVersionArn": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:boto3:1",
    "Description": "",
    "CreatedDate": "2019-06-21T08:57:29.995+0000",
    "Version": 1,
    "CompatibleRuntimes": [
        "python3.7"
    ]
}

AWS CloudFormationのテンプレートを作成する

AWS Lambda-backedカスタムリソースでboto3 のバージョンを確認するテンプレートを作成します。
比較のためにLayer利用する/しないのリソースを準備します。

> touch cfn-template.yaml
cfn-template.yaml
Resources:
  NonUseLambdaLayer:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt NonUseLambdaLayerFunction.Arn

  UseLambdaLayer:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt UseLambdaLayerFunction.Arn

  # 標準のboto3を利用
  NonUseLambdaLayerFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          import boto3
          def handler(event, context):
            print(boto3.__version__)
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
      Runtime: python3.7

  # Lambda Layerのboto3を利用
  UseLambdaLayerFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          import boto3
          def handler(event, context):
            print(boto3.__version__)
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
      Runtime: python3.7
      Layers:
        - arn:aws:lambda:us-east-1:xxxxxxxxxxxx:layer:boto3:1

  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:*:*:*"

AWS CLIからCloudFormationのスタックを作成します。Lambda関数実行用のロールを作成するので、--capabilities CAPABILITY_IAM オプションを指定します。

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

{
    "StackId": "arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/cfn-lambda-backed-test/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

スタック作成できたらリソース一覧からAWS Lambdaの関数名を取得します。

> aws cloudformation list-stack-resources \
  --stack-name cfn-lambda-backed-test

{
    "StackResourceSummaries": [
        {
            "LogicalResourceId": "FunctionExecutionRole",
            "PhysicalResourceId": "cfn-lambda-backed-test-FunctionExecutionRole-XXXXXXXXXXXX",
            "ResourceType": "AWS::IAM::Role",
            "LastUpdatedTimestamp": "2019-06-24T07:25:29.253Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "NonUseLambdaLayer",
            "PhysicalResourceId": "2019/06/24/[$LATEST]8e94b0b2ffc54b00acf35a004e68c522",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-24T07:25:37.368Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "NonUseLambdaLayerFunction",
            "PhysicalResourceId": "cfn-lambda-backed-te-NonUseLambdaLayerFunctio-XXXXXXXXXXXXX",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-24T07:25:32.817Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "UseLambdaLayer",
            "PhysicalResourceId": "2019/06/24/[$LATEST]200facdec8cb4d77ac3dd5f333ceb848",
            "ResourceType": "Custom::CustomResource",
            "LastUpdatedTimestamp": "2019-06-24T07:25:41.716Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "LogicalResourceId": "UseLambdaLayerFunction",
            "PhysicalResourceId": "cfn-lambda-backed-test-UseLambdaLayerFunction-XXXXXXXXXXXXX",
            "ResourceType": "AWS::Lambda::Function",
            "LastUpdatedTimestamp": "2019-06-24T07:25:36.240Z",
            "ResourceStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackResourceDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

リソースが取得できたらLambda関数のログからboto3 のバージョンを確認します。
上記リソースリストにあるResourceTypeAWS::Lambda::FunctionPhysicalResourceId が関数名になります。
スタック作成時のログを確認しても良いのですが、ここでは簡単にしたかったので関数を実行して確認します。

# 標準のboto3
> aws lambda invoke \
  --function-name cfn-lambda-backed-te-NonUseLambdaLayerFunctio-XXXXXXXXXXXX \
  --log-type Tail \
  outputfile.txt \
  --query 'LogResult' | tr -d '"' | base64 -D

START RequestId: 859e7e0b-4041-4fa6-bf61-84a31bd72cc9 Version: $LATEST
1.9.42
    responseUrl = event['ResponseURL']e 15, in sendCCESS, {})
END RequestId: 859e7e0b-4041-4fa6-bf61-84a31bd72cc9
REPORT RequestId: 859e7e0b-4041-4fa6-bf61-84a31bd72cc9  Duration: 54.69 ms      Billed Duration: 100 ms         Memory Size: 128 MB   Max Memory Used: 56 MB


# Lambda Layerのboto3
> aws lambda invoke \
  --function-name cfn-lambda-backed-test-UseLambdaLayerFunction-XXXXXXXXXXXXX \
  --log-type Tail \
  outputfile.txt \
  --query 'LogResult' | tr -d '"' | base64 -D

START RequestId: 2e422fb4-95ae-4274-b9a5-4ced212a78ec Version: $LATEST
1.9.173
    responseUrl = event['ResponseURL']e 15, in sendCCESS, {})
END RequestId: 2e422fb4-95ae-4274-b9a5-4ced212a78ec
REPORT RequestId: 2e422fb4-95ae-4274-b9a5-4ced212a78ec  Duration: 28.95 ms      Billed Duration: 100 ms         Memory Size: 128 MB   Max Memory Used: 35 MB

Layerを利用している関数で最新のAWS SDKを利用できることが確認できました。

まとめ

AWS Lambda LayersへのLayer作成部分もスタックに含めることができればよいのですが、Zipファイルを事前にS3へ上げるなりの準備が必要で、そうなるとS3のリソースを事前に作成しなきゃ。。。
など、、、
どうしても1アクションで完結しなさそうだったので、Layer作成は手動ですることにしました。

もう少し考えればうまくまとまりそうな気がしてますが、今のところはこれで満足です。

参考

AWS Lambda-backed カスタムリソース - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html

Lambda Layers で最新の AWS SDK を使用する - Qiita
https://qiita.com/hayao_k/items/b9750cc8fa69d0ce91b0

5
2
0

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
5
2