Select your cookie preferences

We use essential cookies and similar tools that are necessary to provide our site and services. We use performance cookies to collect anonymous statistics, so we can understand how customers use our site and make improvements. Essential cookies cannot be deactivated, but you can choose “Customize” or “Decline” to decline performance cookies.

If you agree, AWS and approved third parties will also use cookies to provide useful site features, remember your preferences, and display relevant content, including relevant advertising. To accept or decline all non-essential cookies, choose “Accept” or “Decline.” To make more detailed choices, choose “Customize.”

Walkthrough: Create a delay mechanism with a Lambda-backed custom resource

Focus mode
Walkthrough: Create a delay mechanism with a Lambda-backed custom resource - AWS CloudFormation
Filter View

This walkthrough show you the resource configuration of a Lambda-backed custom resource using a sample template, and how to launch a Lambda-backed custom resource using the same sample template. The sample custom resource template creates a Lambda-backed custom resource that creates a delay mechanism.

Note

Be aware of the following:

CloudFormation is a free service; however, you are charged for the AWS resources, such as the Lambda function and EC2 instance, that you include in your stacks at the current rate for each. For more information about AWS pricing, see the detail page for each product at http://aws.amazon.com.

A Lambda-backed custom resource used to be the recommended method for retrieving AMI IDs. Rather than creating a custom resource and Lambda function to retried AMI IDs, you can use AWS Systems Manager parameters in your template to retrieve the latest AMI ID value stored in a Systems Manager parameter. This makes your templates more reusable and easier to maintain. For more information, see Specify existing resources at runtime with CloudFormation-supplied parameter types.

Overview

The following steps provide an overview of this implementation.

  1. Save a sample template containing the Lambda function code as a file on your machine with the name samplelambdabackedcustomresource.template.

  2. Use the sample template to create your stack with a custom resource, a Lambda function, and an IAM role that uses Lambda to write logs to CloudWatch.

  3. The custom resource implements a delay mechanism. The Lambda function sleeps for the specified duration during create and update operations. The resource will only be invoked for an update operation if the properties are modified.

  4. The template Outputs exports two values, TimeWaited and the WaiterId. TimeWaited is the length of time the resource waited, and WaiterId is the unique ID generated during the execution.

Sample template

You can see the Lambda-backed custom resource sample template with the delay mechanism below:

JSON

{ "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "LambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "Policies": [ { "PolicyName": "AllowLogs", "PolicyDocument": { "Statement": [ { "Effect": "Allow", "Action": [ "logs:*" ], "Resource": "*" } ] } } ] } }, "CFNWaiter": { "Type": "AWS::Lambda::Function", "Properties": { "Handler": "index.handler", "Runtime": "python3.9", "Timeout": 900, "Role": { "Fn::GetAtt": [ "LambdaExecutionRole", "Arn" ] }, "Code": { "ZipFile": { "Fn::Sub": "from time import sleep\nimport json\nimport cfnresponse\nimport uuid\n\ndef handler(event, context):\n wait_seconds = 0\n id = str(uuid.uuid1())\n if event[\"RequestType\"] in [\"Create\", \"Update\"]:\n wait_seconds = int(event[\"ResourceProperties\"].get(\"WaitSeconds\", 0))\n sleep(wait_seconds)\n response = {\n \"TimeWaited\": wait_seconds,\n \"Id\": id \n }\n cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)" } } } }, "CFNWaiterCustomResource": { "Type": "AWS::CloudFormation::CustomResource", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "CFNWaiter", "Arn" ] }, "WaitSeconds": 60 } } }, "Outputs": { "TimeWaited": { "Value": { "Fn::GetAtt": [ "CFNWaiterCustomResource", "TimeWaited" ] }, "Export": { "Name": "TimeWaited" } }, "WaiterId": { "Value": { "Fn::GetAtt": [ "CFNWaiterCustomResource", "Id" ] }, "Export": { "Name": "WaiterId" } } } }

