Introduction to AWS CDK
In the introduction to this chapter, we covered a basic history of IaC and how these software tools lack the expressiveness of modern programming languages. AWS without a doubt is the frontrunner in the cloud industry, and its answer to difficult infrastructure setup is CDK.
In AWS’s own definition, CDK is an open source software development framework—notice how AWS is not calling it an infrastructure provisioning framework—that defines AWS resources for a cloud application using familiar programming languages.
AWS’s definitiomeans, for example, that instead of defining the EC2 instance using declarative configuration files as we did with Terraform’s HashiCorp Configuration Language (HCL) in the intro, you can define the same instance as the following:
import * as cdk from '@aws-cdk/core' import * as ec2 from '@aws-cdk/aws-ec2' export class MyAppStack extends Stack { public readonly default_vpc: IVpc; public readonly my_virtual_machine: Instance; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); this.default_vpc = Vpc.fromLookup(this, 'VPC', { // This will get the default account VPC isDefault: true, }); this.my_virtual_machine = new Instance(this, 'single-intance', { // The type of instance to deploy (e.g. a 't2.micro') instanceType: new InstanceType('t2.micro'), // The type of image to use for the instance machineImage: new AmazonLinuxImage(), // A reference to the object representing the VPC vpc: this.default_vpc, }); } }
Note
Don’t worry about running the code in this section; we will get into that after we’ve completed setting up your local machine for CDK development.
You might (rightfully) think that in terms of lines of code, this doesn’t really save you much time. It’s more or less the same amount of configuration we declare. Let’s see what happens if we want to declare 100 EC2 instances:
import * as cdk from '@aws-cdk/core' import * as ec2 from '@aws-cdk/aws-ec2' export class MyAppStack extends Stack { public readonly default_vpc: IVpc; public readonly my_virtual_machines: Instance[]; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); this.default_vpc = Vpc.fromLookup(this, 'VPC', { isDefault: true, }); this.my_virtual_machines = [...Array(100).keys()].map( i => new Instance(this, `single-intance-${i}`, { instanceType: new InstanceType('t2.micro'), machineImage: new AmazonLinuxImage(), vpc: this.default_vpc, }), ); } }
Are you excited yet? Let’s now imagine we want to deploy 100 EC2 instances and we want every odd one to be a t2.large
instance type instead. Let’s also throw in a Simple Storage Service (S3) bucket too and give all the programs that run within our VMs read access to some files in this S3 bucket. Perhaps there is a shell script in there that we want them to run as soon as they spin up. The following CDK code does exactly that:
import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib/core'; import { Instance, InstanceType, AmazonLinuxImage, Vpc, IVpc } from 'aws-cdk-lib/aws-ec2'; import { Bucket } from 'aws-cdk-lib/aws-s3'; import { Construct } from 'constructs'; export class MyAppStack extends Stack { public readonly bucket: Bucket; public readonly default_vpc: IVpc; public readonly my_virtual_machines: Instance[]; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); this.default_vpc = Vpc.fromLookup(this, 'VPC', { isDefault: true, }); this.my_virtual_machines = [...Array(100).keys()].map( i => new Instance(this, `single-intance-${i}`, { instanceType: new InstanceType(i % 2 ? 't2.large' : 't2.micro'), machineImage: new AmazonLinuxImage(), vpc: this.default_vpc, }), ); this.bucket = new Bucket(this, 'MyBucket', { removalPolicy: RemovalPolicy.DESTROY, }); this.my_virtual_machines.forEach(virtual_machine => { this.bucket.grantRead(virtual_machine); }); } }
As you can see, CDK is an extremely versatile IaC framework. It unleashes the power of programming languages and compilers to declare highly complex AWS infrastructure with code that is, compared to the alternatives, easily readable and extensible. We can loop, map, reference, write conditions, and—more importantly—use the helper functions CDK provides to simplify the previously daunting task of implementing IaC.
This is a revolutionary concept. Full stack developers no longer must learn convoluted domain-specific languages (DSLs) to interact with AWS services; they can bring their current development skills to the world of cloud provisioning. There is no longer a context switch between development and DevOps. You no longer need to leave your IDE, and you can build and deploy the CDK infrastructure using common tools you’ve previously used—for example, npm
.
Additionally, CDK has powerful customization and component sharing baked in. You can create batches of stacks, with your own set of governance and compliance requirements as reusable components. A set of certain EC2 machines with AWS Systems Manager parameter store configuration, security groups, certificates, and load balancers that is common within your stack can then be defined as a component (or in CDK terms, a construct) and then initialized and reused as many times as you desire. This is the true power of CDK, and it unleashes countless new opportunities for developers, as we will explore in the following chapters.
Till now, we’ve learned why CDK is cool. We hope you grow to love the toolset just as much as I do as you apply it to your own programming stack. Let’s now get into the good bits and start off by setting up your local machine for CDK development.