DEV Community

Mike McCall
Mike McCall

Posted on

Using AWS CDK to Serverlessly Send Words of Encouragement

I am the father of 2 beautiful girls and as they grow older they are starting to feel the world push back. The emotional roller coaster of growing up can be both amazing and trying. I want them to know the world is full of strong women and they are just as valuable and capable as anyone. I figure some encouraging words from influential women could be nice little reminders.

To achieve this I plan to send a daily text message containing encouraging words from notable women. I have been exploring the various IaC (Infrastructure as Code) tools and AWS CDK was something I was keen on trying. Keep reading if you'd like to see how to achieve this using the AWS CDK with typescript.

First, let's break down the various services.

  1. SNS Topic to subscribe phone numbers to.
  2. Lambda for preparing a message and publishing it to the SNS topic
  3. Event Rule for triggering the lambda on a schedule.

That's it. Time to get started with a new CDK app.

With CDK installed run the following to create a new app.

mkdir encouraging-words && cd encouraging-words
cdk init app --language typescript
Enter fullscreen mode Exit fullscreen mode

Now that the CDK app is initialized we can start describing our stack. We'll start with the SNS topic.

Install the SNS CDK module.

npm install @aws-cdk/aws-sns`
Enter fullscreen mode Exit fullscreen mode

Open lib/encouraging-words-stack.ts and create a new topic.

import * as cdk from '@aws-cdk/core';
import { Topic } from '@aws-cdk/aws-sns'; // new

export class EncouragingWordsStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // topic to publish our message to
    const topic = new Topic(this, 'Topic') // new
  }
}
Enter fullscreen mode Exit fullscreen mode

Nice little one-liner!

Next, we need to create a function and add it to our CDK stack. We'll create a new directory in the root of the project called lambda and create a file for our function. I'll call it encouragingWords.js

mkdir lambda
cd lambda
touch encouragingWords.js
Enter fullscreen mode Exit fullscreen mode

This function needs to export a handler, generate a message to send, and publish the message to the SNS topic.

Inside of the lambda directory, we should install the Nodejs AWS SDK. To do so run npm init inside the lambda dir and install the SNS module once complete.

npm install @aws-sdk/client-sns
Enter fullscreen mode Exit fullscreen mode

Open up encouragingWords.js and add the following content. Tweak as desired.

const { SNSClient, PublishCommand } = require("@aws-sdk/client-sns");

exports.handler = async (_event) => {
  const sns = new SNSClient(process.env.REGION)

  const publishCommand = new PublishCommand({
    Message: `${encouragingMessage()}\n\nSent with ❤️ from Dad`,
    TopicArn: process.env.SNS_TOPIC_ARN,
  });

  await sns.send(publishCommand);
}

function encouragingMessage() {
  // Sample quotes. Add more as desired
  const messages = [
    '“It takes a great deal of courage to stand up to your enemies, but even more to stand up to your friends.” — J.K. Rowling',
    '“I have learned over the years that when one’s mind is made up, this diminishes fear; knowing what must be done does away with fear.” — Rosa Parks',
    '“The most beautiful thing you can wear is confidence.” — Blake Lively',
    '“Do you want to meet the love of your life? Look in the mirror.” — Byron Katie',
    '“Always be a first-rate version of yourself, instead of a second-rate version of somebody else.” — Judy Garland',
    '“The most effective way to do it, is to do it.” — Amelia Earhart',
    '“If you’re not making mistakes, then you’re not making decisions.” — Catherine Cook',
  ]
  return messages[Math.floor(Math.random() * messages.length)];

}
Enter fullscreen mode Exit fullscreen mode

Fairly straightforward. This function is importing the SNS module, initializing a new SNSClient, configuring the message and topic, then publishing it. The encouragingMessage() function returns a random message from the messages array. You might have noticed two environment variables, SNS_TOPIC_ARN and REGION. These will be set when the lambda construct is added to the CDK Stack.

Now let's describe the function with a lambda construct.

First, install the CDK lambda module.

npm install @aws-cdk/aws-lambda`
Enter fullscreen mode Exit fullscreen mode

