DVA-C02Chapter 99 of 101Objective 3.2

CloudFormation Custom Resources with Lambda

This chapter covers AWS CloudFormation Custom Resources, a powerful mechanism to extend CloudFormation's provisioning capabilities beyond native resources. For the DVA-C02 exam, Custom Resources are a high-frequency topic within the Deployment domain (Objective 3.2: Deploy and manage resources with AWS CloudFormation). Expect 1-2 questions specifically on Custom Resource lifecycle, Lambda integration, and failure handling. Mastering this topic requires understanding the request/response protocol, timeout constraints, and common pitfalls like missing signal responses.

25 min read
Intermediate
Updated May 31, 2026

Custom Resources: The CloudFormation Swiss Army Knife

Imagine you are building a house with a construction crew (CloudFormation). The crew has a standard set of tools for framing, wiring, and plumbing — these are the built-in AWS resources. But what if you need a custom-made chandelier that requires a specialized artisan? You can't build it with standard tools. So, you hire a freelance artisan (Lambda function) to create the chandelier. You write a work order (Custom Resource request) specifying the design (properties). The artisan receives the order, builds the chandelier, and sends back a confirmation (response) with the finished product's serial number (physical resource ID). The crew then installs it. If you need to renovate (update) or demolish (delete) the house, the crew sends the artisan a change order or teardown request. The artisan must acknowledge each request and report completion; otherwise, the crew waits indefinitely (timeout). This is exactly how CloudFormation Custom Resources work: they extend the provisioning engine to handle any resource that isn't natively supported, using a Lambda-backed backend that must communicate back within a strict timeout.

How It Actually Works

What Are CloudFormation Custom Resources?

CloudFormation Custom Resources allow you to write custom provisioning logic for resources that are not natively supported by CloudFormation. They act as a bridge between CloudFormation and any AWS service or external system. The custom resource is defined in a CloudFormation template using the AWS::CloudFormation::CustomResource or Custom::* type. When CloudFormation creates, updates, or deletes the stack, it sends a request to a specified endpoint — typically an AWS Lambda function or an SNS topic — which performs the desired action and returns a response to CloudFormation.

Why Custom Resources Exist

AWS CloudFormation supports hundreds of native resources, but you may need to:

Provision resources from third-party services (e.g., a DNS record in Route53 that requires an API call not modeled as a native resource).

Perform complex bootstrapping or configuration that isn't covered by UserData or AWS::CloudFormation::Init.

Integrate with on-premises systems or other cloud providers.

Implement custom validation or logic that depends on stack parameters.

Custom Resources fill this gap by allowing you to define arbitrary provisioning logic.

How It Works Internally

The interaction between CloudFormation and the custom resource provider (Lambda) follows a strict protocol:

1. CloudFormation sends a request — When creating, updating, or deleting a stack, CloudFormation generates a JSON payload and sends it to the specified ServiceToken. The payload includes: - RequestType: Create, Update, or Delete. - ResourceProperties: The properties defined in the template. - OldResourceProperties: (only on Update) the previous properties. - StackId, RequestId, ResourceType, LogicalResourceId, PhysicalResourceId (for Update/Delete).

2. Lambda function processes the request — The Lambda function must perform the desired action (e.g., create an ACM certificate) and then send a response back to CloudFormation via a pre-signed S3 URL provided in the request (ResponseURL). The response must be a JSON object with: - Status: SUCCESS or FAILED. - PhysicalResourceId: A unique identifier for the resource (used in subsequent Update/Delete requests). - StackId, RequestId, LogicalResourceId (must match the request). - Data: Optional key-value pairs that can be referenced using Fn::GetAtt in the template.

3.

CloudFormation waits for the response — CloudFormation waits up to 1 hour (3600 seconds) for the response. If no response is received within that timeout, the stack operation fails and rolls back. The Lambda function has its own timeout (max 15 minutes), so the function must complete and send the response before either timeout expires.

Key Components and Defaults

ServiceToken: The ARN of the Lambda function or SNS topic. Must be in the same region.

