Creating an Instance Scheduler using AWS CDK
Tags: serverless, aws, cdk, iac
Published: 2020-08-14 13:15:00

---
title: Creating an Instance Scheduler using AWS CDK
published: true
description: Building a Serverless Workflow using AWS Cloud Development Kit
tags: serverless, AWS, CDK, iac
//cover_image: https://direct_url_to_image.jpg
---

The AWS CDK is a software development framework used to define cloud infrastructure as code and provision it through CloudFormation. The CDK integrates fully with AWS services and allows developers to use a high-level construct to define cloud infrastructure in code.

In this article, we will build a CDK version of the AWS EC2 Instance Scheduler solution that enables us to easily configure custom start and stop schedules for our Amazon EC2 and Amazon RDS instances.

## The Architecture
By the end of this article, we will have a pipeline to deploy a serverless solution that starts and stops Amazon EC2 instances in an autoscaling group and Amazon RDS instances based on a schedule.

Here’s the architecture diagram:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aw3q4ui6uegckxkoadq1.png)

Through the article we will be covering the following actions:
- Deploying an AWS Step Function with two parallel tasks.
- Creating an SNS topic to send notifications.
- Creating a Cloudeatch event rule which will trigger Step Function based on a schedule.
- Creating a CI/CD pipeline with CodeBuild and CodePipeline.

## Prerequisites
To deploy the CDK application, there are a few prerequisites that need to be met:
- Setup an AWS account.
- Install the latest [aws-cli](https://docs.aws.amazon.com/cli/latest/userguide/installing.html).
- Install [AWS CDK CLI](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html).
- [Deploy a multi-AZ WordPress website with RDS (optional)](https://github.com/yai333/cdkexample/blob/master/WordPress_Multi_AZ.template).

## Before you begin
First, create an AWS CDK project by entering the following commands at the command line.

```
$mkdir cdk-sample 
$cd cdk-sample cdk init --language=javascript
```
Next, install CDK modules, we will use the below modules in our project.
```
$npm install @aws-cdk/core @aws-cdk/aws-codebuild @aws-cdk/aws-codepipeline @aws-cdk/aws-codepipeline-actions @aws-cdk/aws-events @aws-cdk/aws-events-targets @aws-cdk/aws-iam @aws-cdk/aws-lambda @aws-cdk/aws-sns @aws-cdk/aws-ssm @aws-cdk/aws-stepfunctions @aws-cdk/aws-stepfunctions-tasks
```
We need to add a stage parameter because we want to deploy our stack to multiple stages (dev and production).
Let’s add `DEP_ENV` in `bin/cdk-sample.js`.
{% gist https://gist.github.com/yai333/a57064fee82aeb01466168c43b1a0cd0 %}

## Define the base stack class
Now let’s define our base stack class. In the base stack class, we’ll add the code to instantiate three separate stacks: SNS stack, StepFunction stack, and CodePipeline stack.

Make the code look like the following.
{% gist https://gist.github.com/yai333/5823664f851831118096ad47b18bedb2 %}

We can set the optional environment variable MANUAL_DEPLOY to true if we want to deploy only step function locally.
```
$export MANUAL_DEPLOY=true && cdk deploy Stepfunction-dev
```
## Define SNS stack
We’ll add the code (`lib/sns-stack.js`) to create an SNS topic and subscribe to an email to the created topic.
{% gist https://gist.github.com/yai333/fe983662ad1d6bba459ae0ed34ab6065 %}

## Define Step function and Lambdas
Now we’ll expand our `lib/lambda-stack.js` file and add Lambda functions and Step function.

In this example, we will create two lambda functions, [updateScalingGroupFn](https://github.com/yai333/cdkexample/tree/master/src) to update auto-scaling group and [updateDBClusterFn](https://github.com/yai333/cdkexample/tree/master/src) to start/stop RDS instances.
{% gist https://gist.github.com/yai333/f6567f5045766d7fa468dfb7812b795d %}
Next, we will continue by adding our step function definitions. Add the following code to `lib/lambda-stack.js` .
{% gist https://gist.github.com/yai333/11bc2fcadf2508b16a3ff45b04d8f978 %}

As we can see, for each Lambda there is a corresponding taskEvent. We only want to parallelize the Lamba workload (one task for the ec2 autoscaling group and another task for RDS instances). Hence we will add a chain to our workflow by calling the `branch()` function.

To send a notification to the Amazon SNS topic if the Lambda fails, we can add an error handling chain to the workflow by calling `addCatch()` function.

Finally, let’s define a CloudWatch Events rule, CloudWatch is triggering execution of state machine workflow every day at 7 am and 6 pm (UTC). Add the following code to `lib/lambda-stack.js`.

```
new Rule(this, 'Rule', {
  schedule: Schedule.expression('cron(0 7,18 * * ? *)'),
  targets: [new SfnStateMachine(toggleAWSServices)],
});
```

## Define pipeline stack
We define our final stack, codepiepeline-stack. It has a source Action targeting the Github repository, a build Action that builds previously defined stacks, and finally a deploy Action that uses AWS CloudFormation. It takes the Cloudformation template (CDK.out/*.template.json) generated by the AWS CDK build action and passes it to AWS CloudFormation for deployment.

Create lib/codepipeline-stack.js and put the following code in it.
{% gist https://gist.github.com/yai333/0d1c64b3ab21c2d10ccb49d57f2924de %}

Next, create a dev branch and check the code into Git then push it to Github repo.

```
$git branch dev
$git checkout dev
$git add .
$git commit -m "xxxx"
$git push
```
## Deploying the pipeline
Now we can deploy the pipeline with multiple Stages.
Deploy pipeline to `dev` stage, source action targets the Github repository `dev` branch.

```
$export DEPLOY_ENV=dev && cdk deploy CodepipelienStack-dev
```
Deploy pipeline to the production stage, source action targets the Github repository master branch.
```
$export DEPLOY_ENV=production && cdk deploy CodepipelienStack-production
```

After the deployment finishes, we should have a three-stage pipeline that looks like the following.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/39pkmv11w82y55d7uhwb.png)

Once all stacks have deployed, we can explore it in the AWS Console and give it a try. Navigate to the Step Function in the console and click “Start execution”.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l7j6wjvqal5m8ov44nf9.png)

We should see that it passes. Let’s check the EC2 autoscaling group’s DesiredCapacity and RDS instance's status.
```
$aws rds describe-db-instances
"DBInstances": [
{
"DBInstanceIdentifier": "cxxxxxx",
"DBInstanceClass": "db.t2.small",
"Engine": "mysql",
"DBInstanceStatus": "stopped"
...
$aws rds describe-db-instances
"AutoScalingGroups": [
{
"AutoScalingGroupName": "cdk-sample-WebServerGroup-xxxxxx",
"MinSize": 0,
"MaxSize": 0,
"DesiredCapacity": 0,
...
```
Finally, let’s check the cloud watch event rule. We should see the cloud watch event rule looks like the following:
```
$aws events list-rules
{
"Rules": [
{
"Name": "StepfunctionStack-dev-Rulexxxxxxx",
"Arn": "arn:aws:events:ap-southeast-2:xxxxxxx:rule/StepfunctionStack-dev-Rulexxxxxxx-xxxxxxx",
"State": "ENABLED",
"ScheduleExpression": "cron(0 7,18 * * ? *)",
"EventBusName": "default"
}
  ]
}
```

That’s about it, Thanks for reading! You can find the complete project in my [GitHub repo](https://github.com/yai333/cdkexample).