it-swarm.com.de

Wie erstelle ich eine neue Version einer Lambda-Funktion mit CloudFormation?

Ich versuche, mit CloudFormation eine neue Version einer Lambda-Funktion zu erstellen.

Ich möchte mehrere Versionen der gleichen Lambda-Funktion haben, damit ich (a) Aliase auf verschiedene Versionen - wie DEV und PROD - verweisen und (b) auf eine frühere Version zurücksetzen kann

Dies ist die Definition meiner Lambda-Version:

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

Eine Version wird erstellt, wenn "aws cloudformation create-stack" ausgeführt wird, aber die nachfolgenden Befehle "aws cloudformation update-stack" führen nichts aus. Es wurden keine neuen Lambda-Versionen erstellt.

Ich versuche, eine neue Version der Lambda-Funktion zu erstellen, nachdem ich eine neue Zip-Datei in S3 hochgeladen und dann "update-stack" ausgeführt habe. Kann ich das mit CloudFormation machen? Ist die AWS :: Lambda :: -Version wirklich defekt (wie hier erwähnt https://github.com/hashicorp/terraform/issues/6067#issuecomment-211708071 ) oder erhalte ich einfach nichts?

Update 1.11.17 Offizielle Antwort des Amazon-Supports: "... um eine neue Version zu veröffentlichen, müssen Sie einen Zusatz (sic) definieren. AWS :: Lambda :: Versionsressource ... "

AWS CloudFormation/Lambda-Team, wenn Sie dies lesen - das ist inakzeptabel. Repariere es.

23
boris

AWS :: Lambda :: Version ist nicht nützlich. Sie müssen für jede Lambda-Version eine neue Ressource hinzufügen. Wenn Sie für jedes Cloudformation-Update eine neue Version veröffentlichen möchten, müssen Sie das System hacken.

Dieses Problem wurde beim Erstellen einer benutzerdefinierten Lambda-unterstützten Ressource behoben, die für jede Bereitstellung ausgelöst wird. In diesem Lambda erstelle ich eine neue Version für die in Parameter angegebene Lambda-Funktion.

Für die Lambda-Quelle können Sie http://serverless-Arch-eu-west-1.s3.amazonaws.com/serverless.Zip überprüfen.

Hier ist das Beispiel einer Cloudformation, die diese Implementierungs-Lambda-Funktion verwendet (möglicherweise müssen Sie einige Änderungen vornehmen):

{
  "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": "HERE_DEFINE_YOUR_LAMBDA"
    },
    "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"
        }
      }
    }
  }
}

