Creating an Instance Scheduler using AWS CDK

yia333

Yi Ai

Posted on August 14, 2020

Creating an Instance Scheduler using AWS CDK

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

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:

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.

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.

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.

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 to update auto-scaling group and updateDBClusterFn to start/stop RDS instances.


Next, we will continue by adding our step function definitions. Add the following code to lib/lambda-stack.js .

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.

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

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

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.

💖 💪 🙅 🚩
yia333
Yi Ai

Posted on August 14, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related