Timeout: CloudFormation's maximum wait time for a response is 1 hour. This is not configurable. However, you can set a shorter timeout using the ServiceTimeout property (in seconds) on the custom resource.

Lambda Timeout: The Lambda function's timeout must be less than the CloudFormation timeout. Default Lambda timeout is 3 seconds; must be increased for custom resources (typically 5-10 minutes).

PhysicalResourceId: Must be stable across updates; if it changes, CloudFormation treats it as a replacement (deletes old, creates new).

ResponseURL: Pre-signed S3 URL valid for 1 hour. The Lambda function must PUT the response to this URL.

Configuration and Verification

To create a custom resource, define it in the template:

Resources:
  MyCustomResource:
    Type: Custom::MyCustomType
    Properties:
      ServiceToken: !GetAtt MyLambdaFunction.Arn
      Property1: value1
      Property2: value2

The Lambda function must include code to parse the request, perform the action, and send the response. A common pattern is to use the cfn-response module (Node.js) or cfnresponse (Python) to simplify sending responses.

Example Python handler:

import cfnresponse
import boto3

def handler(event, context):
    physical_resource_id = event.get('PhysicalResourceId', None)
    try:
        if event['RequestType'] == 'Create':
            # Create resource
            physical_resource_id = 'unique-id'
        elif event['RequestType'] == 'Update':
            # Update resource
        elif event['RequestType'] == 'Delete':
            # Delete resource
        cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, physical_resource_id)
    except Exception as e:
        cfnresponse.send(event, context, cfnresponse.FAILED, {}, physical_resource_id, reason=str(e))

Important: The function must send a response for every request type, including Delete. Failing to do so will cause the stack to hang and eventually fail.

Interaction with Related Technologies

Lambda: The most common service token. The Lambda function must have permissions to invoke the actions it performs (e.g., acm:RequestCertificate) and to PUT to the S3 response URL.

SNS: You can use an SNS topic as the service token. The SNS topic triggers a Lambda function or sends to an HTTP endpoint. This adds decoupling but introduces an additional point of failure.

IAM: The Lambda execution role must allow logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents for logging, and any resource-specific permissions.

CloudFormation Stack Drift: Custom resources are not checked for drift by CloudFormation. You must implement your own drift detection logic if needed.

Failure Handling and Rollback

If the Lambda function returns FAILED or times out, CloudFormation will:

Mark the stack operation as failed.

If the operation was a Create, roll back by deleting any resources created (including the custom resource, but the Lambda function must handle the Delete request during rollback).

If the operation was an Update, roll back to the previous state. The Lambda function will receive a Delete request for the old resource and a Create request for the new resource (if the update caused replacement).

Common mistake: Not handling the Delete request during rollback. If the function does not respond to the Delete request, the stack will hang and eventually fail rollback.

Best Practices

Always send a response, even on failure.

Use idempotent logic: the same Create request may be retried.

Generate a unique PhysicalResourceId only on Create; reuse it on Update/Delete.

Set appropriate Lambda timeout (e.g., 5 minutes) and CloudFormation timeout (e.g., 300 seconds) using ServiceTimeout.

Log all requests and responses for debugging.

Use the cfn-response library to avoid manual HTTP requests.

Exam-Relevant Details

The ServiceToken must be a Lambda ARN or SNS topic ARN.

ServiceTimeout is an optional property that sets the maximum time (in seconds) CloudFormation waits for a response. Default is 3600 (1 hour).

The Lambda function must respond to Delete requests even during stack rollback.

If the Lambda function fails to respond, the stack will remain in CREATE_IN_PROGRESS or UPDATE_IN_PROGRESS until the timeout expires.

Custom resources can return data via Data in the response, accessible with Fn::GetAtt.

The PhysicalResourceId must be unique per resource; changing it triggers replacement.

Walk-Through

1

Define Custom Resource in Template

In your CloudFormation template, declare a resource with type `Custom::MyType` or `AWS::CloudFormation::CustomResource`. Provide a `ServiceToken` property pointing to the ARN of the Lambda function that will handle the provisioning. You can also pass additional properties that the Lambda function will receive in the `ResourceProperties` object. CloudFormation will generate a unique `RequestId` and `StackId` for each request.

