web-dev-qa-db-ja.com

CloudFormationを使用してLambda関数の新しいバージョンを作成する方法は?

CloudFormationを使用してLambda関数の新しいバージョンを作成しようとしています。

同じLambda関数の複数のバージョンを使用して、(a)DEVやPRODなどの異なるバージョンでエイリアスを指定し、(b)以前のバージョンにロールバックできるようにしたい

これは私のLambdaバージョンの定義です:

LambdaVersion:
  Type: AWS::Lambda::Version
  Properties:
    FunctionName:
      Ref: LambdaFunction

「aws cloudformation create-stack」を実行するとバージョンが作成されますが、後続の「aws cloudformation update-stack」コマンドは何も実行しません。新しいLambdaバージョンは作成されていません。

新しいZipファイルをS3にアップロードし、「update-stack」を実行した後に作成されたLambda関数の新しいバージョンを取得しようとしています。 CloudFormationでできますか? AWS :: Lambda :: Versionは本当に壊れていますか(ここで述べたように https://github.com/hashicorp/terraform/issues/6067#issuecomment-211708071 )または私はちょうど何かを得ていない?

アップデート1/11/17Amazonサポートからの公式返信: "...公開する新しいバージョンについては、追加を定義する必要があります(sic) AWS :: Lambda :: Versionリソース... "

AWS CloudFormation/Lambdaチーム、これを読んでいる場合-これは受け入れられません。修理する。

32
boris

AWS :: Lambda :: Versionは役に立ちません。すべてのLambdaバージョンに新しいリソースを追加する必要があります。 Cloudformationの更新ごとに新しいバージョンを公開する場合は、システムをハッキングする必要があります。

この問題を解決して、デプロイごとにトリガーされるLambdaがバックアップするカスタムリソースを作成しました。このLambda内で、パラメーターで指定されたLambda関数の新しいバージョンを作成しています。

Lambdaのソースについては、 http://serverless-Arch-eu-west-1.s3.amazonaws.com/serverless.Zip を確認できます。

以下は、このDeployment Lambda関数を使用したCloudformationの例です(変更が必要な場合があります)。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "DeploymentTime": {
      "Type": "String",
      "Description": "It is a timestamp value which shows the deployment time. Used to rotate sources."
    }
  },
  "Resources": {
    "LambdaFunctionToBeVersioned": {
      "Type": "AWS::Lambda::Function",
       ## HERE DEFINE YOUR LAMBDA AS USUAL ##
    },
    "DeploymentLambdaRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "ManagedPolicyArns": [
          "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        ],
        "Policies": [
          {
            "PolicyName": "LambdaExecutionPolicy",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "lambda:PublishVersion"
                  ],
                  "Resource": [
                    "*"
                  ]
                }
              ]
            }
          }
        ]
      }
    },
    "DeploymentLambda": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Role": {
          "Fn::GetAtt": [
            "DeploymentLambdaRole",
            "Arn"
          ]
        },
        "Handler": "serverless.handler",
        "Runtime": "nodejs4.3",
        "Code": {
          "S3Bucket": {
            "Fn::Sub": "serverless-Arch-${AWS::Region}"
          },
          "S3Key": "serverless.Zip"
        }
      }
    },
    "LambdaVersion": {
      "Type": "Custom::LambdaVersion",
      "Properties": {
        "ServiceToken": {
          "Fn::GetAtt": [
            "DeploymentLambda",
            "Arn"
          ]
        },
        "FunctionName": {
          "Ref": "LambdaFunctionToBeVersioned"
        },
        "DeploymentTime": {
          "Ref": "DeploymentTime"
        }
      }
    }
  }
}

