简体   繁体   English

如何使用 CloudFormation 创建一个 Lambda function 的新版本?

[英]How to create a new version of a Lambda function using CloudFormation?

I'm trying to create a new version of a Lambda function using CloudFormation.我正在尝试使用 CloudFormation 创建 Lambda function 的新版本。

I want to have multiple versions of the same Lambda function so that I can (a) point aliases at different versions - like DEV and PROD - and (b) be able to roll back to an earlier version我想要同一个 Lambda function 的多个版本,这样我就可以 (a) 将别名指向不同的版本 - 如 DEV 和 PROD - 并且 (b) 能够回滚到早期版本

This is the definition of my Lambda version:这是我的 Lambda 版本的定义:

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

A version gets created when running "aws cloudformation create-stack" but the subsequent "aws cloudformation update-stack" commands don't do anything.运行“aws cloudformation create-stack”时会创建一个版本,但随后的“aws cloudformation update-stack”命令不会执行任何操作。 There are no new Lambda versions created.没有创建新的 Lambda 版本。

I'm trying to get a new version of the Lambda function created after I upload new zip file to S3 and then run "update-stack".在我将新的 zip 文件上传到 S3 然后运行“update-stack”之后,我正在尝试创建 Lambda function 的新版本。 Can I do it with CloudFormation?我可以用 CloudFormation 做吗? Is AWS::Lambda::Version really broken (as mentioned here https://github.com/hashicorp/terraform/issues/6067#issuecomment-211708071 ) or am I just not getting something?是 AWS::Lambda::Version 真的坏了(如这里提到的https://github.com/hashicorp/terraform/issues/6067#issuecomment-211708071 )还是我只是没有得到什么?

Update 1/11/17 Official reply from Amazon support: "...for any new version to be published you need to define an addition (sic) AWS::Lambda::Version resource..." 2017 年 1 月 11 日更新来自亚马逊支持的官方回复:“...对于要发布的任何新版本,您需要定义一个附加(原文如此) AWS::Lambda::Version 资源...”

AWS CloudFormation/Lambda team, if you're reading this - this is unacceptable. AWS CloudFormation/Lambda 团队,如果您正在阅读本文 - 这是不可接受的。 Fix it.修理它。

I have a similar use case (needing to use CloudFormation to manage a lambda function to be used @edge in CloudFront, for which a specific lambda function version is always required, not $LATEST ) and my searches landed me at this question first, but after a bit more digging I was happy to find there is now native support for automatic lambda versioning with the new AutoPublishAlias feature of the AWS Serverless Application Model (basically an optional extra set of higher-level constructs for your CloudFormation templates).我有一个类似的用例(需要使用 CloudFormation 来管理要在 CloudFront 中使用 @edge 的 lambda 函数,为此始终需要特定的 lambda 函数版本,而不是$LATEST )并且我的搜索首先让我找到了这个问题,但是经过更多的挖掘,我很高兴地发现现在AutoPublishAlias AWS 无服务器应用程序模型的新AutoPublishAlias功能(基本上是您的 CloudFormation 模板的一组可选的额外高级构造)对自动 lambda 版本控制的本机支持。

Announced here: https://github.com/awslabs/serverless-application-model/issues/41#issuecomment-347723981在此宣布: https : //github.com/awslabs/serverless-application-model/issues/41#issuecomment-347723981

For details see:详情请见:

Essentially you include AutoPublishAlias in your AWS::Serverless::Function definition:本质上,您在AWS::Serverless::Function定义中包含AutoPublishAlias

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

And then elsewhere in the CloudFormation template you can reference the latest published version as !Ref MyFunction.Version (yaml syntax).然后在 CloudFormation 模板的其他地方,您可以将最新发布的版本引用为!Ref MyFunction.Version (yaml 语法)。

AWS::Lambda::Version is not useful. AWS::Lambda::Version没有用。 You have to add a new resource for every Lambda version.您必须为每个 Lambda 版本添加一个新资源。 If you want to publish a new version for every Cloudformation update, you have to hack the system.如果你想为每个 Cloudformation 更新发布一个新版本,你必须破解系统。

