Using AWS CDK to Serverlessly Send Words of Encouragement
Tags: serverless, aws, cdk, lambda
Published: 2020-12-23 22:53:18

---
title: Using AWS CDK to Serverlessly Send Words of Encouragement
published: true
description: Build an AWS CDK app to send scheduled text messages containing words of encouragement to the people to you love!
tags: serverless, aws, cdk, lambda
//cover_image: https://direct_url_to_image.jpg
---

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.
```bash
mkdir encouraging-words && cd encouraging-words
cdk init app --language typescript
```

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.
```bash
npm install @aws-cdk/aws-sns`
```

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

```javascript
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
  }
}
```

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`

```bash
mkdir lambda
cd lambda
touch encouragingWords.js
```

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.

```bash
npm install @aws-sdk/client-sns
```

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

```javascript
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)];

}
```

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.
```bash
npm install @aws-cdk/aws-lambda`
```

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

```javascript
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
  }
}
```

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.
```bash
npm install @aws-cdk/aws-events @aws-cdk/aws-events-targets
```

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

```javascript
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))

  }
}

```
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:
```bash
cdk synth
```

To deploy:
```bash
cdk deploy
```

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:
```bash
cdk destroy
```

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:
{% github mmccall10/encouraging-words-app no-readme  %} 

Resources
[AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)
[CDK Worskhop](https://cdkworkshop.com)
[CDK Typescript Reference](https://docs.aws.amazon.com/cdk/api/latest/typescript/api/index.html)
[AWS SNS](https://aws.amazon.com/sns/)
[AWS Lambda](https://aws.amazon.com/lambda/)
[AWS Event Bridge](https://docs.aws.amazon.com/eventbridge/latest/userguide)