2

CloudFormation Sends Create Request

When the stack is created, CloudFormation constructs a JSON request with `RequestType` set to `'Create'`, `ResourceProperties` containing the properties from the template, and a `ResponseURL` (a pre-signed S3 URL). This request is sent to the `ServiceToken` (Lambda). The Lambda function is invoked synchronously with this event. CloudFormation then waits for a response to the `ResponseURL`.

3

Lambda Processes Request and Responds

The Lambda function parses the event, performs the desired action (e.g., create an ACM certificate), and generates a response. The response must include `Status` (`'SUCCESS'` or `'FAILED'`), `PhysicalResourceId` (a unique identifier for the resource), and optionally `Data` (key-value pairs). The function sends this response via an HTTP PUT to the `ResponseURL`. It must do so before the Lambda timeout (up to 15 min) and the CloudFormation timeout (default 1 hour).

4

CloudFormation Receives Response and Continues

CloudFormation receives the HTTP PUT at the pre-signed URL. If `Status` is `'SUCCESS'`, it continues stack creation. The `PhysicalResourceId` is stored in the stack metadata. If `Status` is `'FAILED'`, CloudFormation marks the resource as failed and begins stack rollback. During rollback, CloudFormation sends a `Delete` request to the same Lambda function for the custom resource.

5

Update and Delete Operations

On stack update, CloudFormation sends an `Update` request with both `ResourceProperties` and `OldResourceProperties`. The Lambda function must update the resource accordingly and send a response. On stack deletion (or rollback), CloudFormation sends a `Delete` request. The Lambda function must clean up the resource and respond. If no response is received within the timeout, the stack operation fails.

What This Looks Like on the Job

Enterprise Scenario 1: Automated ACM Certificate Request

A company uses CloudFormation to deploy a multi-region application with HTTPS endpoints. ACM certificates are not natively supported by CloudFormation for DNS validation (only email validation was native at the time). They create a custom resource backed by a Lambda function that:

On Create: Requests an ACM certificate with DNS validation, creates Route53 DNS records, and waits for validation.

On Delete: Deletes the certificate and DNS records.

The PhysicalResourceId is the certificate ARN.

The function returns the certificate ARN in Data, which is used by the CloudFront distribution resource.

Scale: Deployed across 5 regions, each with 10 certificates. The Lambda function must handle concurrent requests and respect AWS API rate limits. Pitfall: If the Lambda function times out during DNS validation (which can take minutes), the stack hangs. They set ServiceTimeout to 600 seconds and Lambda timeout to 900 seconds.

Enterprise Scenario 2: On-Premises Database Integration

A financial services company uses CloudFormation to provision EC2 instances that must be registered in an on-premises CMDB. They use a custom resource with a Lambda function that makes HTTP calls to the on-premises API via a VPN. The Lambda function:

On Create: Registers the instance ID and IP in the CMDB.

On Update: Updates the record.

On Delete: Removes the record.

Returns the CMDB record ID as PhysicalResourceId.

Considerations: The Lambda function must have VPC access to reach the on-premises API. Network latency and API failures must be handled with retries. Misconfiguration: If the Lambda function fails to respond due to a network timeout, CloudFormation will wait up to 1 hour before failing, causing a long deployment cycle.

Enterprise Scenario 3: Custom AMI Creation Pipeline

A gaming company uses CloudFormation to build AMIs using a custom resource. The Lambda function triggers an EC2 Image Builder pipeline. The pipeline takes up to 30 minutes. The custom resource:

On Create: Starts the Image Builder pipeline and polls until completion.

On Delete: Does nothing (or deregisters the AMI).

Returns the AMI ID in Data.

Challenge: The Lambda function must handle the long-running process. Since Lambda has a 15-minute max timeout, they cannot wait 30 minutes. Solution: Use an S3 bucket as a coordination point. The function starts the pipeline, sends a response immediately with a placeholder PhysicalResourceId, and then a separate process (e.g., EventBridge rule) updates the stack after completion. This is an advanced pattern called 'async custom resource'.

How DVA-C02 Actually Tests This