YAML

AWSTemplateFormatVersion: "2010-09-09" Resources: LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" Policies: - PolicyName: "AllowLogs" PolicyDocument: Statement: - Effect: "Allow" Action: - "logs:*" Resource: "*" CFNWaiter: Type: AWS::Lambda::Function Properties: Handler: index.handler Runtime: python3.9 Timeout: 900 Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: !Sub | from time import sleep import json import cfnresponse import uuid def handler(event, context): wait_seconds = 0 id = str(uuid.uuid1()) if event["RequestType"] in ["Create", "Update"]: wait_seconds = int(event["ResourceProperties"].get("WaitSeconds", 0)) sleep(wait_seconds) response = { "TimeWaited": wait_seconds, "Id": id } cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id) CFNWaiterCustomResource: Type: "AWS::CloudFormation::CustomResource" Properties: ServiceToken: !GetAtt CFNWaiter.Arn WaitSeconds: 60 Outputs: TimeWaited: Value: !GetAtt CFNWaiterCustomResource.TimeWaited Export: Name: TimeWaited WaiterId: Value: !GetAtt CFNWaiterCustomResource.Id Export: Name: WaiterId

Sample template walkthrough

The following snippets explain relevant parts of the sample template to help you understand how the Lambda function is associated with a custom resource and understand the output.

AWS::Lambda::Function resource CFNWaiter

The AWS::Lambda::Function resource specifies the function's source code, handler name, runtime environment, and execution role Amazon Resource Name (ARN).

The Handler property is set to index.handler since it uses a Python source code. For more information on accepted handler identifiers when using inline function source codes, see AWS::Lambda::Function Code.

The Runtime is specified as python3.9 since the source file is a Python code.

The Timeout is set to 900 seconds.

The Role property uses the Fn::GetAtt function to get the ARN of the LambdaExecutionRole execution role that's declared in the AWS::IAM::Role resource in the template.

The Code property defines the function code inline using a Python function. The Python function in the sample template does the following:

  • Create a unique ID using the UUID

  • Check if the request is a create or update request

  • Sleep for the duration specified for WaitSeconds during Create or Update requests

  • Return the wait time and unique ID

JSON

... "CFNWaiter": { "Type": "AWS::Lambda::Function", "Properties": { "Handler": "index.handler", "Runtime": "python3.9", "Timeout": 900, "Role": { "Fn::GetAtt": [ "LambdaExecutionRole", "Arn" ] }, "Code": { "ZipFile": { "Fn::Sub": "from time import sleep\nimport json\nimport cfnresponse\nimport uuid\n\ndef handler(event, context):\n wait_seconds = 0\n id = str(uuid.uuid1())\n if event[\"RequestType\"] in [\"Create\", \"Update\"]:\n wait_seconds = int(event[\"ResourceProperties\"].get(\"WaitSeconds\", 0))\n sleep(wait_seconds)\n response = {\n \"TimeWaited\": wait_seconds,\n \"Id\": id \n }\n cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)" } } } }, ...

YAML

... CFNWaiter: Type: AWS::Lambda::Function Properties: Handler: index.handler Runtime: python3.9 Timeout: 900 Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: !Sub | from time import sleep import json import cfnresponse import uuid def handler(event, context): wait_seconds = 0 id = str(uuid.uuid1()) if event["RequestType"] in ["Create", "Update"]: wait_seconds = int(event["ResourceProperties"].get("WaitSeconds", 0)) sleep(wait_seconds) response = { "TimeWaited": wait_seconds, "Id": id } cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id) ...
AWS::IAM::Role resource LambdaExecutionRole

The AWS::IAM:Role resource creates an execution role for the Lambda function, which includes an assume role policy which allows Lambda to use it. It also contains a policy allowing CloudWatch Logs access.

JSON

... "LambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "Policies": [ { "PolicyName": "AllowLogs", "PolicyDocument": { "Statement": [ { "Effect": "Allow", "Action": [ "logs:*" ], "Resource": "*" } ] } } ] } }, ...

