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.