Use Case

If you include execution of a Lambda in your CloudFormation template and you update your Lambda Code, CloudFormation will not execute your Custom Resource again unless it detects that the template or parameters have been changed.

Workaround - Include a Parameter

A very easy way to execute the Custom Resource every time when your Lambda changed is to add a parameter to your Custom Resource (eg. a timestamp, which will always be different) - if this parameter changes, the custom resources will automatically be executed again.

Parameters:
  LambdaExecutionTimestamp:
    Type: String
    Default: 201906010800

Resources:
   ExecuteLambda:
    Type: 'Custom::ExecuteLambda'
    Properties:
      ServiceToken: !GetAtt
        - Lambda
        - Arn
      Timestamp: !Ref LambdaExecutionTimestamp

If you are creating custom resources with lambda, you will also need to notify cloudformation if your script has successfully done executing. The python module cfnresponse helps you in giving a response back to Cloudformation about successful execution of the script.

Consider the following python code for your reference:

import boto3, os, botocore, cfnresponse
from botocore.exceptions import ClientError

# USE S3 SERVICE
s3 = boto3.client('s3')

# SET LOGGER
log = logging.getLogger()
log.setLevel(logging.INFO)


def lambda_handler(event, context):
    s3_bucket = os.environ['s3_bucket']
    check_bucket(s3_bucket, event, context)


# VERIFY IF BUCKET EXISTS
def check_bucket(bucket, event, context):
    try:
        s3.head_bucket(Bucket=bucket)
        log.info("Bucket Exists!")
        responseData = {'Success': 'Bucket Exists!'}
        cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
        return True
    except ClientError as e:
        error_code = int(e.response['Error']['Code'])
        if error_code == 403:
            log.info("Private Bucket. Forbidden Access!")
            return True
        elif error_code == 404:
            log.info("Bucket Does Not Exist!")
            responseData = {'Failed': 'Bucket Does not exists!'}
            cfnresponse.send(event, context, cfnresponse.FAILED, responseData)

The cfnresponse module is natively supported for python2 on AWS lambda. In case if you are using python3 on AWS Lambda, you will need to package cfnresponse module along with its dependencies. After packaging you can add your custom python script into the package and upload it to lambda to get it executed.

Run the following commands to create a cfnresponse package in linux:

1. pip install cfnresponse
2. pip install --target ./package cfnresponse
3. cd package
4. zip -r9 ../package.zip .

Now add your custom lambda script to the package.zip file

5. zip -g package.zip your-python-file.py


Hope this gives you the idea of how to create custom lambda callouts and also how to properly respond to cloudformation about the successful execution of your lambda scripts.