Last updated: 2021-08-23
exports.handler = function(event, context) {
console.log("REQUEST RECEIVED:\n" + JSON.stringify(event));
...
}
MyCustomResource:
Type: "Custom::PingTester"
Properties:
ServiceToken:
!Sub |
arn: aws: lambda: us - east - 1: 111122223333: function: awsexamplelambdafunction
Received event: {
"RequestType": "Delete",
"ServiceToken": "arn:aws:lambda:us-east-1:111122223333:function:awsexamplelambdafunction",
"ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.us-east-1.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A111122223333%3Astack/awsexamplecloudformation/33ad60e0-5f25-11e9-a734-0aa6b80efab2%7CMyCustomResource%7Ce2fc8f5c-0391-4a65-a645-7c695646739?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170313T0212304Z&X-Amz-SignedHeaders=host&X-Amz-Expires=7200&X-Amz-Credential=QWERTYUIOLASDFGBHNZCV%2F20190415%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=dgvg36bh23mk44nj454bjb54689bg43r8v011uerehiubrjrug5689ghg94hb",
"StackId": "arn:aws:cloudformation:us-east-1:111122223333:stack/awsexamplecloudformation/33ad60e0-5f25-11e9-a734-0aa6b80efab2",
"RequestId": "e2fc8f5c-0391-4a65-a645-7c695646739",
"LogicalResourceId": "MyCustomResource",
"PhysicalResourceId": "test-MyCustomResource-1URTEVUHSKSKDFF",
"ResourceType": "Custom::PingTester"
$ curl - H 'Content-Type: ' '' - X PUT - d '{ "Status": "SUCCESS", "PhysicalResourceId": "test-CloudWatchtrigger-1URTEVUHSKSKDFF", "StackId": "arn:aws:cloudformation:us-east-1:111122223333:stack/awsexamplecloudformation/33ad60e0-5f25-11e9-a734-0aa6b80efab2 ", "RequestId": "e2fc8f5c-0391-4a65-a645-7c695646739", "LogicalResourceId": "CloudWatchtrigger" } ' ' https: //cloudformation-custom-resource-response-useast1.s3.us-east-1.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A111122223333%3Astack/awsexamplecloudformation/33ad60e0-5f25-11e9-a734-0aa6b80efab2%7CMyCustomResource%7Ce2fc8f5c-0391-4a65-a645-7c695646739?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170313T0212304Z&X-Amz-SignedHeaders=host&X-Amz-Expires=7200&X-Amz-Credential=QWERTYUIOLASDFGBHNZCV%2F20190415%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=dgvg36bh23mk44nj454bjb54689bg43r8v011uerehiubrjrug5689ghg94hb '
As noted in the CloudFormation documentation, CloudFormation expects your Lambda function to callback to it once it has completed its operation; CloudFormation will pause execution until this callback is received. The event sent to your Lambda function by CloudFormation contains the callback URL (ResponseURL) as shown in this example:
{
"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"]
}
}
If you are not able to import the cfnresponse module, which seems to be python2 code, you should be able to simulate the same with python3. Try adding something like this code in your lambda_handler function. Note that you there are required keys in the response that are detailed in this AWS documentation page (i.e. PhysicalResourceId, StackId, RequestId, LogicalResourceId that are straightforward to add, see the documentation).
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))
So to edit the code in your question:
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")
The req.status_code is giving 200 but the anycodings_aws-lambda cloudformation stack is again getting stuck anycodings_aws-lambda at CREATE_IN_PROGRESS. Still not sure how to anycodings_aws-lambda make this work. ,My python script to create SSM parameter anycodings_aws-lambda (whose path is: anycodings_aws-lambda /myapp/dev/test/application.properties) is:,The code runs successfully which means that anycodings_aws-lambda cloudformation template was able to invoke anycodings_aws-lambda the lambda & it's script and create the anycodings_aws-lambda SSM parameter but it hangs after anycodings_aws-lambda ssm_create() function in my script anycodings_aws-lambda completes. I don't know how to return a anycodings_aws-lambda status of "SUCCESS" from my script to the anycodings_aws-lambda cloudformation stack to prevent it from anycodings_aws-lambda hanging in CREATE_IN_PROGRESS state. ,How to detect a blank line when inputing information
I have a lambda that has a python script to anycodings_aws-lambda create an application.properties file in anycodings_aws-lambda parameter store. I have a cloudformation anycodings_aws-lambda template that invokes this lambda to create anycodings_aws-lambda application.properties. My cloudformation anycodings_aws-lambda template looks like:
{
"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"
}
}
}
}
My python script to create SSM parameter anycodings_aws-lambda (whose path is: anycodings_aws-lambda /myapp/dev/test/application.properties) is:
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()
EDIT1: I updated my code to:
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")
As noted in the CloudFormation anycodings_aws-lambda documentation, CloudFormation expects anycodings_aws-lambda your Lambda function to callback to it anycodings_aws-lambda once it has completed its operation; anycodings_aws-lambda CloudFormation will pause execution anycodings_aws-lambda until this callback is received. The anycodings_aws-lambda event sent to your Lambda function by anycodings_aws-lambda CloudFormation contains the callback URL anycodings_aws-lambda (ResponseURL) as shown in this example:
{
"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"]
}
}
If you are not able to import the anycodings_aws-lambda cfnresponse module, which seems to be anycodings_aws-lambda python2 code, you should be able to anycodings_aws-lambda simulate the same with python3. Try anycodings_aws-lambda adding something like this code in your anycodings_aws-lambda lambda_handler function. Note that you anycodings_aws-lambda there are required keys in the response anycodings_aws-lambda that are detailed in this AWS anycodings_aws-lambda documentation page (i.e. anycodings_aws-lambda PhysicalResourceId, StackId, RequestId, anycodings_aws-lambda LogicalResourceId that are anycodings_aws-lambda straightforward to add, see the anycodings_aws-lambda documentation).
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))
So to edit the code in your question:
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")