life is too short for a diary




Fri 25 Jun 2021

Automate creation of AWS Stack

Tags: aws cdk cloudformation typescript tutorial

AWS CDK is a great framework to programmatically deploy cloudformation stack. If you are unfamiliar with AWS CDK, I would recommend first to check out Getting started with AWS CDK.

One of the pet peeves I have while designing cloudformation template is redundancy. I have to manually copy-paste the same properties across multiple resources. I wanted to leverage AWS CDK to create an interface that would allow tto dynamically create cloudformation. For the interface, I harked back to the good old CSV file.

For a simple example, let’s consider a simple cloudformation template

Parameters:
  Environment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - qa
      - prod
    Description: Enter your environment
  Project:
    Type: String
    Default: version
    Description: Enter your version

What if we have to maintain 100 parameters, resources, etc in our project. Soon it will become tedious to maintain multiple resources. Instead, we create a class called Parameter that we instantiate multiple times using different parameters. Here is a class diagram for our Parameter class.

class diagram

Using typescript, we can define the class as

export class Parameter extends cdk.Construct {
private name : string;
private default_value: string;
private desc : string;
private allowed_values: string;
constructor(scope: cdk.Construct,
name: string,
default_value: string,
desc: string,
allowed_values: string) {
super(scope, parameter_name);
this.name = name;
this.default_value = default_value;
this.desc = desc;
this.allowed_values = allowed_values;
}
create() : void {
let param : cdk.CfnParameter;
if (this.allowed_values && this.allowed_values.trim()) {
param = new cdk.CfnParameter(this, this.name , {
type: "String",
default: this.default_value,
allowedValues: this.allowed_values.split("|").map(function (value : string){ return value.trim()}),
desc : this.desc
});
} else {
param = new cdk.CfnParameter(this, this.name , {
type: "String",
default: this.default_value,
description : this.desc
});
}
param.overrideLogicalId(this.name);
}
}
view raw projectCdk4.ts hosted with ❤ by GitHub


We will create a `CSV` file under folder `resources` which would contain information for each instance of our class.

resource create category default description allowed_values
Environment yes parameter dev Enter your environment dev | qa | prod
Project yes parameter version1 Enter version

First, we will read our CSV file into our project. We would need to install an additional library to read the file.

$ npm i --save csvtojson

We will edit the lib/project_cdk-stack.ts file.

import * as cdk from '@aws-cdk/core';
import * as lib from './helpers';
export class ProjectCdkStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const filename = "resources/cdkinput.csv";
lib.read_file(filename).then(result => {
if (result && result.length) {
lib.generateTemplate(result, this);
} else {
console.log("Input csv is empty. Exiting...");
}
}).catch(error => console.log(error));
}
}
view raw projectCdk1.ts hosted with ❤ by GitHub

Then we will create lib/helpers.ts file.

import { ProjectCdkStack } from './project_cdk-stack';
const csvread = require('csvtojson');
export async function read_file(filename : string) {
const jsonArray: any = await csvread().fromFile(filename);
return jsonArray;
}
export function generateTemplate(result : any, obj : ProjectCdkStack){
//TODO implement
}
view raw projectCdk2.ts hosted with ❤ by GitHub

Our code till now will not generate any cloudformation. However, we can test it for any compile errors

$ cdk synth --quiet

Next, we will implement our generateTemplate function.

import { ProjectCdkStack } from './project_cdk-stack';
const csvread = require('csvtojson');
import * as cdk from '@aws-cdk/core';
export function generateTemplate(result : any, obj : ProjectCdkStack){
result.forEach((item:any) => {
if (item["create"] === "yes"){
let object : any;
switch(item["category"]){
case 'parameter':
object = new Parameter(obj,
item["resource"],
item["default"],
item["description"],
item["allowed_values"]);
break;
default:
break;
}
if (object) {
object.create();
}
}
});
}
view raw projectCdk3.ts hosted with ❤ by GitHub


Lastly, we can deploy our cloudformation stack using

$ cdk deploy

In the next article, we will update our code to add additional classes to handle resources, output, etc for our cloudformation template.


comments powered by Disqus