Creating VPC Using Terraform CDKTF

In the previous post, we learned about how to create subnets for a VPC. Now, let's learn about how to create VPC by making use of Terraform Cloud Development Kit.

What is CDKTF ?

Before proceeding further, let us learn what is CDKTF?

Cloud Development Kit for Terraform (CDKTF) allows us to use familiar programming languages to define and provision infrastructure. This gives us access to the entire Terraform ecosystem without learning HashiCorp Configuration Language also known as HCL and lets you leverage the power of the existing toolchain for testing, dependency management, etc.

CDKTF currently supports Typescript, Python, Java , C#.

We can find more information about CDKTF i.e. how to setup CDKTF and create and initialise a project refer the docs here.

Now, assuming that we have a fair bit of knowledge about CDKTF and how it works, let's proceed further to create a VPC by making use of CDKTF. We'll be using the typescript language.

import { Construct } from "constructs";
import { App, TerraformStack, Fn, TerraformOutput } from "cdktf";
import { AwsProvider, datasources } from "@cdktf/provider-aws";
import { Vpc } from '../gen/modules/vpc';

import {
    getPublicSubnetCidrBlocks,
    getPrivateSubnetCidrBlocks
} from '../lib/util';

const cidrPrefix = "10.0.0.0/16";
const nameLabel = "Demo App";
const nameIdentifier = "demo-app";

/**
 * Terraform stack
 */
class MyStack extends TerraformStack {
    userInput: any;
    vpcOutput: Vpc | {
        vpcIdOutput: string
        privateSubnetsOutput: string[]
        publicSubnetsOutput: string[]
    };
    securityGroupOutput: SecurityGroup | {
        thisSecurityGroupIdOutput: string
    };
    iamRole: IamRole | {

    };

    /**
     * Constructor for the terraform stack
     *
     * @param {Construct} scope
     * @param {string} name
     */
    constructor(scope: Construct, name: string) {
        super(scope, name);

        this.userInput = {};
        this.vpcOutput = {
            vpcIdOutput: '',
            privateSubnetsOutput: [],
            publicSubnetsOutput: []
        };
        this.securityGroupOutput = {
            thisSecurityGroupIdOutput: ''
        };
        this.iamRole = {};
    }

    /**
     * Main performer of the class.
     *
     */
    async perform() {

        this._setAwsProvider();

        this._createVpc();

    }

    /**
     * Set AWS provider
     *
     * @private
     */
    _setAwsProvider() {
        new AwsProvider(this, "AWS", {
            region: this.userInput.aws.awsDefaultRegion,
            accessKey: this.userInput.aws.awsAccessKeyId,
            secretKey: this.userInput.aws.awsSecretAccessKey
        });
    }

    /**
     * Create VPC
     *
     * @sets this.vpcOutput - output from the created vpc
     * @private
     */
    _createVpc() {
        const privateSubnetCidrBlocks = getPrivateSubnetCidrBlocks(
            cidrPrefix,
            2,
            2
        );

        const zones = new datasources.DataAwsAvailabilityZones(this, 'zones', {
            state: 'available'
        });

        new TerraformOutput(this, "first_zone", {
            value: Fn.element(zones.names, 0)
        });

        new TerraformOutput(this, "second_zone", {
            value: Fn.element(zones.names, 1)
        });

        const vpcOptions = {
            name: nameLabel,
            azs: [Fn.element(zones.names, 0), Fn.element(zones.names, 1)],
            cidr: cidrPrefix,
            publicSubnets: getPublicSubnetCidrBlocks(cidrPrefix),
            publicSubnetTags: {
                "Name": nameLabel + " public"
            },
            privateSubnets: privateSubnetCidrBlocks,
            privateSubnetTags: {
                "Name": nameLabel + " private"
            },
            enableNatGateway: true,
            singleNatGateway: true,
            enableDnsHostnames: true
        };

        this.vpcOutput = new Vpc(this, nameIdentifier, vpcOptions);
    }
main.ts

Now let us learn what is going on in the above main.ts snippet.

Constructs are the basic building blocks of AWS CDK apps. A construct represents a "cloud component" and encapsulates everything AWS CloudFormation needs to create the component. Constructs are part of the Construct Programming Model (CPM) and are also used by other tools such as CDK for Terraform (CDKTF).

Next, we are importing the modules provided by cdktf/provider-aws . Since the provider is AWS, it depicts that are going to create our resources in AWS.

We set the AWS Credentials in the function _setAwsProvider so that CDKTF can create the resources in the particular region for that account.

We now assign a valid CIDR value in order to create a VPC. In the above example, we are gonna use 10.0.0.0/16 as the CIDR for our VPC. We give a name to the vpc and then we define the objects needed to created our VPC such as setting Availability Zones for our VPC, Creating Public/Private subnets and configuring a few settings such as:-

enableNatGateway: true
singleNatGateway: true
enableDnsHostnames: true

Here is our utils.ts where we define the public and the private subnets functions.

import { Fn } from "cdktf";

function getPublicSubnetCidrBlocks(cidrPrefix: string) {
    return [
        Fn.cidrsubnet(cidrPrefix, 8, 0),
        Fn.cidrsubnet(cidrPrefix, 8, 1)
    ]
}

function getPrivateSubnetCidrBlocks(cidrPrefix: string, privateSubnetCount: number, netNumStart: number) {
    const privateSubnetCidrBlocks: string[] = [];

    for (let index = 0; index < privateSubnetCount; index++) {
        privateSubnetCidrBlocks[index] = Fn.cidrsubnet(cidrPrefix, 8, netNumStart + index);
    }

    return privateSubnetCidrBlocks;
}

export { getPublicSubnetCidrBlocks, getPrivateSubnetCidrBlocks };
utils.ts

The above snippet, defines the subnet CIDR ranges as we had seen in the previous article. We are creating two public subnets and two private subnets based on the above snippet.

How to apply the above resources ?

Using cdktf cli:-

  • Setup cdktf cli by installing the node module

npm install --global cdktf-cli@latest

  • Run cdktf deploy in order to provision the above resource.

Voila, we can now create our VPC and Subnets on AWS using CDKTF :)

Kiran Kamalakar

Kiran Kamalakar

Design and development enthusiast.
Pune, India