Skip to content

Instantly share code, notes, and snippets.

@dmost714
Created December 11, 2022 20:33
Show Gist options
  • Save dmost714/dde2a869cf9e3adfcfcb6d4aae6b6940 to your computer and use it in GitHub Desktop.
Save dmost714/dde2a869cf9e3adfcfcb6d4aae6b6940 to your computer and use it in GitHub Desktop.
Custom SNS Topic and subscriptions, triggered by Lambda in AWS Amplify
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns"
const sns = new SNSClient({ region: process.env.REGION })
export const alertNotice = async (subject: string, message: string) => {
try {
const params = {
TopicArn: process.env.ALERT_NOTICE_SNS_TOPIC_ARN,
Subject: subject,
Message: message
}
console.log('ALERT NOTICE: Sending SNS message - ', subject)
const command = new PublishCommand(params)
const result = await sns.send(command)
console.log('SNS result', result)
} catch (error) {
console.log('SNS error', error)
}
}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"env": {
"Type": "String"
}
},
"Resources": {
"SNSTOPIC": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": {
"Fn::Join": [
"",
[
"yetanothersaas-sns-topic-alert",
{
"Fn::Select": [
3,
{
"Fn::Split": [
"-",
{
"Ref": "AWS::StackName"
}
]
}
]
},
"-",
{
"Ref": "env"
}
]
]
}
}
},
"SNSSUBSCRIPTIONEMAILUSERTWO": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"Protocol": "email",
"TopicArn": {
"Ref": "SNSTOPIC"
},
"Endpoint": "user_two@example.com"
}
},
"SNSSUBSCRIPTIONEMAILUSERONE": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"Protocol": "email",
"TopicArn": {
"Ref": "SNSTOPIC"
},
"Endpoint": "user_one@example.com"
}
}
},
"Outputs": {
"SNSTopicArn": {
"Value": {
"Ref": "SNSTOPIC"
}
}
},
"Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"9.2.1\",\"stackType\":\"custom-customCloudformation\",\"metadata\":{}}"
}
{
"auth": {
// ...
},
"function": {
"yetanothersaasSomeLambda": {
// ...
},
"yetanothersaasAnotherLambda": {
"build": true,
"providerPlugin": "awscloudformation",
"service": "Lambda",
"dependsOn": [
{
// ...
},
{
"category": "custom",
"resourceName": "AlertNotice",
"attributes": [
"SNSTopicArn"
]
}
]
}
},
"storage": {
// ...
},
"custom": {
"AlertNotice": {
"service": "customCloudformation",
"providerPlugin": "awscloudformation",
"dependsOn": []
}
},
"api": {
// ...
}
}
[
{
"Action": [
"SNS:Publish"
],
"Resource": [
{
"Ref": "customAlertNoticeSNSTopicArn"
}
]
}
]
# SNS ALERTS from a Lambda in AWS Amplify
Name the custom resource based on what it is used for.
Here I want to email "alerts" to myself, so I `amplify add custom` and give the custom resource the name "AlertNotice".
In the `amplify/backend/custom/AlertNotice`, define the custom resources in the `AlertNotice-cloudformation.json` file, including the ARN as an output.
In the `amplify/backend/backend-config.json` file, add the new custom resource as a dependency of any lambda that will use it.
Run `amplify env checkout dev` any time you manually change the backend-config.json file or you will see misleading errors when you do `amplify push`.
In any lambda that will trigger the SNS topic, add permissions in that lambda's `custom-policies.json` file.
In the lambda's `yetanothersaasAnotherLambda-cloudformation-template.json` cloudformation template, add the custom property to properties and the lambda's `ENVIRONMENT` variables so you can reference it.
Then in the Lambda code, trigger the SNS topic.
See below for all the files that get touched and samples of their contents.
Much more detail in this GIST:
How to expose an Amplify Custom category as an ENVIRONMENT variable in an Amplify Lambda
https://gist.github.com/dmost714/e914c865e54f9e77a1e49cda3dc78b20
const requireEnvironmentVars = () => {
[
"API_PIXELSTOPOSTCARDS_GRAPHQLAPIENDPOINTOUTPUT",
"API_PIXELSTOPOSTCARDS_GRAPHQLAPIIDOUTPUT",
"ENV",
"REGION",
"ALERT_NOTICE_SNS_TOPIC_ARN"
].map(envvar => {
if (!process.env[envvar]) {
throw new Error(`UNDEFINED ENVIRONMENT VAR: ${envvar}:`)
}
console.log(`[ENVIRONMENT] ${envvar}: ${process.env[envvar]}`)
})
}
export const handler: APIGatewayProxyHandler = async (event) => {
console.log('EVENT', JSON.stringify(event, null, 2))
requireEnvironmentVars()
try {
return await handleEvent(event)
} catch (error) {
console.log('FATAL', error)
const msg = `
ERROR:
${error}
Check yetanothersaasAnotherLambda logs.`
await alertNotice('Subject of alert', msg)
throw error
}
}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"9.2.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
"Parameters": {
"CloudWatchRule": {
"Type": "String",
"Default": "NONE",
"Description": " Schedule Expression"
},
"deploymentBucketName": {
"Type": "String"
},
"env": {
"Type": "String"
},
"s3Key": {
"Type": "String"
},
"customAlertNoticeSNSTopicArn": {
"Type": "String"
}
},
"Conditions": {
"ShouldNotCreateEnvResources": {
"Fn::Equals": [
{
"Ref": "env"
},
"NONE"
]
}
},
"Resources": {
"LambdaFunction": {
"Type": "AWS::Lambda::Function",
"Metadata": {
"aws:asset:path": "./src",
"aws:asset:property": "Code"
},
"Properties": {
"Code": {...},
"Handler": "index.handler",
"FunctionName": {...},
"Environment": {
"Variables": {
"ENV": {
"Ref": "env"
},
"REGION": {
"Ref": "AWS::Region"
},
"ALERT_NOTICE_SNS_TOPIC_ARN": {
"Ref": "customAlertNoticeSNSTopicArn"
}
}
},
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Runtime": "nodejs18.x",
"Architectures": [
"arm64"
],
"Layers": [],
"MemorySize": 256,
"Timeout": 180
}
},
"LambdaExecutionRole": {...},
"lambdaexecutionpolicy": {...},
"AmplifyResourcesPolicy": {...},
"CustomLambdaExecutionPolicy": {...}
},
"Outputs": {...}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment