Why the AWS Cloud Development Kit is taking off.
This article was originally posted on this blog
Infrastructure as Code has become a very popular mechanism for managing software infrastructure.
However, if you want to take advantage of it, you’ll need to make some decisions. Managing SaaS offerings (like Gitlab/Github)? You’ll probably need Terraform.
If you’re managing AWS then you’ll likely use CloudFormation or the CDK. If you are building microservices specifically, you could go with serverless.com.
If you’re using Kubernetes, time to sprinkle some Helm in there. To have a completely automated system you’ll likely be using a few of the offerings out there.
Enter the AWS CDK
When the Cloud Development Kit was released in 2019 many people were excited about what it would provide. CloudFormation has some rough edges.
At re:Invent 2019 many presentations talked about the CDK, either directly or indirectly. There have been over 50 releases since last year and the online community is thriving.
For me, the excitement was about seeing a concise method for representing infrastructure that could be easily abstracted into constructs, the building blocks of infrastructure.
Constructs naturally are flexible in their level of abstraction. Combine that with being able to use the language of my choice, and I quickly found a ton of value in the system.
First, let’s get a little groundwork laid. There are different types of constructs you can use and build. An L1 (Level
- construct is a 1 to 1 mappings with the underlying resource and is usually generated.
For the AWS CDK the L1 constructs are generated from CloudFormation resource definitions. For the cdk8s, the L1 constructs are generated from the Kubernetes Custom Resource Definitions (CRD).
Sitting at an abstraction layer above that are L2 constructs, which use L1 constructs and adds some helper
functionality, like granting permissions to other
resources. The L2 construct for Lambda Functions
gives you a .grantInvoke()
function that you pass a resource (like another Lambda) and it will generate the needed IAM
policy for invoking that Lambda. No more writing IAM policies by hand!
Then you can create L3 constructs that use L1 and L2 constructs and wire them together in meaningful ways, like a microservice construct that builds a DynamoDB table, a Lambda, and an API Gateway endpoint.
If you’ve ever done any React development, this might sound familiar to you. In React, components are wired together, or composed, to form complex UI applications.
React components are highly isolated pieces of code, allowing for developers to easily add third-party components to their system with little risk of “breaking things”.
In this same way, constructs are used to represent small pieces of infrastructure that can be easily wired together with other constructs to build very complex infrastructure.
Then things grew
This year the cdk8s and cdk8s+ projects were announced. These projects, like the AWS CDK, provide a multi-language API for creating constructs that represent Kubernetes resources. The cdk8s project only handles L1 constructs while the cdk8s+ has L2/L3 constructs.
And then last month Hashicorp launched the terraform-cdk project. Now you can handle a wide range of resources, including Github, Gitlab, and other Software as a Service (SaaS) thanks to Terraform’s existing providers.
Construct all the things!
Now we can leverage the same conventions, patterns, CICD, and people to build most of your IaC. This greatly reduces the overall Total Cost of Ownership for your IaC system by making it easier to set up and maintain.
Kubernetes cluster in AWS (the CDK)
Here is an example of creating an Elastic Kubernetes Services cluster:
const cluster = new Cluster(this, 'hello-eks', { version: KubernetesVersion.V1_16, });
Kubernetes resources (cdk8s and cdk8s+)
This code will create a simple deployment of a docker image, with a service:
const deployment = new Deployment(this, 'MyApp', { spec: { replicas: 3, podSpecTemplate: { containers: [new Container({ image: 'node', port: 9000 })], }, }, }); // this will internally create a service deployment.expose({ port: 8000 });
SaaS providers (terraform-cdk)
Finally, we can now use Terraform constructs to create Chef resources.
new ChefProvider(this, 'chef', { serverUrl: 'http://hello.hashicorp.com', clientName: 'terraform' }); const subnetsDataBag = new DataBag(this, 'subnets', { name: 'subnet_cidrs' }); new DataBagItem(this, 'cidrs', { dataBagName: subnetsDataBag.name, contentJson: JSON.stringify({ id: vpc.id, subnet_cidr: subnet.cidrBlock }) });
Comparison
Notice the similarities between all three codebases. A nicely uniform API!
Compare that to CloudFormation, Kubernetes and Terraform:
CloudFormation
Resources: myCluster: Type: 'AWS::EKS::Cluster' Properties: Name: prod Version: '1.14' RoleArn: >- arn:aws:iam::012345678910:role/eks-service-role-AWSServiceRoleForAmazonEKS-EXAMPLEBQ4PI ResourcesVpcConfig: SecurityGroupIds: - sg-6979fe18 SubnetIds: - subnet-6782e71e - subnet-e7e761ac
Kubernetes
kind: Deployment apiVersion: apps/v1 spec: replicas: 3 selector: matchLabels: cdk8s.deployment: MyAppC6A88652 template: metadata: labels: cdk8s.deployment: MyAppC6A88652 spec: <pod-spec-ommitted-for-brevity> --- kind: Service apiVersion: v1 spec: type: ClusterIP ports: - port: 8000
Terraform
# Configure the Chef provider provider "chef" { server_url = "https://api.chef.io/organizations/example/" # You can set up a "Client" within the Chef Server management console. client_name = "terraform" key_material = "${file("chef-terraform.pem")}" } # Create a Chef Environment resource "chef_environment" "production" { name = "production" } # Create a Chef Role resource "chef_role" "app_server" { name = "app_server" run_list = [ "recipe[terraform]", ] }
To wrap up
The construct API design lends itself to easily represent a number of data models in the Infastructure as Code space, including CloudFormation (the original design), Kubernetes, and Terraform.
Now that APIs have been implemented for all three, a DevOps team can cover a lot of resources with IaC and standardize on APIs and tools, gaining a tremendous amount of value by reducing the overall headspace their team needs to set up and maintain the systems.
It’s likely we’ll see other APIs in the future, perhaps an azure-cdk?!
Developer Operations is still a growing team in most organizations. They’re constantly being asked to support more and more development teams. Currently, larger enterprises are seeing a mix of CloudFormation, Terraform, and others within their environment.
This sprawl can cause problems onboarding DevOps engineers when priorities shift. Can someone who knows CloudFormation pick up Terraform, or vice-versa? With time, absolutely. But, during that time productivity is likely to take a hit.
Imagine an organization that has 13 different UI projects under development. Some projects are written in Vue, others in React, while there may be some older projects running different versions of Angular.
If priorities shift and one project requires additional developers, those developers better know that framework or else they’ll be asking more questions than solving problems.
The organization is then inflexible, and unable to meet changing demands. This leads most organizations to try to standardize on a particular framework.
The same is goes for Infrastructure as Code. Organizations will find value in having their DevOps teams working with similar APIs to generate resources.
The AWS CDK has established itself in the AWS IaC space because of its expressive and concise API. The cdk8s and terraform-cdk are in their early days, but are looking like very promising projects that have the same benefits.
Organizations looking to adopt Infrastructure as Code now have a consistent API with wide support for all their needs.