(免責事項:このコードは私の本の一部です。LambdaとAPI Gatewayの詳細については、次を確認できます。 https://www.Amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/ 1787129195

18

私は同様のユースケースを持っています(CloudFormationを使用して、CloudFrontで@Edgeを使用するラムダ関数を管理する必要があり、$LATESTではなく、特定のラムダ関数バージョンが常に必要です)最初に、少し掘り下げてみると、AWS Serverless Application Modelの新しいAutoPublishAlias機能(基本的には、 CloudFormationテンプレート)。

ここで発表: https://github.com/awslabs/serverless-application-model/issues/41#issuecomment-347723981

詳細については、以下を参照してください。

基本的に、AWS::Serverless::Function定義にAutoPublishAliasを含めます。

MyFunction:
  Type: "AWS::Serverless::Function"
  Properties:
    # ...
    AutoPublishAlias: MyAlias

そして、CloudFormationテンプレートの他の場所で、最新の公開バージョンを!Ref MyFunction.Version(yaml構文)として参照できます。

21
Rob Peters

AWS::Lambda::Version リソースは、単一の公開されたLambda関数バージョンのみを表します。コードの更新ごとに新しいバージョンを自動的に公開するわけではありません。これを実現するには、2つのオプションがあります。

1.カスタムリソース

更新ごとに PublishVersion を呼び出す独自の カスタムリソース を実装できます。

この方法では、PublishVersionアクションをトリガーするカスタムリソースの更新をトリガーするために、スタックを更新するたびに少なくとも1つのパラメーターを変更する必要があります。 (ただし、テンプレートを実際に更新する必要はありません。)

完全で実用的な例を次に示します。

 Launch Stack 

Description: Publish a new version of a Lambda function whenever the code is updated.
Parameters:
  Nonce:
    Description: Change this string when code is updated.
    Type: String
    Default: "Test"
Resources:
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Nonce: !Ref Nonce
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          exports.handler = function(event, context) {
            return response.send(event, context, response.SUCCESS, {Result: '${Nonce}'});
          };
      Runtime: nodejs4.3
  LambdaDeploy:
    Type: Custom::LambdaVersion
    Properties:
      ServiceToken: !GetAtt LambdaDeployFunction.Arn
      FunctionName: !Ref MyFunction
      Nonce: !Ref Nonce
  LambdaDeployFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var AWS = require('aws-sdk');
          var response = require('cfn-response');
          exports.handler = (event, context) => {
            console.log("Request received:\n", JSON.stringify(event));
            if (event.RequestType == 'Delete') {
              return response.send(event, context, response.SUCCESS);
            }
            var lambda = new AWS.Lambda();
            lambda.publishVersion({FunctionName: event.ResourceProperties.FunctionName}).promise().then((data) => {
              return response.send(event, context, response.SUCCESS, {Version: data.Version}, data.FunctionArn);
            }).catch((e) => {
              return response.send(event, context, response.FAILED, e);
            });
          };
      Runtime: nodejs4.3
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyName: PublishVersion
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['lambda:PublishVersion']
            Resource: '*'
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaDeploy.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result

2.テンプレートプリプロセッサ

embedded Ruby (または、デプロイごとにテンプレートを手動で更新する)など)のテンプレートプリプロセッサを使用して、AWS::Lambda::Versionを変更することにより、コードの更新ごとに新しいバージョンを公開できます。リソースの 論理ID コードが更新されるたび。

例:

# template.yml
Description: Publish a new version of a Lambda function whenever the code is updated.
<%nonce = Rand 10000%>
Resources:
  LambdaVersion<%=nonce%>:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref MyFunction
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Nonce: <%=nonce%>
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          exports.handler = function(event, context) {
            return response.send(event, context, response.SUCCESS, {Result: '<%=nonce%>'});
          };
      Runtime: nodejs4.3
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaVersion<%=nonce%>.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result

template.ymlerbテンプレートプリプロセッサに渡しながらスタックを作成/更新するには、次を実行します。

aws cloudformation [create|update]-stack \
  --stack-name [stack_name] \
  --template-body file://<(Ruby -rerb -e "puts ERB.new(ARGF.read).result" < template.yml) \
  --capabilities CAPABILITY_IAM
10
wjordan

