Skip to content

Commit d697967

Browse files
committed
more doc changes
1 parent 82e137c commit d697967

File tree

1 file changed

+42
-14
lines changed

1 file changed

+42
-14
lines changed

docs/utilities/custom_resources.md

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,24 @@ https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests
4343
Inside the methods, implement your custom provisioning logic, and return a `Response`. The `AbstractCustomResourceHandler` takes your `Response`, builds a
4444
[custom resource responses](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html) and sends it to CloudFormation automatically.
4545

46-
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)`.
47-
If a `Response` is not returned by your code, `AbstractCustomResourceHandler` defaults the response to `SUCCESS`.
46+
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)`.
47+
The `physicalResourceId` is an identifier that is used during the lifecycle operations of the Custom Resource.
48+
You should supply a `physicalResourceId` during the `CREATE` operation, CloudFormation stores the `physicalResourceId` and includes it `UPDATE` and `DELETE` events.
49+
50+
Here an example of how to implement a Custom Resource using the powertools-cloudformation library:
4851

4952
```java hl_lines="8 9 10 11"
5053
import com.amazonaws.services.lambda.runtime.Context;
5154
import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent;
5255
import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler;
5356
import software.amazon.lambda.powertools.cloudformation.Response;
5457

55-
public class ProvisionOnCreateHandler extends AbstractCustomResourceHandler {
58+
public class MyCustomResourceHandler extends AbstractCustomResourceHandler {
5659

5760
@Override
5861
protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) {
5962
String physicalResourceId = "sample-resource-id-" + UUID.randomUUID(); //Create a unique ID for your resource
60-
ProvisioningResult provisioningResult = doProvisioning();
63+
ProvisioningResult provisioningResult = doProvisioning(physicalResourceId);
6164
if(provisioningResult.isSuccessful()){ //check if the provisioning was successful
6265
return Response.success(physicalResourceId);
6366
}else{
@@ -67,35 +70,57 @@ public class ProvisionOnCreateHandler extends AbstractCustomResourceHandler {
6770

6871
@Override
6972
protected Response update(CloudFormationCustomResourceEvent updateEvent, Context context) {
70-
return null;
73+
String physicalResourceId = updateEvent.getPhysicalResourceId(); //Get the PhysicalResourceId from CloudFormation
74+
UpdateResult updateResult = doUpdates(physicalResourceId);
75+
if(updateResult.isSuccessful()){ //check if the update operations were successful
76+
return Response.success(physicalResourceId);
77+
}else{
78+
return Response.failed(physicalResourceId);
79+
}
7180
}
7281

7382
@Override
7483
protected Response delete(CloudFormationCustomResourceEvent deleteEvent, Context context) {
75-
return null;
84+
String physicalResourceId = deleteEvent.getPhysicalResourceId(); //Get the PhysicalResourceId from CloudFormation
85+
DeleteResult deleteResult = doDeletes(physicalResourceId);
86+
if(deleteResult.isSuccessful()){ //check if the delete operations were successful
87+
return Response.success(physicalResourceId);
88+
}else{
89+
return Response.failed(physicalResourceId);
90+
}
7691
}
7792
}
7893
```
7994

80-
### Signaling Provisioning Failures
95+
### Missing `Response` and exception handling
96+
97+
If a `Response` is not returned by your code, `AbstractCustomResourceHandler` defaults the response to `SUCCESS`.
98+
If your code raises an exception (which is not handled), the `AbstractCustomResourceHandler` defaults the response to `FAILED`.
99+
100+
In both of the scenarios, powertools-java will assign the `physicalResourceId` based on the following logic:
101+
- if present, use the `physicalResourceId` provided in the `CloudFormationCustomResourceEvent`
102+
- if it is not present, use the `LogStreamName` from the Lambda context
81103

82-
If the provisioning inside your Custom Resource fails, you can notify CloudFormation of the failure by returning a `Repsonse.failure(physicalResourceId)`.
104+
#### Why does this matter?
83105

106+
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.
107+
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).
84108

85-
### Configuring Response Objects
109+
### Customising a response
86110

87-
When provisioning results in data to be shared with other parts of the stack, include this data within the returned
88-
`Response` instance.
111+
As well as the `Response.success(physicalResourceId)` and `Response.failed(physicalResourceId)`, you can customise the `Response` by using the `Response.builder()`.
112+
You customise the responses when you need additional attributes to be shared with other parts of the CloudFormation stack.
89113

90-
This Lambda function creates a [Chime AppInstance](https://docs.aws.amazon.com/chime/latest/dg/create-app-instance.html)
114+
In the example below, the Lambda function creates a [Chime AppInstance](https://docs.aws.amazon.com/chime/latest/dg/create-app-instance.html)
91115
and maps the returned ARN to a "ChimeAppInstanceArn" attribute.
92116

93117
```java hl_lines="11 12 13 14"
94118
public class ChimeAppInstanceHandler extends AbstractCustomResourceHandler {
95119
@Override
96120
protected Response create(CloudFormationCustomResourceEvent createEvent, Context context) {
121+
String physicalResourceId = "my-app-name-" + UUID.randomUUID(); //Create a unique ID
97122
CreateAppInstanceRequest chimeRequest = CreateAppInstanceRequest.builder()
98-
.name("my-app-name")
123+
.name(physicalResourceId)
99124
.build();
100125
CreateAppInstanceResponse chimeResponse = ChimeClient.builder()
101126
.region("us-east-1")
@@ -104,6 +129,8 @@ public class ChimeAppInstanceHandler extends AbstractCustomResourceHandler {
104129
Map<String, String> chimeAtts = Map.of("ChimeAppInstanceArn", chimeResponse.appInstanceArn());
105130
return Response.builder()
106131
.value(chimeAtts)
132+
.status(Response.Status.SUCCESS)
133+
.physicalResourceId(physicalResourceId)
107134
.build();
108135
}
109136
}
@@ -118,14 +145,15 @@ For the example above the following response payload will be sent.
118145
"StackId": "arn:aws:cloudformation:us-east-1:123456789000:stack/Custom-stack/59e4d2d0-2fe2-10ec-b00e-124d7c1c5f15",
119146
"RequestId": "7cae0346-0359-4dff-b80a-a82f247467b6",
120147
"LogicalResourceId:": "ChimeTriggerResource",
148+
"PhysicalResourceId:": "my-app-name-db4a47b9-0cac-45ba-8cc4-a480490c5779",
121149
"NoEcho": false,
122150
"Data": {
123151
"ChimeAppInstanceArn": "arn:aws:chime:us-east-1:123456789000:app-instance/150972c2-5490-49a9-8ba7-e7da4257c16a"
124152
}
125153
}
126154
```
127155

128-
Once the custom resource receives this response, it's "ChimeAppInstanceArn" attribute is set and the
156+
Once the custom resource receives this response, its "ChimeAppInstanceArn" attribute is set and the
129157
[Fn::GetAtt function](
130158
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html) may be used to
131159
retrieve the attribute value and make it available to other resources in the stack.

0 commit comments

Comments
 (0)