Custom Resources
CloudFormation Custom resources provide a way for AWS Lambda functions to execute provisioning logic whenever CloudFormation stacks are created, updated, or deleted.
Powertools-cloudformation makes it easy to write Lambda functions in Java that are used as CloudFormation custom resources.
The utility reads incoming CloudFormation events, calls your custom code depending on the operation (CREATE, UPDATE or DELETE) and sends responses back to CloudFormation.
By using this library you do not need to write code to integrate with CloudFormation, and you only focus on writing the custom provisioning logic inside the Lambda function.
Install¶
To install this utility, add the following dependency to your project.
1 2 3 4 5 |
|
1 2 3 4 |
|
Usage¶
To utilise the feature, extend the AbstractCustomResourceHandler
class in your Lambda handler class.
Next, implement and override the following 3 methods: create
, update
and delete
. The AbstractCustomResourceHandler
invokes the right method according to the CloudFormation custom resource request event it receives.
Inside the methods, implement your custom provisioning logic, and return a Response
. The AbstractCustomResourceHandler
takes your Response
, builds a
custom resource responses and sends it to CloudFormation automatically.
Custom resources notify cloudformation either of SUCCESS
or FAILED
status. You have 2 utility methods to represent these responses: Response.success(physicalResourceId)
and Response.failed(physicalResourceId)
.
The physicalResourceId
is an identifier that is used during the lifecycle operations of the Custom Resource.
You should generate a physicalResourceId
during the CREATE
operation, CloudFormation stores the physicalResourceId
and includes it in UPDATE
and DELETE
events.
Here an example of how to implement a Custom Resource using the powertools-cloudformation library:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
Missing Response
and exception handling¶
If a Response
is not returned by your code, AbstractCustomResourceHandler
defaults the response to SUCCESS
.
If your code raises an exception (which is not handled), the AbstractCustomResourceHandler
defaults the response to FAILED
.
In both of the scenarios, powertools-java will return the physicalResourceId
to CloudFormation based on the following logic:
- For CREATE operations, the LogStreamName
from the Lambda context is used.
- For UPDATE and DELETE operations, the physicalResourceId
provided in the CloudFormationCustomResourceEvent
is used.
Why do you need a physicalResourceId?¶
It is recommended that you always explicitly provide a physicalResourceId
in your response rather than letting Powertools for AWS Lambda (Java) generate if for you because physicalResourceId
has a crucial role in the lifecycle of a CloudFormation custom resource.
If the physicalResourceId
changes between calls from Cloudformation, for instance in response to an Update
event, Cloudformation treats the resource update as a replacement.
Customising a response¶
As well as the Response.success(physicalResourceId)
and Response.failed(physicalResourceId)
, you can customise the Response
by using the Response.builder()
.
You customise the responses when you need additional attributes to be shared with other parts of the CloudFormation stack.
In the example below, the Lambda function creates a Chime AppInstance and maps the returned ARN to a "ChimeAppInstanceArn" attribute.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
For the example above the following response payload will be sent.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Once the custom resource receives this response, its "ChimeAppInstanceArn" attribute is set and the Fn::GetAtt function may be used to retrieve the attribute value and make it available to other resources in the stack.
Sensitive Response Data¶
If any attributes are sensitive, enable the "noEcho" flag to mask the output of the custom resource when it's retrieved with the Fn::GetAtt function.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Customizing Serialization¶
Although using a Map
as the Response's value is the most straightforward way to provide attribute name/value pairs,
any arbitrary java.lang.Object
may be used. By default, these objects are serialized with an internal Jackson
ObjectMapper
. If the object requires special serialization logic, a custom ObjectMapper
can be specified.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Advanced¶
Understanding the CloudFormation custom resource lifecycle¶
While the library provides an easy-to-use interface, we recommend that you understand the lifecycle of CloudFormation custom resources before using them in production.
Creating a custom resource¶
When CloudFormation issues a CREATE on a custom resource, there are 2 possible states: CREATE_COMPLETE
and CREATE_FAILED
stateDiagram
direction LR
createState: Create custom resource
[*] --> createState
createState --> CREATE_COMPLETE
createState --> CREATE_FAILED
If the resource is created successfully, the physicalResourceId
is stored by CloudFormation for future operations.
If the resource failed to create, CloudFormation triggers a rollback operation by default (rollback can be disabled, see stack failure options)
Updating a custom resource¶
CloudFormation issues an UPDATE operation on a custom resource only when one or more custom resource properties change. During the update, the custom resource may update successfully, or may fail the update.
stateDiagram
direction LR
updateState: Update custom resource
[*] --> updateState
updateState --> UPDATE_COMPLETE
updateState --> UPDATE_FAILED
In both of these scenarios, the custom resource can return the same physicalResourceId
it received in the CloudFormation event, or a different physicalResourceId
.
Semantically an UPDATE_COMPLETE
that returns the same physicalResourceId
it received indicates that the existing resource was updated successfully.
Instead, an UPDATE_COMPLETE
with a different physicalResourceId
means that a new physical resource was created successfully.
flowchart BT
id1(Logical resource)
id2(Previous physical Resource)
id3(New physical Resource)
id2 --> id1
id3 --> id1
Therefore, after the custom resource update completed or failed, there may be other cleanup operations by Cloudformation during the rollback, as described in the diagram below:
stateDiagram
state if_state <<choice>>
updateState: Update custom resource
deletePrev: DELETE resource with previous physicalResourceId
updatePrev: Rollback - UPDATE resource with previous properties
noOp: No further operations
[*] --> updateState
updateState --> UPDATE_COMPLETE
UPDATE_COMPLETE --> if_state
if_state --> noOp : Same physicalResourceId
if_state --> deletePrev : Different physicalResourceId
updateState --> UPDATE_FAILED
UPDATE_FAILED --> updatePrev
Deleting a custom resource¶
CloudFormation issues a DELETE on a custom resource when:
- the CloudFormation stack is being deleted
- a new physicalResourceId
was received during an update, and CloudFormation proceeds to rollback(DELETE) the custom resource with the previous physicalResourceId
.
stateDiagram
direction LR
deleteState: Delete custom resource
[*] --> deleteState
deleteState --> DELETE_COMPLETE
deleteState --> DELETE_FAILED