What DVA-C02 Tests on Custom Resources

The exam focuses on the Custom Resource lifecycle, the requirement to send a response, and the consequences of failing to do so. Objective 3.2 specifically includes 'Implement resource lifecycle hooks' and 'Troubleshoot stack failures'. Expect questions that test:

The mandatory response to Delete requests.

The effect of missing PhysicalResourceId or incorrect Status.

The purpose of ServiceToken and ServiceTimeout.

How Fn::GetAtt works with custom resources.

Common Wrong Answers and Why Candidates Choose Them

1.

'The Lambda function does not need to respond to Delete requests if the resource was never created.' — Wrong. CloudFormation always sends a Delete request, even if the Create failed. The function must respond to avoid hanging.

2.

'You can use any HTTP endpoint as the ServiceToken.' — Wrong. The ServiceToken must be an ARN of a Lambda function or an SNS topic. Direct HTTP endpoints are not supported.

3.

'The Lambda function must complete within 1 hour.' — Misleading. The Lambda function has its own timeout (max 15 min). The 1-hour is CloudFormation's timeout for receiving the response. The function must respond before both timeouts.

4.

'Custom resources can only be used in Create operations.' — Wrong. They support Create, Update, and Delete.

Specific Numbers and Terms That Appear on the Exam

ServiceToken: Must be an ARN.

ServiceTimeout: Default 3600 seconds (1 hour).

PhysicalResourceId: Must be unique; if it changes on Update, CloudFormation treats it as replacement.

ResponseURL: Pre-signed S3 URL.

cfn-response module is used to send responses.

Edge Cases and Exceptions

Stack rollback: During rollback, CloudFormation sends Delete requests for resources that were created. The Lambda function must handle these.

No response: If the Lambda function crashes without sending a response, the stack will remain in progress until the timeout.

Update with replacement: If the PhysicalResourceId changes during an Update, CloudFormation first sends a Delete for the old resource, then a Create for the new one.

How to Eliminate Wrong Answers

If an answer says 'The Lambda function only needs to respond to Create requests', eliminate it — all request types need a response.

If an answer says 'Use an HTTP URL as ServiceToken', eliminate it — must be ARN.

If an answer says 'Custom resources are checked for drift', eliminate it — they are not.

If an answer mentions 'ServiceTimeout' as a Lambda property, eliminate it — it's a CloudFormation resource property.

Key Takeaways

Custom Resources extend CloudFormation to provision non-native resources using Lambda or SNS as the service token.

The Lambda function must send a response (SUCCESS or FAILED) to the pre-signed S3 URL for every request type: Create, Update, and Delete.

CloudFormation waits up to 1 hour for a response; the Lambda function must respond within both its own timeout (max 15 min) and the CloudFormation timeout.

If no response is received, the stack operation hangs and eventually fails with a timeout error.

The PhysicalResourceId must be stable; changing it on Update triggers resource replacement (Delete old, Create new).

Custom resources are not checked for drift; you must implement custom drift detection if needed.

The ServiceToken must be an ARN (Lambda or SNS); direct HTTP endpoints are not supported.

Use the cfn-response module (Node.js/Python) to simplify sending responses and avoid manual HTTP requests.

Always handle Delete requests, even during stack rollback, to avoid stack hang.

Custom resources can return data via the Data field, accessible with Fn::GetAtt in the template.

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

Custom Resource with Lambda

Direct invocation of Lambda; simpler setup.

Response is sent directly from Lambda to CloudFormation.

Lambda timeout limits execution to 15 minutes.

Easier to debug via CloudWatch Logs.

Commonly tested on DVA-C02.

Custom Resource with SNS

SNS can fan out to multiple subscribers (e.g., Lambda + HTTP).

Response must be sent by the subscriber; SNS does not respond automatically.

No built-in timeout; relies on subscriber.

Adds decoupling but increases complexity.

Less common on the exam, but possible.

Watch Out for These

Mistake

Custom resources can only be used with Lambda functions.

Correct

The ServiceToken can be a Lambda function ARN or an SNS topic ARN. SNS can fan out to multiple endpoints, including Lambda, HTTP, or email.

Mistake