Next, update the lib/encouraging-words-stack.ts with the following code.

import * as cdk from '@aws-cdk/core';
import { Topic } from '@aws-cdk/aws-sns';
import { Function, Code, Runtime } from '@aws-cdk/aws-lambda'; // new

export class EncouragingWordsStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // topic to publish our message to
    const topic = new Topic(this, 'Topic')

    // new
    const fn = new Function(this, 'Function', {
      runtime: Runtime.NODEJS_12_X,
      code: Code.fromAsset('lambda'),
      handler: 'encouragingWords.handler',
      environment: {
        SNS_TOPIC_ARN: topic.topicArn,
        REGION: 'us-west-2',
      }
    })

    topic.grantPublish(fn) // new
  }
}
Enter fullscreen mode Exit fullscreen mode

Again, fairly straightforward. A few things to note in the new Function construct. The code option is telling CDK to look in the lambda folder (relative to where the CDK command is run) and the handler is specifying the file and method. Finally, we set up our environment variables. Most notably the SNS_TOPIC_ARN. We are using the previously assigned topic variable to obtain this value. The code topic.grantPublish(fn) grants permission for the lambda function to publish to the SNS Topic. Very nice!

Lastly, we should schedule this. We don't want to manually run this lambda every time to send the message. That would defeat the purpose. Let's add a scheduled task and complete the CDK app.

Install the events and events targets modules.

npm install @aws-cdk/aws-events @aws-cdk/aws-events-targets
Enter fullscreen mode Exit fullscreen mode

Add the event rule to lib/encouraging-words-stack.ts.

import * as cdk from '@aws-cdk/core';
import { Topic } from '@aws-cdk/aws-sns';
import { Function, Code, Runtime } from '@aws-cdk/aws-lambda';
import { Rule, Schedule } from '@aws-cdk/aws-events'; // new
import { LambdaFunction } from '@aws-cdk/aws-events-targets'; // new

export class EncouragingWordsStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // topic to publish our message to
    const topic = new Topic(this, 'Topic')

    // triggered function for sending message
    const fn = new Function(this, 'Function', {
      runtime: Runtime.NODEJS_12_X,
      code: Code.fromAsset('lambda'),
      handler: 'encouragingWords.handler',
      environment: {
        SNS_TOPIC_ARN: topic.topicArn,
        REGION: 'us-west-2',
      }
    })

    // allow function to publish to sns topic
    topic.grantPublish(fn)

    // new
    const rule = new Rule(this, 'Cron', {
      schedule: Schedule.cron({ minute: '0', hour: '16' })
    })

    //new
    rule.addTarget(new LambdaFunction(fn))

  }
}

Enter fullscreen mode Exit fullscreen mode

This is so readable it's hard to explain any better than the code reads. We create a new event Rule that is scheduled to run at hour 16 and minute 0. The scheduled cron runs in the UTC timezone, so for my purposes, this code will run at 8 am PST.

We also add the lambda function as an event target to the event rule with rule.addTarget(new LambdaFunction(fn)). Very nice!

Time to deploy!

To view the cloudformation template run:

cdk synth
Enter fullscreen mode Exit fullscreen mode

To deploy:

cdk deploy
Enter fullscreen mode Exit fullscreen mode

We are ALMOST done. The stack is deployed and works as expected however we need to subscribe phone numbers to the topic!

Visit the SNS subscriptions section in the AWS console. Click the Create Subscription button. Select the "EncouragingWordsStack-Topic" arn as the Topic and SMS as the Protocol. Add the phone number you'd like to send the message to as the endpoint. Repeat per number.

To test this out invoke the function manually from the CLI or the console.

To clean up simply run:

cdk destroy
Enter fullscreen mode Exit fullscreen mode

This simple app is my first foray into CDK and I really enjoyed it. It's easy to read and the type system makes IaC so comfortable to work with. I was able to achieve this with minimal code liability in a very short amount of time. The cloud has never felt so accessible!

You can view the repo for this project here:

Resources
AWS CDK
CDK Worskhop
CDK Typescript Reference
AWS SNS
AWS Lambda
AWS Event Bridge

Top comments (0)