如何为在 Python 2.7/3.6/3.7 上运行的 AWS Lambda 函数更新 AWS CloudFormation cfn-response 模块?

上次更新日期:2021 年 2 月 16 日

我想为在 Python 2.7/3.6/3.7 上运行的 AWS Lambda 函数更新 AWS CloudFormation cfn-response 模块。

解决方法

注意:以下步骤仅适用于在 Python 2.7/3.6/3.7 上运行的 Lambda 函数。以下命令适用于 Linux 和 macOS 环境。Windows PowerShell 上的语法可能有所不同。

注意:如果您在运行 AWS 命令行界面 (AWS CLI) 命令时收到错误,请确保您运行的是最新版本的 AWS CLI。

1.要查找包含自定义资源的堆栈,请运行以下命令:

aws cloudformation list-stacks --region us-east-1 | grep -oE 'arn:+' | while read arn; do aws cloudformation list-stack-resources --stack-name $arn --region us-east-1 | grep -E '(Custom::)|(::CustomResource)' | awk '{print $2}' | while read resource; do if ]; then echo $arn; echo $resource; fi; done; done

您应会看到与以下示例输出类似的输出:

arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667
"ResourceType": "AWS::CloudFormation::CustomResource",

2.要查找与自定义资源关联的 Lambda 函数,请运行以下命令以从堆栈模板中检查自定义资源的 ServiceToken 属性:

aws cloudformation get-template --stack-name TestStack | jq -r .TemplateBody

注意:步骤 2 中的命令通过使用 jq选项(来自 jq 网站)格式化响应来预览堆栈模板。

您应会看到与以下示例输出类似的输出:

Resources:
  MyCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Name: "John"
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt MyRole.Arn
      Runtime: python3.7
      Code:
        ZipFile: |
          import cfnresponse
          def handler(event, context):
            responseData = {'Message': 'Hello {}!'.format(event)}
            cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
  MyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Outputs:
  Result:
    Value: !GetAtt MyCustomResource.Message

注意:从步骤 2 输出中获得的模板是 Lambda 支持的自定义资源的最小模板示例。属性 ServiceToken: !GetAtt MyFunction.Arn 位于 MyCustomResource 部分。ServiceToken 属性的 !GetAtt MyFunction.Arn 解析的值是 Amazon Simple Notification Service (Amazon SNS) 主题的 Amazon 资源名称 (ARN) 或 Lambda 函数。

3.在步骤 2 的模板中,确定 Lambda 函数的定义位置。

如果您的 Lambda 函数与自定义资源位于同一堆栈中,请跳至步骤 4。例如,步骤 2 中的 Fn::GetAtt 函数显示 Lambda 函数与自定义资源在同一模板中定义。

如果 ServiceToken 属性指向硬编码的 ARN,则 Lambda 函数可能位于另一个堆栈中。如果 ServiceToken 属性是通过 Fn::Import 解析的,则使用 AWS CloudFormation 中的 list-exports API 来查找值。例如:

aws cloudformation list-exports --region us-east-1
{
    "Exports": 
}

然后,通过使用 list-tags 找到 AWS CloudFormation 堆栈 ARN,检查位于单独堆栈中的函数标签。例如:

aws lambda list-tags --resource arn:aws:lambda:us-east-1:123456789012:function:TestStack-MyFunction-5ZE2CQO8RAA9 | grep stack-id

您会收到类似如下内容的输出:

"aws:cloudformation:stack-id": "arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667"

注意:您还可以在 AWS Lambda 控制台中找到函数标签。

4.要允许 AWS CloudFormation 在您的 Lambda 函数中加载最新的 cfn-response 模块,请更新 Lambda 函数的内联源代码。例如:

Code:
        ZipFile: |
          import cfnresponse
          def handler(event, context):
            responseData = {'Message': 'Hello {}!'.format(event)}
            cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")

注意:请参阅步骤 2 以获取具有 Lambda 函数(含内联源代码)的示例模板。

现在,AWS CloudFormation 会将以下 cfn-response 模块代码示例加载到您的 Lambda 函数中。例如:

from botocore.vendored import requests
import json
 
SUCCESS = "SUCCESS"
FAILED = "FAILED"
 
def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False):
    responseUrl = event
 
    print(responseUrl)
 
    responseBody = {}
    responseBody = responseStatus
    responseBody = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
    responseBody = physicalResourceId or context.log_stream_name
    responseBody = event
    responseBody = event
    responseBody = event
    responseBody = noEcho
    responseBody = responseData

注意:有关更多信息,请参阅 cfn-response 模块的“模块源代码”部分中的代码示例。

cfn-response 模块代码示例使用 Lambda 函数部署程序包中的 botocore.requests

要将 cfn-response 模块更新为使用 urllib3 的最新版本,请更新 AWS CloudFormation 模板中的函数内联代码。通过向内联 Lambda 函数的代码添加注释来实现此操作。例如:

ZipFile: |
           import cfnresponse
           def handler(event, context):
+            # This comment was added to force an update on this function's code
             responseData = {'Message': 'Hello {}!'.format(event)}
             cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
   MyRole:

5.保存对包含 Lambda 函数的模板所做的任何更改。

6.更新堆栈。

在堆栈更新完成后,cfn-response 模块将被修改。

注意:如果您的函数代码位于 Amazon Simple Storage Service (Amazon S3) 存储桶或 Amazon Elastic Container Registry (Amazon ECR) 映像中,则必须自行更新模块以包含具有 urllib3 的版本。要获取 cfn-response 模块的最新版本的源代码,请参阅 cfn-response 模块。

注意:如果新的 Python 或 JavaScript 运行时引入了重大更改,则必须更新 cfn-response 模块。无需再次更新 ZipFile,只要更新函数的运行时属性,即可自动附加最新版本的 cfn-response 模块。