Sending response back to CFN custom resource from python Lambda function

CloudFormation uses a pre-signed S3 URL to receive the response back from the custom resources managed by it. There are few blue prints available for Node.js Lambda custom resources but nothing available for python yet. Hence I created this simple function which shall be used to send the response back to CFN custom resource by performing PUT request to the pre-signed S3 URL.

I’m using json module to form the response body, json module is available by default in the Lambda container. Whereas the requests module which I use to perform the http PUT request needs to be packaged along with the python script.

9 Comments

 Add your comment
  1. For posterity, here is working python that you do not have to include requests with the deployment package.

    import logging
    import boto3
    import json
    from botocore.vendored import requests

    client = boto3.client(‘cloudsearch’)
    s3Client = boto3.client(‘s3’)
    log = logging.getLogger()
    log.setLevel(logging.INFO)

    SUCCESS = “SUCCESS”
    FAILED = “FAILED”

    def lambda_handler(event, context):
    response = send(event, context, SUCCESS, {}, None)
    return {
    “Response” : response
    }

    def send(event, context, responseStatus, responseData, physicalResourceId):
    responseUrl = event[‘ResponseURL’]

    log.info(“Event: ” + str(event))
    log.info(“ResponseURL: ” + 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[‘Data’] = responseData

    json_responseBody = json.dumps(responseBody)

    log.info(“Response body: ” + str(json_responseBody))

    headers = {
    ‘content-type’ : ”,
    ‘content-length’ : str(len(json_responseBody))
    }

    try:
    response = requests.put(responseUrl,
    data=json_responseBody,
    headers=headers)
    log.info(“Status code: ” + str(response.reason))
    return SUCCESS
    except Exception as e:
    log.error(“send(..) failed executing requests.put(..): ” + str(e))
    return FAILED

  2. I’m getting error in cloudformation:-
    I added one demo custom resource in cloudfromation (that not gonna create any resource) just to test lambda function..
    CF console output.
    1) CREATE_IN_PROGRESS AWS::CloudFormation::Stack test2 User Initiated
    2) CREATE_IN_PROGRESS Custom::Demo Resource creation Initiated
    3) CREATE_COMPLETE Custom::Demo Demo
    4) ROLLBACK_IN_PROGRESS AWS::CloudFormation::Stack test2

    CustomResource attribute error: Vendor response doesn’t contain Status key in object arn:aws:cloudformation:us-east-1:263341548047:stack/test2/5241b380-81b5-11e6-892a-500c28b32ed2|NetworkInfo|06f8d508-75a3-460d-b811-ff6102223ef0 in S3 bucket cloudformation-custom-resource-storage-useast1. Rollback requested by user.
    Physical ID:arn:aws:cloudformation:us-east-1:263341548047:stack/test2/5241b380-81b5-11e6-892a-500c28b32ed2

  3. local variable ‘req’ referenced before assignment: UnboundLocalError
    Traceback (most recent call last):
    File “/var/task/main.py”, line 11, in lambda_handler
    sendResponse(event, context, responseStatus, responseData)
    File “/var/task/main.py”, line 29, in sendResponse
    print req.text
    UnboundLocalError: local variable ‘req’ referenced before assignment

    any Idea what is wrong ?

  4. Can this example be tested in the Lambda Function console?
    I am getting error:
    Traceback (most recent call last):
    File “/var/task/PythonCFN.py”, line 11, in lambda_handler
    sendResponse(event, context, responseStatus, responseData)
    File “/var/task/PythonCFN.py”, line 17, in sendResponse
    ‘StackId’: event[‘StackId’],
    KeyError: ‘StackId’

    what am i missing?

    • Hi,

      How did you configure your test event? Preferably select “CloudFormation create event” as the event source and update the parameters as used in the python script.

      You don’t have event[‘StackId’] parameter set in your test event hence it’s failing with that error.

    • TL;DR – Delete line 29

      For anyone else coming to this and copy pasting, there’s an error in the code

      Line 29: print req.text

      The variable req is only in scope in the try block.

Leave a Comment

Your email address will not be published.

1 Trackback

  1. AWS Week in Review – December 14, 2015 | SMACBUZZ (Pingback)