08 September 2023
Hidden Costs of Using CDK
There are some hidden costs that you may have to deal with when using higher-level constructs in CDK
Lucian Suta
#aws #cdk #vpc #natgateway #cloudformation

Introduction

AWS CDK (Construct Development Kit) is a great IaC (Infrastructure as Code) technology for defining backend AWS resources for your projects.

CDK comes with three layers of abstraction for constructs

  • L1 constructs
    Layer 1 constructs are low-level constructs that directly map to CloudFormation resources. CfnBucket is an example of such a construct that maps to the AWS::S3::Bucket CloudFormation resource.
  • L2 constructs
    Layer 2 constructs also map to CloudFormation resources, but come with default values, boilerplate and glue code, convenience methods. Bucket is an example of such a construct that maps to the AWS::S3::Bucket CloudFormation resource.
  • L3 constructs
    Layer 3 constructs combine the functionality of other L1 and L2 constructs to provide a more complete solution. Also called 'patterns' in the CDK documentation.

But there is problem with it that can cost you lots of money in addition to frustration. It comes from using layer 2 and 3 constructs which may create resources you may not be aware of, resources that cost when they are instantiated, even when not used (e.g. NAT gateways, databases, load balancers).

Example

Consider the case of using a simple VPC construct in your stack.

import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

export class DemoStack extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);
        const vpc = new ec2.Vpc(this, 'demo-vpc', {
            vpcName: 'Demo VPC'
        });
    };
};

If you deploy this infrastructure to AWS using the cdk deploy command, you might be surprised to find out, at the end of the month that you have been charged tens of dollars (depending on the region you deployed to). This is because the L3 construct above creates a NAT Gateway for each availability zone in the region.

  • AWS::EC2::VPC
  • AWS::EC2::InternetGateway
  • AWS::EC2::VPCGatewayAttachment
  • Custom::VpcRestrictDefaultSG
  • AWS::IAM::Role
  • AWS::Lambda::Function
  • For each availability zone
    • A public subnet containing
      • AWS::EC2::Subnet
      • AWS::EC2::RouteTable
      • AWS::EC2::SubnetRouteTableAssociation
      • AWS::EC2::Route
      • AWS::EC2::EIP
      • AWS::EC2::NatGateway <-- this is where the problem is
    • A private subnet
      • AWS::EC2::Subnet
      • AWS::EC2::RouteTable
      • AWS::EC2::SubnetRouteTableAssociation
      • AWS::EC2::Route

Cost

Assume we deploy in the region eu-north-1. At the time of this article, we have

  • Three availability zones: eu-north-1a, eu-north-1b, eu-north-1c
  • Cost of running a NAT Gateway is $0.046/hour

If you deploy this simple VPC configuration, in one month the costs will add up to over one hundred dollars, a not so insignificant cost if you don't need the gateway in the first place.

3 x 24 x 31 x 0.046 dollars = 102.7 dollars

Solution

The solution to the problem above is to prevent CDK from creating NAT gateways by specifying a count of zero.

const vpc = new ec2.Vpc(this, 'demo-vpc', {
    vpcName: 'Demo VPC',
    natGateways: 0,
});

Conclusion

CDK can save you time by generating complex CloudFormation templates from simple code involving L2 and L3 constructs. However, you need to really understand what those constructs entail or check the CloudFormation output (in the cdk.out folder) to see what resources they create.

Reach us at: contact {at} defsense {dot} eu
Built with Nuxt, Tailwind and deployed on Cloudflare
Copyright © 2024 Defsense