自定义资源(lambda)调用后,AWS CloudFormation事件挂在CREATE_IN_PROGRESS状态

时间:2019-10-16 11:48:02

标签: python aws-lambda amazon-cloudformation

我有一个带python脚本的lambda,可以在参数存储区中创建application.properties文件。我有一个cloudformation模板,该模板调用此lambda来创建application.properties。我的cloudformation模板如下:

{
   "Description": "Create SSM Parameter",
   "Resources": {
      "primerinvoke": {
         "Type": "AWS::CloudFormation::CustomResource",
         "Properties": {
            "Handler": "lambda_function.lambda_handler",
            "ServiceToken": "arn:aws:lambda:us-east-1:1234:function:test_lambda",
            "FunctionName": "test_lambda"
         }
      }
   }
}

我用于创建SSM参数(其路径为/myapp/dev/test/application.properties)的python脚本为:

import boto3
import os

region = os.environ['AWS_REGION']
client = boto3.client('ssm')
def ssm_create():
    response = client.put_parameter(Name='/myapp/'
                                    + os.environ['environment']
                                    + '/test/application.properties',
                                    Description='string',
                                    Value='APPLICATION_NAME=myapp',
                                    Type='SecureString', Overwrite=True)
    return response
def lambda_handler(event, context):
    PutParameterResult = ssm_create()

我按照AWS lambda: No module named 'cfnresponse'中的建议使用了import cfnresponse,但是导入无法正常工作,而且我不知道如何在lambda上外部安装cfnresponse库。

代码成功运行,这意味着cloudformation模板能够调用lambda及其脚本并创建SSM参数,但是在脚本中的ssm_create()函数完成后它挂起。我不知道如何从脚本中将状态“ SUCCESS”返回到cloudformation堆栈,以防止其挂起为CREATE_IN_PROGRESS状态。

任何帮助将不胜感激!谢谢。

EDIT1 :我将代码更新为:

responseStatus = 'SUCCESS'
responseBody={}
def sendResponse(event, context, responseStatus):
            responseBody = {'Status': responseStatus,
                            'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
                            'PhysicalResourceId': context.log_stream_name,
                            'StackId': event['StackId'],
                            'RequestId': event['RequestId'],
                            'LogicalResourceId': event['LogicalResourceId'],
                            }
            print 'RESPONSE BODY:n' + json.dumps(responseBody)
def lambda_handler(event, context):
                logger.info(event)
                test_ssm_create()
                try:
                    req = requests.put(event['ResponseURL'], data=json.dumps(sendResponse(event, context, responseStatus)))
                    if req.status_code != 200:
                        print req.text
                        raise Exception('Recieved non 200 response while sending response to CFN.')
                except requests.exceptions.RequestException as e:
                    print e
                    raise
                return
                print("COMPLETE")

req.status_code给出200,但是cloudformation堆栈再次陷入CREATE_IN_PROGRESS。仍然不确定如何进行这项工作。

1 个答案:

答案 0 :(得分:2)

如CloudFormation文档中所述,CloudFormation希望您的Lambda函数在完成操作后可以对其进行回调; CloudFormation将暂停执行,直到收到此回调为止。通过CloudFormation发送到Lambda函数的事件包含回调URL(ResponseURL),如以下示例所示:

{
   "RequestType" : "Create",
   "ResponseURL" : "http://pre-signed-S3-url-for-response",
   "StackId" : "arn:aws:cloudformation:us-west-2:123456789012:stack/stack-name/guid",
   "RequestId" : "unique id for this create request",
   "ResourceType" : "Custom::TestResource",
   "LogicalResourceId" : "MyTestResource",
   "ResourceProperties" : {
      "Name" : "Value",
      "List" : [ "1", "2", "3" ]
   }
}

如果您无法导入cfnresponse模块(似乎是python2代码),则应该可以使用python3进行模拟。尝试在lambda_handler函数中添加类似以下代码的内容。请注意,您在响应中有必需的键,这些键在this AWS documentation page中有详细说明(即,可以直接添加的PhysicalResourceId,StackId,RequestId,LogicalResourceId,请参见文档)。

import requests
import json
import uuid

status = {'Status': 'SUCCESS',
          'PhysicalResourceId': 'MyResource_' + str(uuid.uuid1()),
          'StackId': event['StackId'],
          'RequestId': event['RequestId'],
          'LogicalResourceId': event['LogicalResourceId']
         }
r = requests.put(event['ResponseURL'], data=json.dumps(status))

因此,要编辑问题中的代码:

responseStatus = 'SUCCESS'

def getResponse(event, context, responseStatus):
            responseBody = {'Status': responseStatus,
                            'PhysicalResourceId': context.log_stream_name,
                            'StackId': event['StackId'],
                            'RequestId': event['RequestId'],
                            'LogicalResourceId': event['LogicalResourceId'],
                            }
            responseBody = json.dumps(responseBody)
            print 'RESPONSE BODY:n' + responseBody

            return responseBody

def lambda_handler(event, context):
                logger.info(event)
                test_ssm_create()
                try:
                    req = requests.put(event['ResponseURL'], data=getResponse(event, context, responseStatus))
                    if req.status_code != 200:
                        print req.text
                        raise Exception('Received non 200 response while sending response to CFN.')
                except requests.exceptions.RequestException as e:
                    print e
                    raise
                return
                print("COMPLETE")