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.
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); | |
} | |
} |
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)); | |
} | |
} |
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 | |
} |
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(); | |
} | |
} | |
}); | |
} |
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.