Last updated on November 6th, 2024 at 08:27 am
In AWS CloudFormation there is a way to run Lambda function as target of a Custom Resource. To get started we need a Service token which is used to invoke Lambda function.
To trigger a Lambda function from CloudFormation, you can leverage a Custom Resource in AWS CloudFormation. This enables the inclusion of custom provisioning logic in your templates, allowing CloudFormation to execute it during stack operations. To proceed, acquire a Service token for invoking the Lambda function. This method proves valuable for provisioning workflows that cannot be expressed using CloudFormation’s built-in resource types.
To start lets go through the steps below
- Step 1 Create Custom Resource Definition
- Step 2 Specify Lambda Function as target
- Step 3 Get Lambda function response in CloudFormation Outputs
- Step 4 CloudFormtion output and Lambda logs in CloudWatch
Step 1 Create Custom Resource Definition
The service token represents Amazon Resource Name (ARN) of the function that AWS CloudFormation triggers when creating, updating, or deleting the stack, and allows for the inclusion of extra properties such as FunctionName, which AWS CloudFormation directly passes to your function.
Resources:
LambdaCustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !GetAtt 'RunMyLambda.Arn'
ParameterOne: "Print me when it is ready"
Step 2 Specify Lambda Function as target
Within your CloudFormation template, you have the option to designate a Lambda function as the intended recipient of a unique resource. By utilizing the ZipFile attribute to designate the function’s source code, you are able to incorporate the cfn-response module, enabling the dispatch of responses from your Lambda function to the specified custom resource.
Note: The
For Lambda code in S3 buckets, you must write your own functions to send responsescfn-response
module is available only when you use theZipFile
property to write your source code.
RunMyLambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import json
import cfnresponse
def lambda_handler(event, context):
print("log -- Event: %s " % json.dumps(event))
responseData = {'Message': 'Hello_From_Lambda'}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData,"CustomResourcePhysicalID")
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: python3.9
Timeout: '120'
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- ec2:DescribeImages
Resource: '*'
Explanation of the code above
1] Defined a Lambda function code using zipfile under resource named AWS::Lambda::Function
2] Inside the LambdaExecutionRole we provided permission to create a log group when the Lambda gets executed and put the logs inside that log group. This will help us verify the Lambda logs once the CloudFormation is executed successfully.
3] In the role I have also provided ec2:DescribeImages permission for Lambda. This is just to show that if your Lambda function need to call specific API’s you can add them there.
Step 3 Get Lambda function response in CloudFormation Outputs
In order to get the function response all you have to do is add this
Outputs:
MyOutput:
Description: Lambda Output
Value: !GetAtt LambdaCustomResource.Message
In this case LambdaCustomResource is the Service token we created initially in Step 1. The message is thrown from this line of code
responseData = {'Message': 'Hello_From_Lambda'}
You can change Message to any key as per your requirement.
Step 4 CloudFormtion output and Lambda logs in CloudWatch
Before we delve into the CloudFormation details, here is the complete YAML file used for this tutorial
AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS CloudFormation template for testing Lambda trigger.'
Resources:
LambdaCustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !GetAtt 'AMIInfoFunction.Arn'
ParameterOne: "Print me when it is ready"
AMIInfoFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import json
import cfnresponse
def lambda_handler(event, context):
print("log -- Event: %s " % json.dumps(event))
responseData = {'Message': 'Hello_From_Lambda'}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData,"CustomResourcePhysicalID")
Handler: "index.lambda_handler"
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: python3.9
Timeout: '120'
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- ec2:DescribeImages
Resource: '*'
Outputs:
MyOutput:
Description: Lambda Output
Value: !GetAtt LambdaCustomResource.Message
After running the CloudFormation template, you will receive the following output
Once you see the above CloudFormation output, the next step is to navigate to CloudWatch where the Lambda logs reside.
As you can see we have a ResponseURL section with presigned S3 bucket that receives responses from the custom resource provider to AWS CloudFormation, more details can be found here. It also have the parameter we passed as part of the ServiceToken.
The above screenshot has the final response from our Lambda function that got pushed to CloudFormation as its output.