2018年2月に更新された回答

AWS SAM(サーバーレスアプリケーションモデル) 、およびそのsam packageおよびsam deployコマンドを使用してLambdaを更新できます。これらはaws cloudformation packageおよびaws cloudformation deployコマンドに似ていますが、Lambdaバージョンを自動的に更新することもできます。

SAMは、コードをパッケージ化(または、別の方法で作成したZipパッケージを取得)してS3にアップロードし、そこからLambdaの$LATESTバージョンを更新できます。 (これで十分な場合は、SAMなしでaws cloudformationでも実行できます。コード例は以下と同じですが、CloudFormationの標準宣言のみを使用します)。次に、SAMを使用して、適切に構成されていれば、バージョンを自動的に公開し、エイリアスを更新してそれを指すようにすることもできます。また、オプションで、AWS CodeDeployを使用して、以前のバージョンから新しいバージョンにトラフィックを徐々に移動し、エラーが発生した場合にロールバックできます。これはすべて Safe Lambda deployments で説明されています。


技術的には、スタックを更新するたびに、AWS::Lambda::FunctionCodeがS3のnewパッケージを指す必要があるという考え方です。これにより、スタックを更新すると、Lambdaの$ LATESTバージョンが新しいパッケージから確実に更新されます。次に、新しいバージョンの公開を自動化し、エイリアスをそれに切り替えることもできます。

そのために、CloudFormationテンプレート(のスーパーセット)に似たSAMテンプレートを作成します。以下のAWS::Serverless::FunctionのようなSAM固有の宣言を含めることができます。 Codeをソースコードディレクトリ(または事前にパッケージ化されたZip)にポイントし、AutoPublishAliasプロパティを設定します。

...

MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      ...  # all usual CloudFormation properties are accepted 
      AutoPublishAlias: dev  # will publish a Version and create/update Alias `dev` to point to it
      Code: ./my/lambda/src
...

実行:

$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket my-bucket

これは、ソースディレクトリの内容をZipとしてパッケージ化し(CodeがまだZipでない場合)、新しい自動生成キーでS3にアップロードし、packaged.yamlに最終的なCloudFormationテンプレートを生成して、適切なCodeそれへの参照。このような:

...
MyFunction:
    Properties:
      Code:
        S3Bucket: my-bucket
        S3Key: ddeeaacc44ddee33ddaaee223344
...

生成されたpackaged.yamlをSAMで使用して、関数Versionを作成できます。

sam deploy --template-file packaged.yaml --stack-name my-stack [--capabilities ...]

これにより、Lambdaの$LATESTバージョンが更新され、AutoPublishAliasが定義されている場合は、それを新しいバージョンとして発行し、新しく発行されたバージョンを指すようにエイリアスを更新します。

完全なテンプレートコードについては、 SAM GitHubリポジトリの例 を参照してください。

7
Timur

S3からデプロイされたLambda関数で機能する同様のものを探しています。

私のユースケースはこれでした:

  • S3バケットの場所からLambda関数を作成するcloudformationテンプレートがあります
  • この関数を更新して、コードをローカルで変更し、変更をS3にプッシュする必要があります。
  • これらの変更をLambdaにプッシュして、スタックを更新しようとすると、cloudformationは更新する変更がないため、AWS Lambdaコンソールを使用して手動でコードを更新する必要があると言います。

これに満足せず、私は代替案を探し、この質問に出会いました。答えがまったくうまくいかなかったので、ここでいくつかのアイデアを取り入れ、ここで答えを修正し、Pythonで書かれた独自のバージョンを作成しました。

このコードは、@ wjordanからの回答を基にしていますので、アイデアと元の回答は彼の功績です。違いは次のとおりです。

  • これはPythonで書かれています
  • S3バケットからデプロイされたLambdaコードで動作します
  • コードを更新し、新しいバージョンを公開します

