Today, I discovered yet again that there are countless ways to tackle a single task as a developer.
I was tasked with automating a workflow that involved an AWS Lambda Function triggered by an SNS event source. The goal was to publish a message to an SNS topic in a different AWS account when the status of a CloudFormation stack updated.
We use AWS CDK for infrastructure as code (IAC). While exploring the documentation and blog posts, I found that there is no direct equivalent of the Notification Policy in CloudFormation to publish notifications to an SNS topic on a CloudFormation stack status change. Instead, there are several common patterns to achieve this. Let's start with solution diagram. Here's a simplified version of the architecture diagram of what I implemented:
If this is what you are looking for, you can simply achieve it in 3 different ways:
Using AWS Event Bridge
- Create SNS Topic as a Stack B CDK resource:
import { Topic } from "aws-cdk-lib/aws-sns";
const SNSTopic = new Topic(this, "SNS_TOPIC_ID", {
displayName: "YOUR DISPLAY NAME",
});
_Note: you need to add an event source to your function or any other resources that is going to subscribe to this topic. in my case I needed to configure a lambda event source as _bellow:
MyFunction.addEventSource(new SnsEventSource(SNSTopic));
- Add Event Rule
new Rule(this, "Trigger", {
eventPattern: {
source: ["aws.cloudformation"],
detailType: ["CloudFormation Stack Status Change"],
detail: {
eventName: ["CREATE_COMPLETE", "UPDATE_COMPLETE", "DELETE_COMPLETE"],
requestParameters: {
stackName: [this.stackName],
},
},
},
targets: [new SnsTopic(SNSTopic)],
});
Using AWS Custom Resources
Second approach to achieve this is by using AWS Custom Resources.
- Create an AWS Custom Resource within the Stack B CDK:
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources";
const Trigger = new AwsCustomResource(this, "TriggerOnSuccess", {
onUpdate: {
service: "SNS",
action: "publish",
parameters: {
TopicArn: "YOUR_TOPIC_ARN",
Message: "Stack updated successfully",
},
physicalResourceId: PhysicalResourceId.of("TriggerOnSuccess"),
},
onDelete: {
service: "SNS",
action: "publish",
parameters: {
TopicArn: "YOUR_TOPIC_ARN",
Message: "Stack deleted successfully",
},
physicalResourceId: PhysicalResourceId.of("TriggerOnSuccess"),
},
policy: AwsCustomResourcePolicy.fromStatements( [new PolicyStatement({
actions: ["sns:Publish"],
effect: Effect.ALLOW,
resources: [SNSTopic.topicArn],
})]),
});
- Add Dependency order so that CDK doesn't return Dependency Cycle error
Trigger.node.addDependency(YOUR_FUNCTION);
Trigger.node.addDependency(SNSS_TOPIC);
Using AWS Custom Resource with Lambda Invoke Action
There is also a 3rd solution for this as well which I am not a big fan of it and that is. I personally prefer to use fan out approach, to populate events to Lambda via an "Event Service" such as SNS or EventBridge. If you look for a simplified Custom Resource, here is what you should update your AWS Custom Resource to:
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources";
const Trigger = new AwsCustomResource(this, "TriggerOnSuccess", {
onUpdate: {
service: "Lambda",
action: "invoke",
parameters: {
FunctionName: "YOUR_FUNCTION_ARN",
InvokationType: "Event"
},
physicalResourceId: PhysicalResourceId.of("TriggerOnSuccess"),
},
onDelete: {
service: "Lambda",
action: "invoke",
parameters: {
FunctionName: "YOUR_FUNCTION_ARN",
InvokationType: "Event"
},
physicalResourceId: PhysicalResourceId.of("TriggerOnSuccess"),
},
policy: AwsCustomResourcePolicy.fromStatements( [new PolicyStatement({
actions: ["lambda:InvokeFunction"],
effect: Effect.ALLOW,
resources: [YOUR_FUNCTION_ARN],
})]),
});
This concludes our brief discussion. While I'm still hopeful about discovering if CDK offers an API for configuring Stack Notification Options, I wanted to share these workarounds in the meantime.