Configuration to check behavior of CloudFormation Custom resources
One of the features of CloudFormation is custom resources.
Custom resources enable you to write custom provisioning logic in templates that AWS CloudFormation runs anytime you create, update (if you changed the custom resource), or delete stacks.
Custom resources
In this article, we will check the behavior of Lambda-backed custom resources.
Environment
Create CloudFormation stacks and configure a Lambda function in it as a custom resource.
Configure function to log to CloudWatch Logs each time the function is executed.
The runtime for the function is Python 3.8.
CloudFormation template files
The above configuration is built using CloudFormation.
The CloudFormation template is located at the following URL
https://github.com/awstut-an-r/awstut-fa/tree/main/043
Explanation of key points of template files
Lambda functions for custom resources
Check the Lambda function that works as a backend for custom resources.
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import cfnresponse
import datetime
response_data = {}
def lambda_handler(event, context):
try:
now = datetime.datetime.now()
message = '{datetime} Request Type: {request_type}, Test Resource Property: {property}'.format(
datetime=now,
request_type=event['RequestType'],
property=event['ResourceProperties']['TestProperty'])
print(message)
response_data['message'] = message
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
FunctionName: !Sub "${Prefix}-function"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)
Basically the same as a Lambda function.
Code and ZipFile properties allow inline definition of the code to be executed by the function.
For more information on the basics of Lambda functions, please refer to the following page
For Python, a module (cfnresponse) is available for creating Lambda-backed custom resources.
This module will be used in this case.
You can check the operation details of CloudFormation.
The content of the operation can be obtained by event[‘RequestType’].
By using this value as a reference, you can implement a process that is limited to specific operations in the CloudFormation stack.
For example, the following image
if event['RequestType'] == 'Create':
pass
elif event['RequestType'] == 'Update':
pass
elif event['RequestType'] == 'Delete':
pass
Code language: Python (python)
When creating a custom resource, certain arguments can be passed to the Lambda function.
Arguments can be defined in the form of properties of the custom resource.
You can get them in the form event[‘ResourceProperties’][].
This is checked again on the custom resource side.
The function must give a given response at the end of the process.
The custom resource provider processes the AWS CloudFormation request and returns a response of SUCCESS or FAILED to the pre-signed URL.
Custom resources
This process can be implemented using the send function of the cfnresponse module described earlier.
The response can be passed as the third argument to the send function, in this case “cfnresponse.SUCCESS” or “cfnresponse.FAILED” in this code.
For more information on cfnresponse, please refer to the following page
The result of the work performed by the function can be passed to the CloudFormation stack side as some kind of return value.
The return value is set as an argument to the send function described above.
Specifically, it is passed as the fourth argument, “response_data” in this code.
Based on the above, the following is a summary of the process to be executed by this function.
- Create a string (message) based on the content and arguments of the CloudFormation operation and the current date and time
- Set the message as the return value when sending a response (SUCCESS) with cfnresponse.send
- If the above process fails, send a response (FAILED) in cfnresponse.send in the form of exception handling.
Note that, for the use of cfnresponse, the return value is required even when sending FAILED.
So it is set to pass an empty dictionary.
Custom Resources
Check custom resources.
Resources:
CustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !Ref FunctionArn
TestProperty: !Ref Prefix
#TestProperty: !Sub "${Prefix}-Updated"
Outputs:
CustomResourceReturnValue:
Value: !GetAtt CustomResource.message
Code language: YAML (yaml)
The Type property of a custom resource can be defined by the user according to a naming convention.
Use the AWS::CloudFormation::CustomResource or Custom::MyCustomResourceTypeName resource type to define custom resources in your templates.
Custom resources
The ServiceToken property sets the ARN of the resource to run on the backend.
In this case, the aforementioned Lambda function will run, so set the ARN for this function.
TestProperty is an argument passed to the aforementioned function.
This time, set this property to the string “fa-043”.
To check the return value of the custom resource, retrieve the return value in the Outputs section.
Use the built-in function Fn::GetAtt to reference the value (message) set in the function through the custom resource.
Architecting
Behavior when creating CloudFormation stacks
Create a CloudFormation stack from the AWS CLI.
After placing the template file in the specified S3 bucket, execute the following command
$ aws cloudformation create-stack \
--stack-name [stack-name] \
--template-url https://[bacuket-name].s3.[region-name].amazonaws.com/fa-043.yaml
--capabilities CAPABILITY_IAM
Code language: Bash (bash)
For more information on creating a stack, please see the following page
The following is the Lambda function log generated during stack creation.
The “Request Type: Create” indicates that the function was executed when the stack was created.
Also, “Test Resource Property: fa-043” indicates that the function is able to reference the value of a custom resource property.
We also check the return value.
In the Outputs of the stack, we see a string similar to the one in the log we just checked.
You can also get the return value from the custom resource in this way.
Behavior when updating custom resources
Next, update the description of the custom resource in the template file and execute the changes.
First, update the custom resource as follows
Resources:
CustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !Ref FunctionArn
#TestProperty: !Ref Prefix
TestProperty: !Sub "${Prefix}-Updated"
Code language: YAML (yaml)
The value of TestProperty has been updated.
After re-uploading this template file to the S3 bucket, run the following command
$ aws cloudformation update-stack \
--stack-name [stack-name] \
--template-url https://[bucket-name].s3.[region-name].amazonaws.com/fa-043.yaml \
--capabilities CAPABILITY_IAM
Code language: Bash (bash)
The following log is generated during a stack change.
In this case, “Request Type: Update” indicates that a function was executed at the time of the stack change.
Also, “Test Resource Property: fa-043-Updated” indicates that the function was able to reference the value of the custom resource property after the change.
Behavior when deleting CloudFormation stacks
Delete the CloudFormation stack from the AWS CLI
$ aws cloudformation delete-stack \
--stack-name fa-043
Code language: Bash (bash)
The following log is generated when deleting the stack.
This time, “Request Type: Delete” indicates that the function was executed when deleting the stack.
Summary
We have confirmed the behavior of Lambda-backed custom resources.
By referencing the value of RequestType, we found that we can implement processing tailored to specific operations (stack creation, deletion, etc.).