ナンスパラメータが必要です。コードをLambdaに再公開する必要がある場合、このパラメーターの値を変更します。これは、cloudformationがカスタムリソースを確実に更新するためです。カスタムリソースが更新されると、最終的にLambdaコードを更新するPythonコードが実行されます。

これが誰かを助けることを願っています。

Description: Publish a new version of a Lambda function whenever the code is updated.
Parameters:
  Nonce:
    Description: Change this string when code is updated.
    Type: String
    Default: "Test"
Resources:
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Nonce: !Ref Nonce
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        S3Bucket: BucketContainingYourLambdaFunction
        S3Key: KeyToYourLambdaFunction.Zip
      Runtime: "python3.6"
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  LambdaDeployCustomResource:
    Type: Custom::LambdaVersion
    Properties:
      ServiceToken: !GetAtt LambdaDeployFunction.Arn
      FunctionName: !Ref MyFunction
      S3Bucket: BucketContainingYourLambdaFunction
      S3Key: KeyToYourLambdaFunction.Zip
      Nonce: !Ref Nonce
  LambdaDeployFunction:
    Type: AWS::Lambda::Function
    DependsOn: LambdaDeployFunctionExecutionRole
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaDeployFunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import boto3
          import json
          import logging
          import cfnresponse
          import time
          from botocore.exceptions import ClientError

          def handler(event, context):
            logger = logging.getLogger()
            logger.setLevel(logging.INFO)
            logger.info (f"Input parameters from cloud formation: {event}")
            responseData = {}
            if (event["RequestType"] == 'Delete'):
              logger.info("Responding to delete event...")
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)

            try:            
              lambdaClient = boto3.client('lambda')
              s3Bucket = event['ResourceProperties']['S3Bucket']
              s3Key = event['ResourceProperties']['S3Key']
              functionName = event['ResourceProperties']['FunctionName']
              logger.info("Updating the function code for Lambda function '{}' to use the code stored in S3 bucket '{}' at key location '{}'".format(functionName, s3Bucket, s3Key))
              logger.info("Sleeping for 5 seconds to allow IAM permisisons to take effect")
              time.sleep(5)             
              response = lambdaClient.update_function_code(
                FunctionName=functionName,
                S3Bucket='{}'.format(s3Bucket),
                S3Key='{}'.format(s3Key),
                Publish=True)
              responseValue = "Function: {}, Version: {}, Last Modified: {}".format(response["FunctionName"],response["Version"],response["LastModified"])
              responseData['Data'] = responseValue
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, response["FunctionArn"])
            except ClientError as e:
              errorMessage = e.response['Error']['Message']
              logger.error(errorMessage)
              cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
      Runtime: "python3.6"
      Timeout: "30"
  LambdaDeployFunctionExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: 
            Service: lambda.amazonaws.com
          Action: 
            - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyName: ReadS3BucketContainingLambdaCode
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: 
              - s3:GetObject              
            Resource: ArnOfS3BucketContainingLambdaCode/*
      - PolicyName: UpdateCodeAndPublishVersion
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: 
              - lambda:UpdateFunctionCode
              - lambda:PublishVersion
            Resource: '*'
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaDeploy.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result 
2
CarlR

残念ながら、CloudFormationを使用してこれを行うことはできません。各バージョンのCloudFormationテンプレートに新しいAWS::Lambda::Versionセクションを追加する必要があります。

最も近い解決策は、.erbテンプレートを作成し、すべてのバージョンでCloudFormationテンプレートを生成することです。

1
Sinan Gedik

これはちょっとしたハックであり、gitlab-ci(または同様のもの)の使用に依存しますが、コミットハッシュを(テンプレートのパラメーターを介して)cloudformationテンプレートに渡すことは非常に便利です。

(@Jerryの答えに少し似ていますが、コミットハッシュを使用しています。)

この場合、次のようなことができます。

テンプレートにコミットハッシュ用のパラメーターがあります。例:

AWSTemplateFormatVersion: '2010-09-09'
Description: Template for Lambda Sample.
Parameters:
  ciCommitSha:
    Type: String
  s3Bucket:
    Type: String
  ...

次のように、ラムダリソースでこれを参照できます。

  CFNLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: cfn_trigger_fn
      Description: lambda which gets triggered by cloudformation
      Runtime: python3.7
      Code:
        S3Bucket: !Ref s3Bucket
        S3Key: !Join [ ".", [ !Ref ciCommitSha, "Zip"]]
      Handler: function.handler
      ...

Ciパイプラインは次のように見える必要があります(cloudformationテンプレートをstack-template.yamlと呼ぶと仮定します):

variables:
  REGION: us-east-1
  S3_BUCKET_NAME: my-bucket

stages:
 - build
 - Push
 - deploy

build-package:
  stage: build
  script:
    - some code to produce a deployment package called function.Zip
  artifacts:
    name: deployment_package
    paths:
      - function.Zip


Push-code:
  stage: Push
  script:
    - aws s3 cp function.Zip s3://$S3_BUCKET_NAME/$CI_COMMIT_SHA.Zip

deploy-trigger-stack:
  stage: deploy
  script: 
      - aws cloudformation deploy
            --template-file stack-template.yaml
            --stack-name my-stack
            --region $REGION
            --no-fail-on-empty-changeset
            --capabilities CAPABILITY_NAMED_IAM
            --parameter-overrides
            ciCommitSha=$CI_COMMIT_SHA
            s3Bucket=$S3_BUCKET_NAME

この手法を使用して、EC2メタデータでcfn-initをトリガーすることもできます。

0
Tom Greenwood
  1. Lambdaデプロイメントパッケージを作成できます。
  2. LambdaパッケージをCloud Formationパラメーターの1つとしてバージョンとともに渡します。 "LambdaPakcageNameWithVersion";
  3. Lambdaコードs3キーとして「LambdaPakcageNameWithVersion」を使用します。
  4. 新しいLamdbaパッケージは、aws-cliコマンドを実行してcloudformationスタックを更新するとき、またはCI/CDパイプラインを実行するときにデプロイされます。
  MyLambda:
    Type: AWS::Lambda::Function
    Properties:
      Role: LambdaRole
      Code:
        S3Bucket: LambdaPackageS3Bucket
        S3Key: !Sub "${LambdaPakcageNameWithVersion}"
      FunctionName: LambdaFunctionName
      Handler: lambda_function.lambda_handler
      Runtime: python3.6
      Timeout: 60
0
Jerry

CI/CD、antスクリプト、およびgitリビジョンを使用してこれを解決し、コミットごとにS3バケットに一意のZip名を作成しました。

AntスクリプトはCI/CDによって呼び出され、gitリビジョンをラムダコードZipファイルとcloudformationテンプレートの名前に置き換えます。これらの参照は、コードとcloudformationスクリプトをS3にコピーする前に作成されます。これはSAMの動作に似ていますが、これは単純な古いCloudformationスタックで動作します。執筆時点では、SAMはCFスタックセットと互換性がありませんでした

2つのファイルがあります。Antファイルと、AntファイルにZipするラムダソースディレクトリを伝えるプロパティファイルです。

最初にbuild.xml antファイル:

<project basedir="." name="AWS Lambda Tooling Bucket Zip" default="ziplambdas">
    <!-- this ant file is responsible for zipping up lambda source code that needs to be placed on an S3 bucket for deployment.
        It reads a file `lambda-Zip-build.properties` that contains a list of lambda folders and the corresponding Zip names.
        This allows a lambda to include source code and any required library packages into a single Zip for deployment.
        For further information refer to the comments at the top of the Zip properties file.
    -->

    <property name="ant.home" value="${env.ANT_HOME}" />
    <taskdef resource="net/sf/antcontrib/antlib.xml">
        <classpath path="${ant.home}/lib/ant-contrib-1.0b3.jar" />
    </taskdef>

    <!-- <available file=".git" type="dir" property="git.present"/> -->
    <available file="../../.git" type="dir" property="git.present"/>

    <!-- get the git revision to make a unique filename on S3. This allows the Zip key to be replaced, forcing an update if CloudFormation is deployed. Clunky,
         AWS Support raised but advice was to use SAM, which is not compatible with StackSets ... *sigh* -->
    <target name="gitrevision" description="Store git revision in ${repository.version}" if="git.present">
        <exec executable="git" outputproperty="git.revision" failifexecutionfails="false" errorproperty="">
            <arg value="describe"/>
            <arg value="--tags"/>
            <arg value="--always"/>
            <arg value="HEAD"/>
        </exec>
        <condition property="repository.version" value="${git.revision}" else="unknown">
            <and>
                <isset property="git.revision"/>
                <length string="${git.revision}" trim="yes" length="0" when="greater"/>
            </and>
        </condition>
        <echo>git revision is ${git.revision} </echo>
    </target>

    <target name="replace.git.revision.in.files" depends="gitrevision" description="replace the git marker text in cloudformation files and Zip properties file">
        <replace dir="." token="@git.revision@" value="${git.revision}" summary="yes"/>
    </target>

    <property file="lambda.Zip.build.properties"/>

    <!-- Zip the lambda code into a unique Zip name based on the git revision -->
    <target name="ziplambdas" description="Create Zip files based on the property list" depends="replace.git.revision.in.files">
        <property file="lambda.Zip.build.properties" prefix="zipme." />
        <propertyselector property="Zip.list" match="^zipme\.(.*)" select="\1"/>

        <foreach list="${Zip.list}" delimiter="," target="Zip" param="folder"/>
    </target>

    <target name="Zip">
        <propertycopy property="Zip.path" from="${folder}" />
        <basename property="Zip.file" file="${Zip.path}" />
        <echo message="${folder} is being zipped to ${Zip.path}"/>
        <Zip destfile="${Zip.path}">
            <zipfileset dir="${folder}">
               <exclude name="**/${Zip.file}"/>
            </zipfileset> 
        </Zip>
    </target>