I solved this issue creating a Lambda backed custom resource which is triggered for every deployment.我解决了这个问题,创建了一个 Lambda 支持的自定义资源,该资源为每个部署触发。 Inside this Lambda, I am creating a new version for the Lambda function given in parameter.在这个 Lambda 中,我正在为参数中给出的 Lambda 函数创建一个新版本。

For the Lambda's source you can check http://serverless-arch-eu-west-1.s3.amazonaws.com/serverless.zip对于 Lambda 的来源,您可以查看http://serverless-arch-eu-west-1.s3.amazonaws.com/serverless.zip

Here is the example Cloudformation using this Deployment Lambda function (You might need some modification):以下是使用此部署 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"
        }
      }
    }
  }
}

(Disclaimer: This code is a part of my book, for more information about Lambda & API Gateway you can check: https://www.amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/1787129195 ) (免责声明:此代码是我书中的一部分,有关 Lambda 和 API 网关的更多信息,您可以查看: https : //www.amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/1787129195

This post is out-of-date.这个帖子已经过时了。 I am updating it here so others can see the correct solution for versioning Lambdas as of 06-09-2020, without the need for extra custom versioning Lambdas.我在这里更新它,以便其他人可以看到截至 2020 年 6 月 9 日版本控制 Lambda 的正确解决方案,而无需额外的自定义版本控制 Lambda。

This:这个:

Description: Lambda Example
Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Code:
        ZipFile: |
          'Example Code';
      Runtime: nodejs12.x
      Timeout: 5

Becomes this:变成这样:

Description: Lambda Example
Transform: AWS::Serverless-2016-10-31
Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      AutoPublishAlias: live
      Handler: index.handler
      InlineCode: |
        'Example Code';
      Runtime: nodejs12.x
      Timeout: 5

The Transform: allows AWS::Serverless::Function inside of a CloudFormation template which in turn supports lambda versioning. Transform:允许在 CloudFormation 模板中使用AWS::Serverless::Function ,而后者又支持 lambda 版本控制。

Don't let the dated "Best Answer" above - built for that persons book - throw you down a rabbit hole like I did.不要让上面过时的“最佳答案” - 专为该人的书而设计 - 像我一样把你扔进兔子洞。

You're welcome.别客气。

The AWS::Lambda::Version resource only represents a single published Lambda function version- it will not automatically publish new versions on every update of your code. AWS::Lambda::Version资源仅代表一个已发布的 Lambda 函数版本 - 它不会在每次更新代码时自动发布新版本。 To accomplish this, you have two options:为此,您有两个选择:

1. Custom resource 1. 自定义资源

You can implement your own Custom Resource that calls PublishVersion on each update.您可以实现自己的自定义资源,在每次更新时调用PublishVersion

For this approach, you'll still need to change at least one Parameter every time you update your stack, in order to trigger an update on the Custom Resource that will trigger the PublishVersion action.对于这种方法,每次更新堆栈时,您仍然需要至少更改一个参数,以便触发自定义资源的更新,从而触发 PublishVersion 操作。 (You won't have to actually update the template, though.) (不过,您实际上不必更新模板。)

Here's a full, working example:这是一个完整的工作示例:

启动堆栈

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. Template preprocessor 2. 模板预处理器

You can use a template preprocessor like embedded Ruby (or just manually updating your template on each deploy) to publish a new Version on each update of your code by changing the AWS::Lambda::Version resource's Logical ID whenever your code is updated.您可以使用嵌入式 Ruby 之类的模板预处理器(或仅在每次部署时手动更新模板),通过在代码更新时更改AWS::Lambda::Version资源的逻辑 ID,在每次代码更新时发布新版本。

Example:例子:

# 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

To create/update the stack while passing template.yml through the erb template preprocessor, run:要在通过erb模板预处理器传递template.yml创建/更新堆栈,请运行:

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

Answer updated for February 2018 2018 年 2 月更新的答案

You can use AWS SAM (Serverless Application Model) , and its sam package and sam deploy commands to update Lambda .您可以使用AWS SAM(无服务器应用程序模型)及其sam packagesam deploy命令来更新 Lambda They are similar to aws cloudformation package and aws cloudformation deploy commands, but also let you update Lambda versions automatically.它们类似于aws cloudformation packageaws cloudformation deploy命令,但还允许您自动更新 Lambda 版本。

