如何使用CloudFormation创建新版本的Lambda函数?

bor*_*ris 34 amazon-web-services aws-cloudformation aws-lambda

我正在尝试使用CloudFormation创建一个新版本的Lambda函数.

我想拥有相同Lambda函数的多个版本,以便我可以(a)指向不同版本的别名 - 比如DEV和PROD - 以及(b)能够回滚到早期版本

这是我的Lambda版本的定义:

LambdaVersion:
  Type: AWS::Lambda::Version
  Properties:
    FunctionName:
      Ref: LambdaFunction
Run Code Online (Sandbox Code Playgroud)

运行"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/17 亚马逊支持的官方回复:"...对于要发布的任何新版本,您需要定义一个添加(原文如此) AWS :: Lambda :: Version资源......"

AWS CloudFormation/Lambda团队,如果您正在阅读此内容 - 这是不可接受的.修理它.

Rob*_*ers 21

我有一个类似的用例(需要使用CloudFormation来管理在CloudFront中使用@edge的lambda函数,对于该函数,总是需要特定的lambda函数版本而不是$LATEST)并且我的搜索首先让我得到这个问题,但是更多挖掘我很高兴地发现现在支持自动lambda版本化,具有AutoPublishAliasAWS无服务器应用程序模型的新功能(基本上是CloudFormation模板的一组可选的更高级别构造).

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

详情见:

基本上你AutoPublishAlias在你的AWS::Serverless::Function定义中包括:

MyFunction:
  Type: "AWS::Serverless::Function"
  Properties:
    # ...
    AutoPublishAlias: MyAlias
Run Code Online (Sandbox Code Playgroud)

然后在CloudFormation模板的其他位置,您可以将最新发布的版本作为!Ref MyFunction.Version(yaml语法)引用.

  • 虽然这似乎是一个很好的答案,但应该注意答案依赖于 AWS SAM。请注意,函数类型是 AWS::Serverless::Function。没有为我工作。 (7认同)
  • 这很简单,应该是新批准的答案。谢谢 (2认同)

Çağ*_*ürk 18

AWS :: Lambda :: Version无用.您必须为每个Lambda版本添加新资源.如果要为每个Cloudformation更新发布新版本,则必须破解系统.

我解决了这个问题,创建了一个为每个部署触发的Lambda支持的自定义资源.在这个Lambda中,我正在为参数中给出的Lambda函数创建一个新版本.

对于Lambda的源代码,您可以查看http://serverless-arch-eu-west-1.s3.amazonaws.com/serverless.zip

以下是使用此部署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"
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

(免责声明:此代码是我的书的一部分,有关Lambda和API Gateway的更多信息,请查看:https://www.amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/1787129195)

  • 谢谢!这太疯狂了。我通过我们的企业支持计划向 AWS 提出支持请求,如果他们确认 AWS::Lambda::Version 没有用,我会尝试并接受此答案。 (2认同)
  • @boris您好,鲍里斯(Boris),如果此功能自实施以来是否有任何更新? (2认同)
  • @GrzegorzOledzki我已经编辑了代码以弄清楚。这是实际的Lambda代码。 (2认同)

小智 15

这个帖子已经过时了。我在这里更新它,以便其他人可以看到截至 2020 年 6 月 9 日版本控制 Lambda 的正确解决方案,而无需额外的自定义版本控制 Lambda。

这个:

Description: Lambda Example
Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Code:
        ZipFile: |
          'Example Code';
      Runtime: nodejs12.x
      Timeout: 5
Run Code Online (Sandbox Code Playgroud)

变成这样:

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
Run Code Online (Sandbox Code Playgroud)

Transform:允许AWS::Serverless::Function这反过来又支持拉姆达版本一个CloudFormation模板内。

不要让上面过时的“最佳答案” - 专为该人的书而设计 - 像我一样把你扔进兔子洞。

别客气。


wjo*_*dan 10

AWS::Lambda::Version资源仅代表单个已发布的Lambda函数版本 - 它不会在每次更新代码时自动发布新版本.要实现此目的,您有两种选择:

1.自定义资源

您可以实现自己的自定义资源,以调用PublishVersion每个更新.

对于此方法,每次更新堆栈时仍需要更改至少一个参数,以便触发将触发PublishVersion操作的自定义资源的更新.(不过,您不必实际更新模板.)

这是一个完整的,有效的例子:

启动堆栈

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
Run Code Online (Sandbox Code Playgroud)

2.模板预处理器

您可以使用模板预处理器(如嵌入式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
Run Code Online (Sandbox Code Playgroud)

要在template.yml通过erb模板预处理器时创建/更新堆栈,请运行:

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
Run Code Online (Sandbox Code Playgroud)


Tim*_*mur 7

答案更新于2018年2月

您可以使用AWS SAM(无服务器应用程序模型),它sam packagesam deploy命令来更新LAMBDA.它们aws cloudformation packageaws cloudformation deploy命令类似,但也允许您自动更新Lambda版本.

SAM可以打包您的代码(或者使用您创建的ZIP包),将其上传到S3,然后$LATEST从中更新Lambda 的版本.(如果这就是你所需要的,也可以在aws cloudformation没有SAM的情况下完成;代码示例与下面相同,但仅使用CloudFormation标准声明).然后,使用SAM,如果进行了相应配置,您还可以自动发布版本并更新别名以指向它.它还可以选择使用AWS CodeDeploy逐渐将流量从先前版本移动到新版本,并在出现错误时回滚.所有这些都在Safe Lambda部署中进行了解释.


从技术上说,这个想法是,每次更新堆栈,你需要你AWS::Lambda::FunctionCode指向的S3包.这将确保在更新堆栈时,Lambda的$ LATEST版本将从新包中更新.然后,您还可以自动发布新版本并将Alias切换到该版本.

为此,创建一个SAM模板,类似于CloudFormation模板的(超集).它可能包括特定于SAM的声明,如AWS::Serverless::Function下面的声明.指向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 Code Online (Sandbox Code Playgroud)

跑:

$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket my-bucket
Run Code Online (Sandbox Code Playgroud)

这将源目录内容打包为ZIP(如果Code不是ZIP),在新的自动生成密钥下将其上传到S3,并生成最终的CloudFormation模板packaged.yaml,为您提供正确的Code引用; 像这样:

...
MyFunction:
    Properties:
      Code:
        S3Bucket: my-bucket
        S3Key: ddeeaacc44ddee33ddaaee223344
...
Run Code Online (Sandbox Code Playgroud)

现在您可以使用packaged.yamlSAM 生成,来创建函数版本:

sam deploy --template-file packaged.yaml --stack-name my-stack [--capabilities ...]
Run Code Online (Sandbox Code Playgroud)

这将更新Lambda的$LATEST版本,如果AutoPublishAlias已定义,则将其作为新版本发布,并更新Alias以指向新发布的版本.

有关完整的模板代码,请参阅SAM GitHub仓库中示例.


Car*_*rlR 5

正在寻找与从 S3 部署的 Lambda 函数配合使用的类似功能。

我的用例是这样的:

  • 您有一个 cloudformation 模板,可从 S3 存储桶位置创建 Lambda 函数
  • 您需要更新此函数,以便在本地进行代码更改并将更改推送到 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 
Run Code Online (Sandbox Code Playgroud)