-
Notifications
You must be signed in to change notification settings - Fork 96
docs(cloudformation-module): Improve Docs #1090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
82e137c
d697967
0373c1c
6c2ae4c
7cea781
e609e07
b30a8fa
fba5e57
b5c4bff
2d7d5a3
c5bb489
1300bfe
b5cb50b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,17 +3,14 @@ title: Custom Resources | |
description: Utility | ||
--- | ||
|
||
[Custom resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) | ||
[CloudFormation Custom resources](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html) | ||
provide a way for [AWS Lambda functions]( | ||
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html) to execute | ||
provisioning logic whenever CloudFormation stacks are created, updated, or deleted. The CloudFormation utility enables | ||
developers to write these Lambda functions in Java. | ||
provisioning logic whenever CloudFormation stacks are created, updated, or deleted. | ||
|
||
The utility provides a base `AbstractCustomResourceHandler` class which handles [custom resource request events]( | ||
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html), constructs | ||
[custom resource responses](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html), and | ||
sends them to the custom resources. Subclasses implement the provisioning logic and configure certain properties of | ||
these response objects. | ||
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 | ||
|
||
|
@@ -40,57 +37,90 @@ To install this utility, add the following dependency to your project. | |
|
||
## Usage | ||
|
||
Create a new `AbstractCustomResourceHandler` subclass and implement the `create`, `update`, and `delete` methods with | ||
provisioning logic in the appropriate methods(s). | ||
To utilise the feature, extend the `AbstractCustomResourceHandler` class in your Lambda handler class. | ||
After you extend the `AbstractCustomResourceHandler`, 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]( | ||
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html) it receives. | ||
Inside the methods, implement your custom provisioning logic, and return a `Response`. The `AbstractCustomResourceHandler` takes your `Response`, builds a | ||
[custom resource responses](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html) and sends it to CloudFormation automatically. | ||
|
||
As an example, if a Lambda function only needs to provision something when a stack is created, put the provisioning | ||
logic exclusively within the `create` method; the other methods can just return `null`. | ||
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 supply a `physicalResourceId` during the `CREATE` operation, CloudFormation stores the `physicalResourceId` and includes it `UPDATE` and `DELETE` events. | ||
|
||
Here an example of how to implement a Custom Resource using the powertools-cloudformation library: | ||
|
||
```java hl_lines="8 9 10 11" | ||
import com.amazonaws.services.lambda.runtime.Context; | ||
import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; | ||
import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler; | ||
import software.amazon.lambda.powertools.cloudformation.Response; | ||
|
||
public class ProvisionOnCreateHandler extends AbstractCustomResourceHandler { | ||
public class MyCustomResourceHandler extends AbstractCustomResourceHandler { | ||
|
||
@Override | ||
protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) { | ||
doProvisioning(); | ||
return Response.success(); | ||
String physicalResourceId = "sample-resource-id-" + UUID.randomUUID(); //Create a unique ID for your resource | ||
ProvisioningResult provisioningResult = doProvisioning(physicalResourceId); | ||
if(provisioningResult.isSuccessful()){ //check if the provisioning was successful | ||
return Response.success(physicalResourceId); | ||
}else{ | ||
return Response.failed(physicalResourceId); | ||
} | ||
} | ||
|
||
@Override | ||
protected Response update(CloudFormationCustomResourceEvent updateEvent, Context context) { | ||
return null; | ||
String physicalResourceId = updateEvent.getPhysicalResourceId(); //Get the PhysicalResourceId from CloudFormation | ||
UpdateResult updateResult = doUpdates(physicalResourceId); | ||
if(updateResult.isSuccessful()){ //check if the update operations were successful | ||
return Response.success(physicalResourceId); | ||
}else{ | ||
return Response.failed(physicalResourceId); | ||
} | ||
} | ||
|
||
@Override | ||
protected Response delete(CloudFormationCustomResourceEvent deleteEvent, Context context) { | ||
return null; | ||
String physicalResourceId = deleteEvent.getPhysicalResourceId(); //Get the PhysicalResourceId from CloudFormation | ||
DeleteResult deleteResult = doDeletes(physicalResourceId); | ||
if(deleteResult.isSuccessful()){ //check if the delete operations were successful | ||
return Response.success(physicalResourceId); | ||
}else{ | ||
return Response.failed(physicalResourceId); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### Signaling Provisioning Failures | ||
### 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 assign the `physicalResourceId` based on the following logic: | ||
mriccia marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
- if present, use the `physicalResourceId` provided in the `CloudFormationCustomResourceEvent` | ||
- if it is not present, use the `LogStreamName` from the Lambda context | ||
|
||
|
||
#### Why does this matter? | ||
|
||
If provisioning fails, the stack creation/modification/deletion as a whole can be failed by either throwing a | ||
`RuntimeException` or by explicitly returning a `Response` with a failed status, e.g. `Response.failure()`. | ||
It is recommended that you always provide a `physicalResourceId` in your response because `physicalResourceId` has a crucial role in the lifecycle of a CloudFormation custom resource. | ||
mriccia marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
If the `physicalResourceId` changes between calls from Cloudformation, for instance in response to an `Update` event, Cloudformation [treats the resource update as a replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html). | ||
|
||
### Configuring Response Objects | ||
### Customising a response | ||
|
||
When provisioning results in data to be shared with other parts of the stack, include this data within the returned | ||
`Response` instance. | ||
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. | ||
|
||
This Lambda function creates a [Chime AppInstance](https://docs.aws.amazon.com/chime/latest/dg/create-app-instance.html) | ||
In the example below, the Lambda function creates a [Chime AppInstance](https://docs.aws.amazon.com/chime/latest/dg/create-app-instance.html) | ||
and maps the returned ARN to a "ChimeAppInstanceArn" attribute. | ||
|
||
```java hl_lines="11 12 13 14" | ||
public class ChimeAppInstanceHandler extends AbstractCustomResourceHandler { | ||
@Override | ||
protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) { | ||
String physicalResourceId = "my-app-name-" + UUID.randomUUID(); //Create a unique ID | ||
CreateAppInstanceRequest chimeRequest = CreateAppInstanceRequest.builder() | ||
.name("my-app-name") | ||
.name(physicalResourceId) | ||
.build(); | ||
CreateAppInstanceResponse chimeResponse = ChimeClient.builder() | ||
.region("us-east-1") | ||
|
@@ -99,6 +129,8 @@ public class ChimeAppInstanceHandler extends AbstractCustomResourceHandler { | |
Map<String, String> chimeAtts = Map.of("ChimeAppInstanceArn", chimeResponse.appInstanceArn()); | ||
return Response.builder() | ||
.value(chimeAtts) | ||
.status(Response.Status.SUCCESS) | ||
.physicalResourceId(physicalResourceId) | ||
.build(); | ||
} | ||
} | ||
|
@@ -113,14 +145,15 @@ For the example above the following response payload will be sent. | |
"StackId": "arn:aws:cloudformation:us-east-1:123456789000:stack/Custom-stack/59e4d2d0-2fe2-10ec-b00e-124d7c1c5f15", | ||
"RequestId": "7cae0346-0359-4dff-b80a-a82f247467b6", | ||
"LogicalResourceId:": "ChimeTriggerResource", | ||
"PhysicalResourceId:": "my-app-name-db4a47b9-0cac-45ba-8cc4-a480490c5779", | ||
"NoEcho": false, | ||
"Data": { | ||
"ChimeAppInstanceArn": "arn:aws:chime:us-east-1:123456789000:app-instance/150972c2-5490-49a9-8ba7-e7da4257c16a" | ||
} | ||
} | ||
``` | ||
|
||
Once the custom resource receives this response, it's "ChimeAppInstanceArn" attribute is set and the | ||
Once the custom resource receives this response, its "ChimeAppInstanceArn" attribute is set and the | ||
[Fn::GetAtt function]( | ||
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html) may be used to | ||
retrieve the attribute value and make it available to other resources in the stack. | ||
|
Uh oh!
There was an error while loading. Please reload this page.