diff --git a/examples/pom.xml b/examples/pom.xml
index cca621163..c9b8ea8ae 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -21,6 +21,7 @@
powertools-examples-serialization
powertools-examples-sqs
powertools-examples-validation
+ powertools-examples-cloudformation
diff --git a/examples/powertools-examples-cloudformation/README.md b/examples/powertools-examples-cloudformation/README.md
new file mode 100644
index 000000000..6dbffcf37
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/README.md
@@ -0,0 +1,40 @@
+# Cloudformation Custom Resource Example
+
+This project contains an example of Lambda function using the CloudFormation module of Powertools for AWS Lambda in Java. For more information on this module, please refer to the [documentation](https://awslabs.github.io/aws-lambda-powertools-java/utilities/custom_resources/).
+
+## Deploy the sample application
+
+This sample can be used either with the Serverless Application Model (SAM) or with CDK.
+
+### Deploy with SAM CLI
+To use the SAM CLI, you need the following tools.
+
+* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)
+* Java 8 - [Install Java 8](https://docs.aws.amazon.com/corretto/latest/corretto-8-ug/downloads-list.html)
+* Maven - [Install Maven](https://maven.apache.org/install.html)
+* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community)
+
+To build and deploy this application for the first time, run the following in your shell:
+
+```bash
+cd infra/sam
+sam build
+sam deploy --guided --parameter-overrides BucketNameParam=my-unique-bucket-20230717
+```
+
+### Deploy with CDK
+To use CDK you need the following tools.
+
+* CDK - [Install CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html)
+* Java 8 - [Install Java 8](https://docs.aws.amazon.com/corretto/latest/corretto-8-ug/downloads-list.html)
+* Maven - [Install Maven](https://maven.apache.org/install.html)
+* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community)
+
+To build and deploy this application for the first time, run the following in your shell:
+
+```bash
+cd infra/cdk
+mvn package
+cdk synth
+cdk deploy -c BucketNameParam=my-unique-bucket-20230718
+```
\ No newline at end of file
diff --git a/examples/powertools-examples-cloudformation/infra/cdk/.gitignore b/examples/powertools-examples-cloudformation/infra/cdk/.gitignore
new file mode 100644
index 000000000..1db21f162
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/cdk/.gitignore
@@ -0,0 +1,13 @@
+.classpath.txt
+target
+.classpath
+.project
+.idea
+.settings
+.vscode
+*.iml
+
+# CDK asset staging directory
+.cdk.staging
+cdk.out
+
diff --git a/examples/powertools-examples-cloudformation/infra/cdk/cdk.json b/examples/powertools-examples-cloudformation/infra/cdk/cdk.json
new file mode 100644
index 000000000..fe011b328
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/cdk/cdk.json
@@ -0,0 +1,37 @@
+{
+ "app": "mvn -e -q compile exec:java",
+ "watch": {
+ "include": [
+ "**"
+ ],
+ "exclude": [
+ "README.md",
+ "cdk*.json",
+ "target",
+ "pom.xml",
+ "src/test"
+ ]
+ },
+ "context": {
+ "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
+ "@aws-cdk/core:checkSecretUsage": true,
+ "@aws-cdk/core:target-partitions": [
+ "aws",
+ "aws-cn"
+ ],
+ "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
+ "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
+ "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
+ "@aws-cdk/aws-iam:minimizePolicies": true,
+ "@aws-cdk/core:validateSnapshotRemovalPolicy": true,
+ "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
+ "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
+ "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
+ "@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
+ "@aws-cdk/core:enablePartitionLiterals": true,
+ "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
+ "@aws-cdk/aws-iam:standardizedServicePrincipals": true,
+ "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
+ "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true
+ }
+}
diff --git a/examples/powertools-examples-cloudformation/infra/cdk/pom.xml b/examples/powertools-examples-cloudformation/infra/cdk/pom.xml
new file mode 100644
index 000000000..30172fa3f
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/cdk/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+ com.myorg
+ powertools-examples-cloudformation-cdk
+ 0.1
+
+
+ UTF-8
+ 2.59.0
+ [10.0.0,11.0.0)
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 8
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.0.0
+
+ com.myorg.PowertoolsExamplesCloudformationCdkApp
+
+
+
+
+
+
+
+
+ software.amazon.awscdk
+ aws-cdk-lib
+ ${cdk.version}
+
+
+
+ software.constructs
+ constructs
+ ${constructs.version}
+
+
+
diff --git a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java
new file mode 100644
index 000000000..84060171b
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java
@@ -0,0 +1,16 @@
+package com.myorg;
+
+import software.amazon.awscdk.App;
+import software.amazon.awscdk.StackProps;
+
+public class PowertoolsExamplesCloudformationCdkApp {
+ public static void main(final String[] args) {
+ App app = new App();
+
+ new PowertoolsExamplesCloudformationCdkStack(app, "PowertoolsExamplesCloudformationCdkStack", StackProps.builder()
+ .build());
+
+ app.synth();
+ }
+}
+
diff --git a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java
new file mode 100644
index 000000000..e880a3534
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkStack.java
@@ -0,0 +1,89 @@
+package com.myorg;
+
+import software.amazon.awscdk.Stack;
+import software.amazon.awscdk.*;
+import software.amazon.awscdk.services.iam.Effect;
+import software.amazon.awscdk.services.iam.PolicyStatement;
+import software.amazon.awscdk.services.iam.PolicyStatementProps;
+import software.amazon.awscdk.services.lambda.Code;
+import software.amazon.awscdk.services.lambda.Function;
+import software.amazon.awscdk.services.lambda.FunctionProps;
+import software.amazon.awscdk.services.lambda.Runtime;
+import software.amazon.awscdk.services.s3.assets.AssetOptions;
+import software.constructs.Construct;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+import static java.util.Collections.singletonList;
+import static software.amazon.awscdk.BundlingOutput.NOT_ARCHIVED;
+
+public class PowertoolsExamplesCloudformationCdkStack extends Stack {
+
+ public static final String SAMPLE_BUCKET_NAME = "sample-bucket-name-20230315-abc123";
+
+ public PowertoolsExamplesCloudformationCdkStack(final Construct scope, final String id) {
+ this(scope, id, null);
+ }
+
+ public PowertoolsExamplesCloudformationCdkStack(final Construct scope, final String id, final StackProps props) {
+ super(scope, id, props);
+
+
+ List functionPackagingInstructions = Arrays.asList(
+ "/bin/sh",
+ "-c",
+ "mvn clean install" +
+ "&& mkdir /asset-output/lib" +
+ "&& cp target/powertools-examples-cloudformation-*.jar /asset-output/lib"
+ );
+ BundlingOptions bundlingOptions = BundlingOptions.builder()
+ .command(functionPackagingInstructions)
+ .image(Runtime.JAVA_11.getBundlingImage())
+ .volumes(singletonList(
+ // Mount local .m2 repo to avoid download all the dependencies again inside the container
+ DockerVolume.builder()
+ .hostPath(System.getProperty("user.home") + "/.m2/")
+ .containerPath("/root/.m2/")
+ .build()
+ ))
+ .user("root")
+ .outputType(NOT_ARCHIVED)
+ .build();
+
+ Function helloWorldFunction = new Function(this, "HelloWorldFunction", FunctionProps.builder()
+ .runtime(Runtime.JAVA_11)
+ .code(Code.fromAsset("../../", AssetOptions.builder().bundling(bundlingOptions)
+ .build()))
+ .handler("helloworld.App::handleRequest")
+ .memorySize(512)
+ .timeout(Duration.seconds(20))
+ .environment(Collections
+ .singletonMap("JAVA_TOOL_OPTIONS", "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"))
+ .build());
+ helloWorldFunction.addToRolePolicy(new PolicyStatement(PolicyStatementProps.builder()
+ .effect(Effect.ALLOW)
+ .actions(Arrays.asList("s3:GetLifecycleConfiguration",
+ "s3:PutLifecycleConfiguration",
+ "s3:CreateBucket",
+ "s3:ListBucket",
+ "s3:DeleteBucket"))
+ .resources(singletonList("*")).build()));
+
+ String bucketName = (String) this.getNode().tryGetContext("BucketNameParam");
+
+ Map crProperties = new HashMap<>();
+ crProperties.put("BucketName", bucketName);
+ CustomResource.Builder
+ .create(this, "HelloWorldCustomResource")
+ .serviceToken(helloWorldFunction.getFunctionArn())
+ .properties(crProperties)
+ .build();
+
+ }
+}
diff --git a/examples/powertools-examples-cloudformation/infra/sam/events/create_event.json b/examples/powertools-examples-cloudformation/infra/sam/events/create_event.json
new file mode 100644
index 000000000..26ef0a03b
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/sam/events/create_event.json
@@ -0,0 +1,12 @@
+{
+ "RequestType": "Create",
+ "ResponseURL": "http://pre-signed-S3-url-for-response",
+ "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid",
+ "RequestId": "unique id for this create request",
+ "ResourceType": "Custom::TestResource",
+ "ResourceProperties": {
+ "BucketName": "test-bucket-20230307-1",
+ "RetentionDays" : 10,
+ "StackName": "MyStack"
+ }
+}
diff --git a/examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json b/examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json
new file mode 100644
index 000000000..d18fdd3e4
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/sam/events/delete_event.json
@@ -0,0 +1,14 @@
+{
+ "RequestType": "Delete",
+ "ResponseURL": "http://pre-signed-S3-url-for-response",
+ "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid",
+ "RequestId": "unique id for this create request",
+ "ResourceType": "Custom::TestResource",
+ "LogicalResourceId": "MyTestResource",
+ "PhysicalResourceId": "test-bucket-20230307-1",
+ "ResourceProperties": {
+ "BucketName": "test-bucket-20230307-1",
+ "RetentionDays" : 10,
+ "StackName": "MyStack"
+ }
+}
diff --git a/examples/powertools-examples-cloudformation/infra/sam/events/update_event.json b/examples/powertools-examples-cloudformation/infra/sam/events/update_event.json
new file mode 100644
index 000000000..5a5ae2e3f
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/sam/events/update_event.json
@@ -0,0 +1,14 @@
+{
+ "RequestType": "Update",
+ "ResponseURL": "http://pre-signed-S3-url-for-response",
+ "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid",
+ "RequestId": "unique id for this create request",
+ "ResourceType": "Custom::TestResource",
+ "LogicalResourceId": "MyTestResource",
+ "PhysicalResourceId": "test-bucket-20230307-1",
+ "ResourceProperties": {
+ "BucketName": "test-bucket-20230307-1",
+ "RetentionDays" : 100,
+ "StackName": "MyStack"
+ }
+}
diff --git a/examples/powertools-examples-cloudformation/infra/sam/template.yaml b/examples/powertools-examples-cloudformation/infra/sam/template.yaml
new file mode 100644
index 000000000..a7ce4adf1
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/infra/sam/template.yaml
@@ -0,0 +1,50 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: >
+ powertools-examples-cloudformation
+
+ Sample SAM Template for powertools-examples-cloudformation
+
+Globals:
+ Function:
+ Timeout: 20
+
+Parameters:
+ BucketNameParam:
+ Type: String
+
+Resources:
+ HelloWorldCustomResource:
+ Type: AWS::CloudFormation::CustomResource
+ Properties:
+ ServiceToken: !GetAtt HelloWorldFunction.Arn
+ BucketName: !Ref BucketNameParam
+
+ HelloWorldFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ CodeUri: ../../
+ Handler: helloworld.App::handleRequest
+ Runtime: java11
+ Architectures:
+ - x86_64
+ MemorySize: 512
+ Policies:
+ - Statement:
+ - Sid: bucketaccess1
+ Effect: Allow
+ Action:
+ - s3:GetLifecycleConfiguration
+ - s3:PutLifecycleConfiguration
+ - s3:CreateBucket
+ - s3:ListBucket
+ - s3:DeleteBucket
+ Resource: '*'
+ Environment:
+ Variables:
+ JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1
+
+Outputs:
+ HelloWorldFunction:
+ Description: "Hello World Lambda Function ARN"
+ Value: !GetAtt HelloWorldFunction.Arn
diff --git a/examples/powertools-examples-cloudformation/pom.xml b/examples/powertools-examples-cloudformation/pom.xml
new file mode 100644
index 000000000..198a85894
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/pom.xml
@@ -0,0 +1,219 @@
+
+ 4.0.0
+
+ software.amazon.lambda.examples
+ 1.17.0-SNAPSHOT
+ powertools-examples-cloudformation
+ jar
+
+ AWS Lambda Powertools for Java library Examples - CloudFormation
+
+
+ 2.20.0
+ 1.8
+ 1.8
+ true
+ 1.2.2
+ 3.11.2
+ 2.20.102
+
+
+
+
+ software.amazon.awssdk
+ bom
+ ${aws.sdk.version}
+ pom
+ import
+
+
+
+
+
+
+ com.amazonaws
+ aws-lambda-java-core
+ ${lambda.core.version}
+
+
+ com.amazonaws
+ aws-lambda-java-events
+ ${lambda.events.version}
+
+
+ software.amazon.lambda
+ powertools-cloudformation
+ ${project.version}
+
+
+ software.amazon.lambda
+ powertools-logging
+ ${project.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j.version}
+
+
+ software.amazon.awssdk
+ s3
+
+
+ software.amazon.awssdk
+ netty-nio-client
+
+
+ software.amazon.awssdk
+ apache-client
+
+
+
+
+ software.amazon.awssdk
+ apache-client
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.apache.logging.log4j
+ log4j-jcl
+ ${log4j.version}
+
+
+
+
+
+
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+ 1.13.1
+
+
+ ${maven.compiler.target}
+ ${maven.compiler.target}
+
+
+ software.amazon.lambda
+ powertools-logging
+
+
+
+
+
+
+ compile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.0
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
+
+ com.github.edwgiz
+ maven-shade-plugin.log4j2-cachefile-transformer
+ 2.15
+
+
+
+
+
+
+
+
+ jdk8
+
+ (,11)
+
+
+ 1.9.7
+
+
+
+
+ org.aspectj
+ aspectjtools
+ ${aspectj.version}
+
+
+
+
+
+
+
+ dev.aspectj
+ aspectj-maven-plugin
+ ${aspectj.plugin.version}
+
+
+ ${maven.compiler.target}
+ ${maven.compiler.target}
+
+
+ software.amazon.lambda
+ powertools-logging
+
+
+
+
+
+
+ compile
+ test-compile
+
+
+
+
+
+
+ org.aspectj
+ aspectjtools
+ ${aspectj.version}
+
+
+
+
+
+
+
+
+
diff --git a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java
new file mode 100644
index 000000000..c7744cd5a
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java
@@ -0,0 +1,164 @@
+package helloworld;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import software.amazon.awssdk.awscore.exception.AwsServiceException;
+import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.core.waiters.WaiterResponse;
+import software.amazon.awssdk.http.apache.ApacheHttpClient;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.*;
+import software.amazon.awssdk.services.s3.waiters.S3Waiter;
+import software.amazon.lambda.powertools.cloudformation.AbstractCustomResourceHandler;
+import software.amazon.lambda.powertools.cloudformation.Response;
+
+import java.util.Objects;
+
+/**
+ * Handler for requests to Lambda function.
+ */
+
+public class App extends AbstractCustomResourceHandler {
+ private final static Logger log = LogManager.getLogger(App.class);
+ private final S3Client s3Client;
+
+ public App() {
+ super();
+ s3Client = S3Client.builder().httpClientBuilder(ApacheHttpClient.builder()).build();
+ }
+
+ /**
+ * This method is invoked when CloudFormation Creates the Custom Resource.
+ * In this example, the method creates an Amazon S3 Bucket with the provided `BucketName`
+ *
+ * @param cloudFormationCustomResourceEvent Create Event from CloudFormation
+ * @param context Lambda Context
+ * @return Response to send to CloudFormation
+ */
+ @Override
+ protected Response create(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) {
+ // Validate the CloudFormation Custom Resource event
+ Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null.");
+ Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null.");
+
+ log.info(cloudFormationCustomResourceEvent);
+ String bucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName");
+ log.info("Bucket Name {}", bucketName);
+ try {
+ // Create the S3 bucket with the given bucketName
+ createBucket(bucketName);
+ // Return a successful response with the bucketName as the physicalResourceId
+ return Response.success(bucketName);
+ } catch (AwsServiceException | SdkClientException e) {
+ // In case of error, return a failed response, with the bucketName as the physicalResourceId
+ log.error(e);
+ return Response.failed(bucketName);
+ }
+ }
+
+ /**
+ * This method is invoked when CloudFormation Updates the Custom Resource.
+ * In this example, the method creates an Amazon S3 Bucket with the provided `BucketName`, if the `BucketName` differs from the previous `BucketName` (for initial creation)
+ *
+ * @param cloudFormationCustomResourceEvent Update Event from CloudFormation
+ * @param context Lambda Context
+ * @return Response to send to CloudFormation
+ */
+ @Override
+ protected Response update(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) {
+ // Validate the CloudFormation Custom Resource event
+ Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null.");
+ Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null.");
+
+ log.info(cloudFormationCustomResourceEvent);
+ // Get the physicalResourceId. physicalResourceId is the value returned to CloudFormation in the Create request, and passed in on subsequent requests (e.g. UPDATE or DELETE)
+ String physicalResourceId = cloudFormationCustomResourceEvent.getPhysicalResourceId();
+ log.info("Physical Resource ID {}", physicalResourceId);
+
+ // Get the BucketName from the CloudFormation Event
+ String newBucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName");
+
+ // Check if the physicalResourceId equals the new BucketName
+ if (!physicalResourceId.equals(newBucketName)) {
+ // The bucket name has changed - create a new bucket
+ try {
+ // Create a new bucket with the newBucketName
+ createBucket(newBucketName);
+ // Return a successful response with the newBucketName
+ return Response.success(newBucketName);
+ } catch (AwsServiceException | SdkClientException e) {
+ log.error(e);
+ return Response.failed(newBucketName);
+ }
+ } else {
+ // Bucket name has not changed, and no changes are needed.
+ // Return a successful response with the previous physicalResourceId
+ return Response.success(physicalResourceId);
+ }
+ }
+
+ /**
+ * This method is invoked when CloudFormation Deletes the Custom Resource.
+ * NOTE: CloudFormation will DELETE a resource, if during the UPDATE a new physicalResourceId is returned.
+ * Refer to the Powertools Java Documentation for more details.
+ *
+ * @param cloudFormationCustomResourceEvent Delete Event from CloudFormation
+ * @param context Lambda Context
+ * @return Response to send to CloudFormation
+ */
+ @Override
+ protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) {
+ // Validate the CloudFormation Custom Resource event
+ Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null.");
+ Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), "PhysicalResourceId cannot be null.");
+
+ log.info(cloudFormationCustomResourceEvent);
+ // Get the physicalResourceId. physicalResourceId is the value provided to CloudFormation in the Create request.
+ String bucketName = cloudFormationCustomResourceEvent.getPhysicalResourceId();
+ log.info("Bucket Name {}", bucketName);
+
+ // Check if a bucket with bucketName exists
+ if (bucketExists(bucketName)) {
+ try {
+ // If it exists, delete the bucket
+ s3Client.deleteBucket(DeleteBucketRequest.builder().bucket(bucketName).build());
+ log.info("Bucket Deleted {}", bucketName);
+ // Return a successful response with bucketName as the physicalResourceId
+ return Response.success(bucketName);
+ } catch (AwsServiceException | SdkClientException e) {
+ // Return a failed response in case of errors during the bucket deletion
+ log.error(e);
+ return Response.failed(bucketName);
+ }
+ } else {
+ // If the bucket does not exist, return a successful response with the bucketName as the physicalResourceId
+ log.info("Bucket already deleted - no action");
+ return Response.success(bucketName);
+ }
+
+ }
+
+ private boolean bucketExists(String bucketName) {
+ try {
+ HeadBucketResponse headBucketResponse = s3Client.headBucket(HeadBucketRequest.builder().bucket(bucketName).build());
+ if (headBucketResponse.sdkHttpResponse().isSuccessful()) {
+ return true;
+ }
+ } catch (NoSuchBucketException e) {
+ log.info("Bucket does not exist");
+ return false;
+ }
+ return false;
+ }
+
+ private void createBucket(String bucketName) {
+ S3Waiter waiter = s3Client.waiter();
+ CreateBucketRequest createBucketRequest = CreateBucketRequest.builder().bucket(bucketName).build();
+ s3Client.createBucket(createBucketRequest);
+ WaiterResponse waiterResponse = waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build());
+ waiterResponse.matched().response().ifPresent(log::info);
+ log.info("Bucket Created {}", bucketName);
+ }
+}
\ No newline at end of file
diff --git a/examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml b/examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..8e8162128
--- /dev/null
+++ b/examples/powertools-examples-cloudformation/src/main/resources/log4j2.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ %d{dd MMM yyyy HH:mm:ss,SSS} [%p] <%X{AWSRequestId}> (%t) %c:%L: %m%n
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file