</project>

lambda.Zip.build.propertiesファイルは次のようになります。

# This property file contains instructions for CI/CD Build Process to Zip directories containing lambda code to place on the S3  bucket.
# Lambda source code when deployed by CloudFormation must be available inside a Zip file in a S3 bucket.
# CI/CD runs an ant task that reads this file to create the appropriate Zip files referenced by the CloudFormation scripts. 
# 
# Each property key value pair below contains a key of the top level directory containing the lambda code (in python, javascript or whatever), 
# and a value of the path to the Zip file that should be deployed to S3. The @git.revision@ tag is substituted with the actual git revision before copying to S3.
# This allows the lambda S3key to change for each deployment and forces a lambda code update. 
#
# for example: myproject/lambda/src=myproject/lambda/[email protected]@.Zip
#              ^^ Directory    ^^ Zip File
#
###################################################################################################################################################################################
myproject/lambda/src=myproject/lambda/[email protected]@.Zip
# place your key value pairs above here...

そして、CloudFormationテンプレート:

Resources:
  MyLambda:
    Type: AWS::Lambda::Function
    Properties:
    # git.revision is placed when code is zipped up by CI/CD and placed on S3 bucket. It allows a unique name for each commit and thereby forces
    # lambda code to be replaced on cloudformation stackset redeployment.
      Code:
        S3Bucket: mybucket
        S3Key: myproject/lambda/[email protected]@.Zip
      Handler: autotag-costcentre.lambda_handler
      MemorySize: 128
      Runtime: python3.7
      Timeout: 10
      .... etc

結果は、一意の名前lambda-code-0f993c3.Zipを持つZipファイルと、一意の名前を参照するS3Keyを持つCloudformationテンプレートです。

S3Key: myproject/lambda/lambda-code-0f993c3.Zip

S3の場所からテンプレートを展開すると、既存のラムダコードが毎回強制的に更新されます。

0