SAM can package your code (or take ZIP package you created otherwise), upload it to S3, and update the $LATEST Version of the Lambda from it. SAM 可以打包您的代码(或采用您以其他方式创建的 ZIP 包),将其上传到 S3,并从中更新 Lambda 的$LATEST版本。 (If this is all you need, this can also be done with aws cloudformation , without SAM; code examples are same as below, but only use CloudFormation 's standard declarations). (如果这就是您所需要的,也可以使用aws cloudformation完成,无需 SAM;代码示例与下面相同,但仅使用CloudFormation的标准声明)。 Then, with SAM, if configured accordingly, you can also automatically publish a Version and update an Alias to point to it.然后,使用 SAM,如果进行了相应的配置,您还可以自动发布版本并更新别名以指向它。 It can also, optionally, use AWS CodeDeploy to gradually move traffic from previous Version to new one, and rollback in case of errors.它还可以选择使用 AWS CodeDeploy 将流量从以前的版本逐渐转移到新版本,并在出现错误时回滚。 All this is explained in Safe Lambda deployments .所有这些都在安全 Lambda 部署中进行了解释。


Technically, the idea is that every time you update the stack, you need your AWS::Lambda::Function 's Code to point to the new package in S3.从技术上讲,这个想法是每次更新堆栈时,您都需要AWS::Lambda::FunctionCode来指向 S3 中的包。 This will ensure that when you update the stack, Lambda's $LATEST version will be updated from the new package.这将确保当您更新堆栈时,Lambda 的 $LATEST 版本将从新包中更新。 Then, you can also automate the publishing of new Version and switch an Alias to it.然后,您还可以自动发布新版本并为其切换别名。

For it, create a SAM template, which is similar to (a superset of) CloudFormation template.为此,创建一个 SAM 模板,它类似于 CloudFormation 模板的(超集)。 It may include SAM-specific declarations, like the one for AWS::Serverless::Function below.它可能包含特定于 SAM 的声明,例如下面针对AWS::Serverless::Function Point the Code to source code directory (or a prepackaged ZIP), and set the AutoPublishAlias property.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
...

Run:跑:

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

This packages source directory contents as a ZIP (if Code is not a ZIP already), uploads it to S3 under new autogenerated key, and generates final CloudFormation template to packaged.yaml , putting for you the proper Code reference into it;这将源目录内容打包为 ZIP(如果Code已经不是 ZIP),在新的自动生成密钥下将其上传到 S3,并生成最终 CloudFormation 模板到packaged.yaml ,为您提供正确的Code参考; like this:像这样:

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

Now you can use generated packaged.yaml with SAM, to create function Version:现在您可以使用生成的packaged.yaml和 SAM 来创建函数版本:

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

This will update Lambda's $LATEST version, and, if AutoPublishAlias was defined, publish it as a new Version and update the Alias to point to the newly published Version.这将更新 Lambda 的$LATEST版本,如果定义了AutoPublishAlias ,则将其发布为新版本并更新别名以指向新发布的版本。

See the examples in SAM GitHub repo for a complete template code.有关完整的模板代码,请参阅SAM GitHub 存储库中示例

Looking for a similar thing that works with Lambda functions deployed from S3.寻找与从 S3 部署的 Lambda 函数一起使用的类似内容。

My use case was this:我的用例是这样的:

  • You have a cloudformation template that creates a Lambda function from an S3 bucket location您有一个从 S3 存储桶位置创建 Lambda 函数的 cloudformation 模板
  • You need to update this function so you make code changes locally and push the changes to S3您需要更新此函数,以便在本地更改代码并将更改推送到 S3
  • You now want to push these changes to Lambda so you try to update the stack and cloudformation says there are no changes to update so you have to resort to manually updating the code using the AWS Lambda console.您现在想要将这些更改推送到 Lambda,因此您尝试更新堆栈,而 cloudformation 表示没有更改要更新,因此您必须使用 AWS Lambda 控制台手动更新代码。

Not happy with this I looked for an alternative and came across this question.对此不满意,我寻找替代方案并遇到了这个问题。 None of the answers exactly worked for me so I have taken some ideas and adapted the answers here and made my own version written in Python.没有一个答案完全适合我,所以我采取了一些想法并在此处调整了答案,并制作了我自己的 Python 版本。

This code is adapted from the answer from @wjordan so credit to him for the idea and the original answer.这段代码改编自@wjordan 的答案,因此感谢他的想法和原始答案。 The differences are:区别在于:

  • This is written in Python这是用 Python 编写的
  • It works with Lambda code deployed from an S3 bucket它适用于从 S3 存储桶部署的 Lambda 代码
  • It updates the code and publishes a new version它更新代码并发布新版本

You need a nonce parameter.您需要一个 nonce 参数。 You change the value of this parameter when the code needs to be republished to Lambda.当需要将代码重新发布到 Lambda 时,您可以更改此参数的值。 This is to ensure that cloudformation will update your custom resource.这是为了确保 cloudformation 将更新您的自定义资源。 When the custom resource is updated, it will run the Python code that ultimately updates your Lambda code.更新自定义资源时,它将运行最终更新您的 Lambda 代码的 Python 代码。

Hope this helps someone.希望这可以帮助某人。

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 

Unfortunately, this is not possible to do using CloudFormation.不幸的是,使用 CloudFormation 无法做到这一点。 You will need to add new AWS::Lambda::Version sections in your CloudFormation template for each version.您需要在 CloudFormation 模板中为每个版本添加新的AWS::Lambda::Version部分。

The closest solution would be to create .erb templates and have it generate CloudFormation templates with all the versions.最接近的解决方案是创建 .erb 模板并让它生成所有版本的 CloudFormation 模板。

  1. We can make a Lambda deployment package;我们可以制作一个Lambda部署包;
  2. Pass the Lambda package with the version as one of Cloud Formation parameters, eg "LambdaPakcageNameWithVersion";传递带有版本的 Lambda 包作为 Cloud Formation 参数之一,例如“LambdaPakcageNameWithVersion”;
  3. Use "LambdaPakcageNameWithVersion" as the Lambda code s3 key;使用“LambdaPakcageNameWithVersion”作为Lambda代码s3键;
  4. The new Lamdba package will be deployed when running the aws-cli command to update the cloudformation stack or running CI/CD pipeline.运行 aws-cli 命令以更新 cloudformation 堆栈或运行 CI/CD 管道时,将部署新的 Lamdba 包。

 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

This is a bit of a hack, and depends on using gitlab-ci (or something similar), but I find passing the commit hash into a cloudformation template (via the template's parameters) very useful.这有点小技巧,取决于使用 gitlab-ci(或类似的东西),但我发现将提交哈希传递到 cloudformation 模板(通过模板的参数)非常有用。

(It's a bit like @Jerry 's answer, but using the commit hash.) (这有点像 @Jerry 的答案,但使用提交哈希。)

In this case you could do something like:在这种情况下,您可以执行以下操作:

Have a parameter in your template for the commit hash, eg:在提交哈希的模板中有一个参数,例如:

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

You can then reference this in the lambda resource, like this:然后您可以在 lambda 资源中引用它,如下所示:

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

Your ci pipeline then needs to look something like (assuming you call your cloudformation template stack-template.yaml):您的 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

You can use this technique for triggering cfn-init on EC2 metadata as well..您也可以使用此技术在 EC2 元数据上触发 cfn-init。

Worked for me the following:为我工作如下:

"LambdaAlias": {
            "Type": "AWS::Lambda::Alias",
            "DeletionPolicy" : "Retain",
            "Properties": {
                "FunctionName": {
                    "Ref": "LambdaFunction"
                },
                "FunctionVersion": {
                    "Fn::GetAtt": ["LambdaVersion","Version"]
                },
                "Name": "MyAlias"
            }

I solved this using CI/CD, an ant script and the git revision to create a unique zip name in the S3 bucket for each commit.我使用 CI/CD、ant 脚本和 git 修订版解决了这个问题,以便为每次提交在 S3 存储桶中创建一个唯一的 zip 名称。

The ant script is invoked by the CI/CD to substitute the git revision into the name of the lambda code zip file and cloudformation template. ant 脚本由 CI/CD 调用以将 git 修订版替换为 lambda 代码 zip 文件和 cloudformation 模板的名称。 These references are made before copying the code and cloudformation scripts to S3.这些引用是在将代码和 cloudformation 脚本复制到 S3 之前进行的。 This is similar to the way SAM works but this works with plain old Cloudformation Stacks and importantly Stack Sets that might need be deployed across a number of accounts.这与 SAM 的工作方式类似,但它适用于普通的旧 Cloudformation Stacks 以及可能需要跨多个帐户部署的重要 Stack Sets。 At time of writing SAM was not compatible with CF Stack Sets在撰写本文时,SAM 与 CF 堆栈集不兼容

There are two files: the ant file and a properties file that tells the ant file which lambda source directories to zip.有两个文件:ant 文件和一个属性文件,它告诉 ant 文件要压缩哪些 lambda 源目录。

First the build.xml ant file:首先是 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>

The lambda.zip.build.properties file looks like this: 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/my-src-@git.revision@.zip
#              ^^ Directory    ^^ Zip File
#
###################################################################################################################################################################################
myproject/lambda/src=myproject/lambda/lambda-code-@git.revision@.zip
# place your key value pairs above here...

And then the CloudFormation Template:然后是 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/lambda-code-@git.revision@.zip
      Handler: autotag-costcentre.lambda_handler
      MemorySize: 128
      Runtime: python3.7
      Timeout: 10
      .... etc

The result is a zip file with a unique name lambda-code-0f993c3.zip and a Cloudformation template with S3Key referencing the unique name.结果是一个具有唯一名称lambda-code-0f993c3.zip的 zip 文件和一个 Cloudformation 模板,其中S3Key引用了该唯一名称。

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

Deploy the template from the S3 location and it will force the existing lambda code to be refreshed every time.从 S3 位置部署模板,它将强制每次刷新现有的 lambda 代码。

I do a checksum on the folder, and append this to S3Key of the lambda我对文件夹进行校验和,并将其附加到 lambda 的 S3Key

- lambda_src_version=$(find ${Lambda1} -type f -print0  | xargs -0 sha1sum | sha1sum )
- lambda_src_version =$(echo ${lambda_src_version//[[:blank:]]/})
- export S3Key="lambda_functions/${stage}/cloudformation-${lambda_src_version}.zip"
- zip - -r . -x '*.git*' | aws s3 cp - s3://${S3Bucket}/${S3Key}
- sam deploy --template-file cloudformation.yml --stack-name XXX --parameter-overrides Lambda1Bucket=${S3Bucket} Lambda1CodeZip="${S3Key}"

// cloudformation.yml snippet // cloudformation.yml 片段

Parameters:      
  Lambda1CodeZip:
    Type: String
  Lambda1Bucket:
    Type: String 

Type: 'AWS::Lambda::Function'
    Properties: 
      Code:
        S3Bucket: !Sub ${Lambda1Bucket}
        S3Key: !Sub ${Lambda1CodeZip}

I had a similar problem and solved it for my situation which is not applicable to some of the others mentioned.我有一个类似的问题,并针对我的情况解决了它,但不适用于提到的其他一些问题。 My Lambda code is in a zip file in a versioned bucket.我的 Lambda 代码位于版本化存储桶中的 zip 文件中。 To force a new version the function has to be replaced.要强制使用新版本,必须更换 function。 To force a function replacement, I force a function name change by incorporating the code object's version ID.为了强制替换 function,我通过合并代码对象的版本 ID 强制更改 function 名称。 Object Version IDs can have a period in them which is invalid in a function name, so any periods begin truncation of the object version ID. Object 版本 ID 中可以有一个句点,这在 function 名称中无效,因此任何句点都开始截断 object 版本 ID。

rLambdaVersion:
  Type: AWS::Lambda::Version
  Properties:
    FunctionName: !Ref rLambdaFunction
    ProvisionedConcurrencyConfig:
      ProvisionedConcurrentExecutions: !Ref pProvisionedConcurrency

rLambdaFunction:
  Type: 'AWS::Lambda::Function'
  Properties:
    #FunctionName incorporates the code object version id so that code updates cause function version updates
    #S3 object verion IDs can have a period in them which is invalid in a function name so any period is truncated
    FunctionName: !Sub
      - 'lambda-function-name-${CodeObjectVersionId}'
      - CodeObjectVersionId:
          Fn::Select: [0, !Split [".", !Ref pLambdaCodeS3ObjectVersion]]
    Code:
      S3Bucket: !Ref pS3Bucket
      S3Key: !Ref pLambdaCodeS3Key
      S3ObjectVersion: !Ref pLambdaCodeS3ObjectVersion

Another strategy to update AWS Lambda Version using CloudFormation only consists to do the following changes in your template:仅使用 CloudFormation 更新 AWS Lambda 版本的另一种策略包括在模板中进行以下更改:

  • update your Lambda Function更新你的 Lambda Function
  • add a new Lambda Version (keep the previous version if needed)添加新的 Lambda 版本(如果需要,保留以前的版本)
  • refers this version in an Lambda alias在 Lambda 别名中引用此版本
  • Update your CloudFormation template in AWS在 AWS 中更新您的 CloudFormation 模板

Example: template-stack.yml示例:模板堆栈.yml

# Your updated Lambda function
MyLambdaFunction
  Type: AWS::Lambda::Function
  Properties:
    FunctionName: myFunctionName
    ...

# Current version
MyLambdaVersionV1:
  Type: AWS::Lambda::Version
  Properties:
    Description: V1
    FunctionName: !Ref MyLambdaFunction  #Previously deployed - stay inchanged

# New version
MyLambdaVersionV1:
  Type: AWS::Lambda::Version
  Properties:
    Description: V2
    FunctionName: !Ref MyLambdaFunction #Nextly deployed - Will be created

# Your Lambda Alias
MyLambdaAlias:
  Type: AWS::Lambda::Alias
  Properties:
    FunctionName: !Ref MyLambdaFunction
    FunctionVersion: !Ref MyLambdaVersionV2.Version #Update the reference to the next version here
    Name: myAliasName

It's quite a strange way to work, but it allows updating your lambda function while creating a new version.这是一种非常奇怪的工作方式,但它允许在创建新版本时更新您的 lambda function。 Note that this also changes the so-called "Latest" version.请注意,这也会更改所谓的“最新”版本。

When you no longer need a version, you can remove it from the template and redeploy your stack again.当您不再需要某个版本时,您可以将其从模板中删除并再次重新部署您的堆栈。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何为使用 CloudFormation 模板创建的 AWS Lambda function 添加触发器? - How to add triggers for a AWS Lambda function created using a CloudFormation template? 使用 Cloudformation 创建触发器以启动 lambda function 并在我将文件上传到 S3 存储桶时验证哪个区域可用 - Create trigger using Cloudformation to launch a lambda function and verify in what region is available when I upload a file in S3 bucket 使用 CloudFormation 时,AWS Lambda function 缺少 websocket 网关触发 - AWS Lambda function is missing trigger by websocket gateway when using CloudFormation 如何使用 lambda 删除和重新创建 CDK 堆栈 (Cloudformation)? - How can I delete and recreate a CDK Stack (Cloudformation) using a lambda? 如何使用 AWS CloudFormation 创建 Amazon VPC? - How to create an Amazon VPC using AWS CloudFormation? 如何使用 AWS CLI 创建 AWS Lambda 函数? - How can I create an AWS Lambda function using the AWS CLI? 从 cloudformation 模板调用 lambda function - Invoke lambda function from a cloudformation template 使用一个 cloudFormation 模板从 zip 文件和 S3 存储桶创建一个 lambda - Create a lambda from zip file and and an S3 bucket using one cloudFormation Template 如何使用无服务器框架创建 API 的新版本? - How do you create a new version of an API using serverless framework? 在具有未知 ImageUri 属性的 CloudFormation 中创建 Lambda - Create Lambda in CloudFormation with unknown ImageUri property
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM