How do I update the AWS CloudFormation cfn-response module for AWS Lambda functions running on Python 2.7/3.6/3.7?
Last updated: 2021-02-16
I want to update the AWS CloudFormation cfn-response module for AWS Lambda functions running on Python 2.7/3.6/3.7.
Resolution
Note: The following steps are applicable only to Lambda functions running on Python 2.7/3.6/3.7. The following commands are applicable to Linux and macOS environments. Syntax may vary on Windows PowerShell.
Note: If you receive errors when running AWS Command Line Interface (AWS CLI) commands, make sure that you’re using the most recent AWS CLI version.
1. To find the stacks that contain custom resources, run the following command:
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
You should see output similar to the following example output:
arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667
"ResourceType": "AWS::CloudFormation::CustomResource",
2. To find the Lambda function associated with the custom resource, run the following command to check the custom resource’s ServiceToken property from the stack’s template:
aws cloudformation get-template --stack-name TestStack | jq -r .TemplateBody
Note: The command in step 2 previews the stack’s template by using the jq option (from the jq website) to format the response.
You should see output similar to the following example output:
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
Note: The template that you get from the output for step 2 is an example of a minimal template for a Lambda-backed custom resource. The property ServiceToken: !GetAtt MyFunction.Arn is in the MyCustomResource section. The value resolved by the !GetAtt MyFunction.Arn of the ServiceToken property is either the Amazon Resource Name (ARN) of the Amazon Simple Notification Service (Amazon SNS) topic or the Lambda function.
3. In the template from step 2, identify where your Lambda function is defined.
If your Lambda function is in the same stack as the custom resource, skip to step 4. For example, the Fn::GetAtt function in step 2 shows that the Lambda function is defined in the same template as the custom resource.
If the ServiceToken property points to a hardcoded ARN, then the Lambda function could be in another stack. If the ServiceToken property is resolved through Fn::Import, then use the list-exports API in AWS CloudFormation to look up the value. For example:
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"
}
]
}
Then, check for function tags located in a separate stack by using list-tags to locate the AWS CloudFormation stack ARN. For example:
aws lambda list-tags --resource arn:aws:lambda:us-east-1:123456789012:function:TestStack-MyFunction-5ZE2CQO8RAA9 | grep stack-id
You receive output similar to the following:
"aws:cloudformation:stack-id": "arn:aws:cloudformation:us-east-1:123456789012:stack/TestStack/3497b950-55f1-11eb-aad4-124a026c8667"
Note: You can also find function tags in the AWS Lambda console.
4. To allow AWS CloudFormation to load the latest cfn-response module in your Lambda function, update the inline source code of your Lambda function. For example:
Code:
ZipFile: |
import cfnresponse
def handler(event, context):
responseData = {'Message': 'Hello {}!'.format(event['ResourceProperties']['Name'])}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
Note: See step 2 for an example template that has a Lambda function with inline source code.
Now, the following cfn-response module code example is loaded by AWS CloudFormation into your Lambda function. For example:
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
Note: For more information, see the code examples in the "Module source code" section of the cfn-response module.
The cfn-response module code example uses botocore.requests in the Lambda function’s deployment package.
To update the cfn-response module to the latest version that uses urllib3, update the function’s inline code in the AWS CloudFormation template. Do this by adding a comment to the inline Lambda function's code. For example:
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. Save any changes to the template that contains your Lambda function.
The cfn-response module is modified after the stack is finished updating.
Note: If your function's code resides in an Amazon Simple Storage Service (Amazon S3) bucket or Amazon Elastic Container Registry (Amazon ECR) image, you must update the module yourself to include the version with urllib3. To get the source code for the latest version of the cfn-response module, see cfn-response module.
Note: If a new Python or JavaScript runtime introduces a breaking change, you must update the cfn-response module. Instead of updating the ZipFile again, you can automatically attach the newest version of the cfn-response module whenever a function’s Runtime property is updated.
Did this article help?
Do you need billing or technical support?