(Disclaimer: Dieser Code ist ein Teil meines Buches. Weitere Informationen zu Lambda & API Gateway finden Sie unter: https://www.Amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/1787129195 )

15

Ich habe einen ähnlichen Anwendungsfall (CloudFormation muss zur Verwaltung einer Lambda-Funktion verwendet werden, um @Edge in CloudFront verwendet zu werden, für die immer eine bestimmte Lambda-Funktionsversion erforderlich ist, nicht $LATEST). Meine Suchanfragen haben mich zuerst bei dieser Frage gelandet Ich war froh, dass es nun native Unterstützung für die automatische Lambda-Versionierung mit der neuen AutoPublishAlias-Funktion des AWS Serverless Application Model (grundsätzlich ein optionaler zusätzlicher Satz übergeordneter Konstrukte für Ihre CloudFormation-Vorlagen) gibt.

Hier angekündigt: https://github.com/awslabs/serverless-application-model/issues/41#issuecomment-347723981

Für Details siehe:

Im Wesentlichen fügen Sie AutoPublishAlias in Ihre AWS::Serverless::Function-Definition ein:

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

An anderer Stelle in der CloudFormation-Vorlage können Sie die zuletzt veröffentlichte Version als !Ref MyFunction.Version (yaml-Syntax) referenzieren.

14
Rob Peters

Die Ressource AWS::Lambda::Version stellt nur eine einzige veröffentlichte Version der Lambda-Funktion dar. Bei jeder Aktualisierung Ihres Codes werden nicht automatisch neue Versionen veröffentlicht. Um dies zu erreichen, haben Sie zwei Möglichkeiten:

1. Benutzerdefinierte Ressource

Sie können Ihre eigene Custom Resource , die PublishVersion bei jedem Update aufruft.

Für diesen Ansatz müssen Sie immer noch mindestens einen Parameter ändern, wenn Sie Ihren Stack aktualisieren, um eine Aktualisierung der benutzerdefinierten Ressource auszulösen, die die Aktion PublishVersion auslöst. (Die Vorlage muss jedoch nicht wirklich aktualisiert werden.)

Hier ist ein vollständiges Arbeitsbeispiel:

 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. Vorlagenvorprozessor

Sie können einen Vorlagen-Präprozessor wie embedded Ruby verwenden (oder einfach Ihre Vorlage bei jeder Implementierung manuell aktualisieren), um eine neue Version bei jeder Aktualisierung Ihres Codes zu veröffentlichen, indem Sie die AWS::Lambda::Version-Ressource Logical ID ändern, wenn Ihr Code ist aktualisierte.

Beispiel:

# 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

Führen Sie den folgenden Befehl aus, um den Stapel zu erstellen/zu aktualisieren, während template.yml durch den Vorprozessor für erb-Vorlage geleitet wird:

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
7
wjordan

Antwort aktualisiert auf Februar 2018

Sie können AWS SAM (Serverless Application Model) und die Befehle sam package und sam deploy zum Aktualisieren von Lambda verwenden. Sie ähneln den Befehlen aws cloudformation package und aws cloudformation deploy, Sie können jedoch auch Lambda-Versionen automatisch aktualisieren.

SAM kann Ihren Code in ein Paket packen (oder ein anderes Zip-Paket verwenden), es in S3 hochladen und die $LATEST-Version des Lambda daraus aktualisieren. (Wenn dies alles ist, was Sie brauchen, können Sie dies auch mit aws cloudformation ohne SAM tun; die Codebeispiele sind die gleichen wie unten, verwenden Sie jedoch nur die Standarddeklarationen von CloudFormation). Mit SAM können Sie bei entsprechender Konfiguration auch automatisch eine Version veröffentlichen und einen Alias ​​aktualisieren, um darauf zu verweisen. Optional kann AWS CodeDeploy verwendet werden, um den Verkehr schrittweise von der vorherigen Version auf die neue Version zu verschieben und im Fehlerfall ein Rollback auszuführen. All dies wird in Sichere Lambda-Bereitstellungen erläutert.


Technisch gesehen ist die Idee, dass jedes Mal, wenn Sie den Stack aktualisieren, der Code Ihres AWS::Lambda::Function_ auf das new -Paket in S3 verweist. Dadurch wird sichergestellt, dass beim Aktualisieren des Stapels die $ LATEST-Version von Lambda aus dem neuen Paket aktualisiert wird. Anschließend können Sie auch die Veröffentlichung einer neuen Version automatisieren und einen Alias ​​darauf umstellen. 

Erstellen Sie dazu eine SAM-Vorlage, die der CloudFormation-Vorlage (einer Obermenge von) ähnelt. Es kann SAM-spezifische Deklarationen enthalten, wie die unten stehende für AWS::Serverless::Function. Verweisen Sie Code auf das Quellcode-Verzeichnis (oder eine vorinstallierte Zip-Datei), und legen Sie die AutoPublishAlias-Eigenschaft fest.

...

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
...

Lauf:

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

Dieses packt den Inhalt des Quellverzeichnisses als ZIP-Paket (wenn Code nicht bereits ein ZIP-Element ist), lädt es unter dem neuen, automatisch generierten Schlüssel in S3 hoch und generiert die endgültige CloudFormation-Vorlage in packaged.yaml, wodurch die richtige Code-Referenz eingefügt wird. so was:

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

Jetzt können Sie generierte packaged.yaml mit SAM verwenden, um die Funktion Version zu erstellen:

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

Dadurch wird die $LATEST-Version von Lambda aktualisiert. Wenn AutoPublishAlias definiert wurde, veröffentlichen Sie sie als neue Version und aktualisieren Sie den Alias, sodass er auf die neu veröffentlichte Version verweist.

In den Beispielen in SAM GitHub repo finden Sie einen vollständigen Vorlagencode.

6
Timur

Suchen Sie nach einem ähnlichen Element, das mit Lambda-Funktionen von S3 aus funktioniert.

Mein Anwendungsfall war folgender:

  • Sie verfügen über eine Cloudformationsvorlage, die eine Lambda-Funktion aus einer S3-Bucket-Position erstellt
  • Sie müssen diese Funktion aktualisieren, damit Sie Codeänderungen lokal vornehmen und die Änderungen in S3 übernehmen können
  • Sie möchten jetzt diese Änderungen in Lambda verschieben, um den Stack und die Cloud-Informationen zu aktualisieren. Es gibt keine zu aktualisierenden Änderungen. Daher müssen Sie den Code manuell über die AWS Lambda-Konsole aktualisieren.

Ich war nicht zufrieden damit, dass ich nach einer Alternative suchte und auf diese Frage stieß. Keine der Antworten funktionierte für mich genau, daher habe ich ein paar Ideen genommen und die Antworten hier angepasst und meine eigene Version in Python geschrieben.

Dieser Code wurde aus der Antwort von @wjordan angepasst, so dass er für die Idee und die ursprüngliche Antwort gutgeschrieben wird. Die Unterschiede sind:

  • Dies ist in Python geschrieben
  • Es funktioniert mit Lambda-Code, der aus einem S3-Bucket bereitgestellt wird
  • Es aktualisiert den Code und veröffentlicht eine neue Version

Sie benötigen einen Nonce-Parameter. Sie ändern den Wert dieses Parameters, wenn der Code in Lambda erneut veröffentlicht werden muss. Dadurch wird sichergestellt, dass die Cloudform Ihre benutzerdefinierte Ressource aktualisiert. Wenn die benutzerdefinierte Ressource aktualisiert wird, wird der Python-Code ausgeführt, der schließlich Ihren Lambda-Code aktualisiert.

Hoffe das hilft jemandem.

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

Leider ist dies mit CloudFormation nicht möglich. Sie müssen für jede Version neue AWS::Lambda::Version-Abschnitte in Ihrer CloudFormation-Vorlage hinzufügen. 

Die nächste Lösung wäre, .erb-Vorlagen zu erstellen und CloudFormation-Vorlagen mit allen Versionen zu generieren.

1
Sinan Gedik

Dies ist ein bisschen hacken und hängt von der Verwendung von gitlab-ci (oder etwas Ähnlichem) ab, aber ich finde es sehr nützlich, den Commit-Hash in eine Cloudformation-Vorlage (über die Parameter der Vorlage) zu übergeben.

(Es ist ein bisschen wie @Jerrys Antwort, aber mit dem Commit-Hash.)

In diesem Fall könnten Sie Folgendes tun:

Verfügen Sie in Ihrer Vorlage über einen Parameter für den Festschreibungs-Hash, z.

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

Sie können dies dann in der Lambda-Ressource wie folgt referenzieren:

  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
      ...

Ihre ci-Pipeline muss dann ungefähr so ​​aussehen (vorausgesetzt, Sie nennen Ihre Cloudformation-Vorlage 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

Mit dieser Technik können Sie cfn-init auch für EC2-Metadaten auslösen.

0
Tom Greenwood
  1. Wir können ein Lambda-Bereitstellungspaket erstellen. 
  2. Übergeben Sie das Lambda-Paket mit der Version als einen der Parameter für die Cloud-Formation, z. B. "LambdaPakcageNameWithVersion"; 
  3. Verwenden Sie "LambdaPakcageNameWithVersion" als Schlüssel für den Lambda-Code s3. 
  4. Das neue Lamdba-Paket wird bereitgestellt, wenn der Befehl aws-cli ausgeführt wird, um den Cloudformationsstapel zu aktualisieren oder die CI/CD-Pipeline auszuführen.

  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

Ich habe dieses Problem mithilfe von CI/CD, einem Ant-Skript und der Git-Revision gelöst, um für jedes Commit einen eindeutigen Zip-Namen im S3-Bucket zu erstellen.

Das Ant-Skript wird von der CI/CD aufgerufen, um die Git-Revision in den Namen der Lambda-Code-Zip-Datei und der Wolkenbildungsvorlage zu ersetzen. Diese Verweise werden erstellt, bevor der Code und die Cloudformations-Skripte in S3 kopiert werden. Dies ähnelt der Funktionsweise von SAM, funktioniert jedoch mit einfachen alten Cloudformation-Stacks und vor allem mit Stack-Sets, die möglicherweise für mehrere Konten bereitgestellt werden müssen. Zum Zeitpunkt des Schreibens war SAM nicht mit CF-Stack-Sets kompatibel

Es gibt zwei Dateien: die Ant-Datei und eine Eigenschaftendatei, die der Ant-Datei mitteilt, welche Lambda-Quellverzeichnisse zu Zip gehören.

Zuerst die build.xml Ant-Datei:

<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="@[email protected]" 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>

Das lambda.Zip.build.properties Datei sieht so aus:

# 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 @[email protected] 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...

Und dann die CloudFormation-Vorlage:

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

Das Ergebnis ist eine Zip-Datei mit einem eindeutigen Namen lambda-code-0f993c3.Zip und eine Wolkenbildungsvorlage mit S3Key unter Bezugnahme auf den eindeutigen Namen.

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

Stellen Sie die Vorlage vom S3-Speicherort aus bereit und erzwingen Sie, dass der vorhandene Lambda-Code jedes Mal aktualisiert wird.

0