June 30, 2019 / 8 minutes / #AWS #CDK #Terraform #Cloud
As AWS Architect my day-to-day job includes writing a lot of Terraform code in HCL.
While I really like TF, in few places it's really verbose and some quirks of HCL are frustrating. So, I decided to take AWS Cloud Development Kit for a spin and get my hands dirty by building a simple 2-tier AWS infra like this:
VPC with two subnets (public and private) in each AZ, IPGW as
0.0.0.0/0 route in public subnet, NAT Gateway as
0.0.0.0/0 route in private subnet
ECS Cluster running Fargate cluster with Node.js service and task
Application Load Balancer exposed to the public connected to the Fargate service
Public S3 bucket with assets
IAM Role for task to manipulate S3 Bucket
Postgres DB instance in private subnet, accessible only from ECS tasks
Before getting into actions I've listed a couple of obvious differences which didn't require any practice from me:
Written in HCL
Supports multiple providers
SDK calls under the hood
Written in TS, Java or Python
Cloudformation under the hood
After result of few hours fight with both tools, I present you...
Imperative nature of the language used in CDK might be counterintuitive for declaring things like infrastructure where the end state is the most important thing. This might be even tricky due to async nature of JS (e.g.
for loop with
async) but when used correctly pays off in better possibilities to express collection operations. This has been improved greatly in Terraform 0.12, however, it's still far from flexibility and easy of use that we're used from ES6 spec like
CDK API changes pretty quickly, some of the examples are outdated and require some adjustments like
When bootstrapping a project make sure you're running the same version of CLI and all the modules. Otherwise you might face some cryptic errors and lose unnecesary amount of time debugging them.
In CDK nomenclature "Modules" are called "Constructs". Unlike Terraform, CDK comes with a bunch of really robust constructs like
ecs.LoadBalancedFargateService which encapsulate hundreds of CloudFormation or Terraform code in just 10 or even less. While this is really useful this tradeoff is not without any compromises. Some of these modules are really implicit and you end up digging in the source code wondering what are the required params, what's included and how it should plug to the rest of your infra.
Using the same language for both code, configuration and infrastructure makes whole process of development really seamless. Also, when writing in a language with type completion and properly configurated IDE, it just feels super nice, all the parts "fit" like lego.
Really refreshing comparing to writing huge YAML or JSON files. However, going with Python where there is no static typing I couldn't see big benefit. Plus, Troposphere is with us already for a moment.
Also the amount of code is much less in CDK:
➜ find ./cdk/lib -name "*.ts" | xargs cat | wc -l 105 ➜ find ./terraform -name "*.tf" | xargs cat | wc -l 516
If you're interested in final result, you can find the repo with code here!. After pulling it, simply execute
./build-and-push.sh to create an AWS Elastic Container Registry, build the Docker image and push it to the cloud. It will be used by both infras.