YAML

... LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" Policies: - PolicyName: "AllowLogs" PolicyDocument: Statement: - Effect: "Allow" Action: - "logs:*" Resource: "*" ...
AWS::CloudFormation::CustomResource resource CFNWaiterCustomResource

The custom resource links to the Lambda function with its ARN using !GetAtt CFNWaiter.Arn. It will implement a 60 second wait time for create and update operations, as set in WaitSeconds. The resource will only be invoked for an update operation if the properties are modified.

JSON

... "CFNWaiterCustomResource": { "Type": "AWS::CloudFormation::CustomResource", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "CFNWaiter", "Arn" ] }, "WaitSeconds": 60 } } }, ...

YAML

... CFNWaiterCustomResource: Type: "AWS::CloudFormation::CustomResource" Properties: ServiceToken: !GetAtt CFNWaiter.Arn WaitSeconds: 60 ...
Outputs

The Output of this template are the TimeWaited and the WaiterId. The TimeWaited value uses a Fn::GetAtt function to provide the amount of time the waiter resource actually waited. The WaiterId uses a Fn::GetAtt function to provide the unique ID that was generated and associated with the execution.

JSON

... "Outputs": { "TimeWaited": { "Value": { "Fn::GetAtt": [ "CFNWaiterCustomResource", "TimeWaited" ] }, "Export": { "Name": "TimeWaited" } }, "WaiterId": { "Value": { "Fn::GetAtt": [ "CFNWaiterCustomResource", "Id" ] }, "Export": { "Name": "WaiterId" } } } ...

YAML

... Outputs: TimeWaited: Value: !GetAtt CFNWaiterCustomResource.TimeWaited Export: Name: TimeWaited WaiterId: Value: !GetAtt CFNWaiterCustomResource.Id Export: Name: WaiterId ...

Prerequisites

You must have IAM permissions to use all the corresponding services, such as Lambda and CloudFormation.

Launching the stack

To create the stack
  1. Find the template of your preference (YAML or JSON) from the Sample template section and save it to your machine with the name samplelambdabackedcustomresource.template.

  2. Open the CloudFormation console at https://console.aws.amazon.com/cloudformation/.

  3. From the Stacks page, choose Create stack at top right, and then choose With new resources (standard).

  4. For Prerequisite - Prepare template, choose Choose an existing template.

  5. For Specify template, choose Upload a template file, and then choose Choose file.

  6. Select the samplelambdabackedcustomresource.template template file you saved earlier.

  7. Choose Next.

  8. For Stack name, type SampleCustomResourceStack. and choose Next.

  9. For this walkthrough, you don't need to add tags or specify advanced settings, so choose Next.

  10. Ensure that the stack name looks correct, and then choose Create.

It might take several minutes for CloudFormation to create your stack. To monitor progress, view the stack events. For more information, see View stack information from the CloudFormation console.

If stack creation succeeds, all resources in the stack, such as the Lambda function and custom resource, were created. You have successfully used a Lambda function and custom resource.

If the Lambda function returns an error, view the function's logs in the CloudWatch Logs console. The name of the log stream is the physical ID of the custom resource, which you can find by viewing the stack's resources. For more information, see View log data in the Amazon CloudWatch User Guide.

Cleaning up resources

Delete the stack to clean up all the stack resources that you created so that you aren't charged for unnecessary resources.

To delete the stack
  1. From the CloudFormation console, choose the SampleCustomResourceStack stack.

  2. Choose Actions and then Delete Stack.

  3. In the confirmation message, choose Yes, Delete.

All the resources that you created are deleted.

Now that you understand how to create and use Lambda-backed custom resource, you can use the sample template and code from this walkthrough to build and experiment with other stacks and functions.

Related information

PrivacySite termsCookie preferences
© 2025, Amazon Web Services, Inc. or its affiliates. All rights reserved.