Written by: Bojan Zivic, Senior Consultant
Traditionally, it was the task of engineers to build the infrastructure to a determined specification, whilst developers would deploy applications to said servers using their own set of tooling. Engineers would build manually via ClickOps (point and click through a menu) through an on-premise hypervisor like vSphere or HyperV, selecting core count, memory and allocating spacing, whilst developers script up their deployments leading to the usage of various languages, everything from Shell Script to Python/JavaScript/Ruby etc. Developers would store their code in version control systems like GIT, or prior to that SVN, while Engineers would rely on As-Build guides and vendor documentations.
Looking at only the above, it can be assumed that Dev deployment processes were faster, repeatable, and less prone to error. Code, including the deployment scripts, would be checked in a Git repository like GitLab, where the code is versioned and subjected to review by other developers. The same, originally could not be said for Infrastructure practices. This however has changed with the introduction of Infrastructure as Code(IaC).
IaC changed how Developers/Engineers look at infrastructure, it allowed infrastructure to be easily repeatable as the existing template would typically only require changes to unique names, and as per best practice Environment tagging. Take AWS for example with Cloudformation.
Although simple the above would create two VPCs, and if copied again, with the changes to Dev/Prod respectively, more and more of the same type of resource can be created. Extending beyond the simple VPC, an entire environment can be defined within a YAML (as per our example), or JSON spec, however this is where the maintainability becomes a factor.
Going from 7-10 lines of YAML to several hundred or a thousand lines makes maintaining the implemented Infrastructure much more difficult to do, a large codebase as is with any application, is a problem for Infrastructure as well. Tooling such as Ansible, Jinja2 have made this process easier, where environment specific variables are held invoked from separate files, and replace placeholder variables in the Cloudformation template. This does however add another layer of complexity/ Engineers would need to be trained up in either Ansible or Jinja2 and would be locked into a specific way of working, noting that the large codebase that was mentioned earlier, would still be present. With all the above in mind, AWS released a product that set a new ground for IaC, IaC 2.0 if you will, AWS Cloud Development Kit, On July 11th 2019, CDK was made GA.
CDK: What is it
CDK is a programmatic approach to IaC with the option of being built in multiple languages TypeScript, JavaScript, Python, Java, C#/.Net, and Go, and with these languages comes all the features of a complex programming language. CDK adds a programmatic layer on top of Cloudformation -what you generate via your CDK deployments are Cloudformation templates, but instead of a configuration like syntax where the logic is implicit in YAML/JSON, the logic instead is put on the code.
As a Developer or Engineer you would need to know one of the languages mentioned earlier to get started, as each of the AWS Services are tied to language specific packages and are pulled in via package managers such as pip or npm and with the right IDE, providing a IntelliSense approach to building your code, in the examples provided we have used VSCode as IDE of choice.
The iteration through code allowed for a much smaller codebase, in the example below we can see that 10 lines of CDK(written in Typescript) with the use of the forEach() method creates what is in Cloudformation 40.
Taking a deeper look, CDK achieves the above via the use of Constructs. Constructs are the core components of AWS CDK, a construct represents an AWS Service and defines everything AWS CloudFormation needs to create the component.
Constructs are viewed in two categories, L1 Low-level Constructs and L2 High-Level Constructs, with L1 closely representing the Cloudformation resources, and L2 which work in a similar manner however come with more defaults such as security standard i.e encryption, deletion policies already set and provide a method based approach where L1 does not.
Constructs can contain either a single resource like a Lambda Function, or a complete service containing a multitude of resources such as those found in aws-cdk/ecs-patterns. On using one of the ecs-patterns, a multitude of resources are generated. With the example below the ApplicationLoadBalanceFargateService Construct will generate:
- An ALB
- Listener
- ECS Cluster
- Fargate Service
- Task Definition
- Target Group
- Default Security group
- Default IAM Role and Policy
- Log Group
All from the single L2 Construct. A similar example from AWS with a VPC added creates well over 500+ lines of Cloudformation.
L2 High-level constructs are not limited to just what AWS creates via their CDK modules, instead Developers and Engineers alike have the ability to create their own. By extending out the core `Construct` class, users of CDK are able to combine multiple L1 and L2 constructs to create their very own Services. For example, say we need a specific S3 Bucket, that triggers an SNS topic on new objects being added we can Extend the core Construct class and create:
This new Construct called NotifyingBucket can now be imported and used via a class call as with any other construct i.e. new NotifyingBucket() .
As with Resources in Cloudformation to wrap up each of the constructs to form a functional Cloudformation stack, Constructs are added to Stacks which are individual .ts/.py/etc files that house a group of resources making up the required infrastructure. Without going in depth, these are then wrapped into App files where the Stacks are then executed as individual Cloudformation stacks. For more on concepts of CDK outside the core see the AWS documentation here
CDK: Why use it
On a technical focus, AWS CDK comes across as the next generation in AWS IaC. The examples above and the breakdown of key components outline the clear advantages of CDK, its robust language support and adoption of an existing platform, Cloudformation.
Within the wider DevOps/SRE community, CDK has quickly become a staple. AWS has invested in expanding CDK beyond its own realms with the introduction of cdktf, a CDK approach to create Terraform templates created in conjunction with HashiCorp and cdk8s, in the same manner as cdktf, a CDK layer to creating Kubernetes YAML to define Kubernetes clusters.
From the perspective of a project team, CDK has great reusability and an easier barrier of entry. As long as an individual knows a programming language, that CDK can be written in, the barrier of entry shrinks. CDK enables componentization and re-usability making for faster deployments and a better base to build off with the usage of package management solutions. The detailed documentation for each language provides enough of a starting point for new individuals, that they only need to understand the AWS Services, instead of having to know how JSON/Yaml based Cloudformation works. Although this is to say, on using CDK, knowledge of Cloudformation is gained.
A modern Managed Service Provider reaps the same benefits as a project team. Engineers can be empowered to write in the language best suited for the client, and although language may change, the approach to CDK does not. No one client will be identical to the next, some may be .NET shops that could favour using C# over say Java, and from an interaction perspective it would make sense to use CDK in C#. All the features of CDK remain across all the languages presented, therefore when comparing, Constructs, Stacks, Apps and the method based approach is still there.