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

3 分钟阅读
0

我想为在 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 [[ -n $resource ]]; 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['ResourceProperties']['Name'])}
            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": [
        {
            "ExportingStackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/SomeOtherStack/481dc040-b283-11e9-b1bd-12d607a4fd1c",
            "Value": "arn:aws:lambda:us-east-1:123456789012:function:SomeOtherStack-MyFunction-5ZE2CQO8RAA9",
            "Name": "MyExport"
        }
    ]
}

然后,通过使用 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['ResourceProperties']['Name'])}
            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['ResponseURL']
 
    print(responseUrl)
 
    responseBody = {}
    responseBody['Status'] = responseStatus
    responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
    responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
    responseBody['StackId'] = event['StackId']
    responseBody['RequestId'] = event['RequestId']
    responseBody['LogicalResourceId'] = event['LogicalResourceId']
    responseBody['NoEcho'] = noEcho
    responseBody['Data'] = 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['ResourceProperties']['Name'])}
             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 模块。


AWS 官方
AWS 官方已更新 3 年前