The Lambda function must respond within 15 minutes (Lambda timeout).

Correct

Lambda timeout is 15 minutes max, but CloudFormation's ServiceTimeout defaults to 1 hour. The function must respond before either timeout. You can set a shorter ServiceTimeout to fail fast.

Mistake

If the Lambda function fails during Create, the Delete request is not sent.

Correct

CloudFormation always sends a Delete request during rollback, even if the Create failed. The function must handle it and respond.

Mistake

You can use Fn::GetAtt to retrieve any attribute from a custom resource.

Correct

Fn::GetAtt only retrieves attributes that were returned in the Data map of the response. If the Lambda function does not return a Data key, Fn::GetAtt will fail.

Mistake

Custom resources support only Create and Delete operations.

Correct

Custom resources support Create, Update, and Delete. The Lambda function must handle all three RequestTypes.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

What happens if my Lambda function does not send a response to CloudFormation?

If the Lambda function does not send a response (either because it crashed or the code didn't call the response URL), CloudFormation will wait for the duration of the ServiceTimeout (default 1 hour). After that, the stack operation fails with a timeout error. The stack will be rolled back, and during rollback, CloudFormation will send a Delete request to the same function. If the function still does not respond, the rollback will also hang. Always ensure your function sends a response in all code paths, including error handlers.

Can I use a custom resource to create a resource that takes longer than 15 minutes?

The Lambda function has a maximum timeout of 15 minutes. If your provisioning action takes longer, you cannot wait synchronously in Lambda. Instead, use an asynchronous pattern: have the Lambda function start the long-running process, return immediately with a placeholder PhysicalResourceId, and then have a separate mechanism (e.g., EventBridge rule, Step Functions) update the stack with the actual resource ID using the CloudFormation UpdateStack API. This is an advanced pattern and is not commonly tested on DVA-C02.

What is the difference between ServiceTimeout and Lambda timeout?

ServiceTimeout is a property of the custom resource in the CloudFormation template. It specifies the maximum time (in seconds) that CloudFormation will wait for a response from the custom resource provider. The default is 3600 (1 hour). Lambda timeout is a property of the Lambda function itself, with a maximum of 900 seconds (15 minutes). The effective timeout for the custom resource operation is the minimum of these two values. You should set ServiceTimeout to a value slightly higher than the expected Lambda execution time to avoid premature failures.

Can I use Fn::GetAtt to get the PhysicalResourceId of a custom resource?

No, the PhysicalResourceId is not accessible via Fn::GetAtt. It is used internally by CloudFormation to track the resource. To expose data to other parts of the template, your Lambda function must return key-value pairs in the Data field of the response. For example, if you return {"Data": {"Arn": "arn:..."}}, you can use !GetAtt MyCustomResource.Arn in the template.

What happens if I change the ServiceToken after stack creation?

If you update the ServiceToken property (e.g., change the Lambda function ARN), CloudFormation will treat the custom resource as requiring an update. It will send an Update request to the new ServiceToken. However, the PhysicalResourceId should remain the same to avoid replacement. If the PhysicalResourceId changes, CloudFormation will delete the old resource and create a new one.

Is it possible to test a custom resource without creating a stack?

Yes, you can manually invoke the Lambda function with a test event that mimics the CloudFormation request. You must provide a valid ResponseURL (you can generate a pre-signed S3 URL or use a dummy URL for testing, but the function will try to PUT to it). A better approach is to use the AWS CloudFormation console's 'Test' button (if available) or use the AWS CLI with the `aws cloudformation test-custom-resource` command (not a real command; you can simulate by invoking Lambda directly).

Can a custom resource have dependencies on other resources?

Yes, you can use DependsOn to ensure that other resources are created before the custom resource. Also, you can reference other resources using Ref or Fn::GetAtt in the Properties of the custom resource. CloudFormation will pass those resolved values in the ResourceProperties to the Lambda function.

Terms Worth Knowing

Ready to put this to the test?

You've just covered CloudFormation Custom Resources with Lambda — now see how well it sticks with free DVA-C02 practice questions. Full explanations included, no account needed.

Done with this chapter?