Let's try CDKTF (with Python)
Tags: terraform, aws, cloud, python
Published: 2022-09-27 08:25:47

## Why CDKTF ?

It's been a few years that I heard about [AWS CDK](https://aws.amazon.com/fr/cdk/), an abstraction layer on top of [CloudFormation](https://aws.amazon.com/fr/cloudformation/), allowing you to build infrastructure with common programming languages, already known by developers, like TypeScript, JavaScript, Python, Java or C#.

[CDKTF](https://developer.hashicorp.com/terraform/cdktf) is the Hashicorp's answer, offering the same concept, but with some pretty differences : 
* It runs with Terraform !
* Languages are not exactly the same : TypeScript, Python, Java, C# and Go
* Ultimately, you can use every Terraform provider and module available on the [Terraform Registry](https://registry.terraform.io/), as the documentation says !

## And why Python ?

The most appropriate language seems to be TypeScript, it offers more possibilities and needs less installations.
But I'm more confortable with Python, and it's interesting to see how it's integrated.
Also, the language is available on both CDK and CDKTF, so I can benefit from both documentations.

## Installation

For this example, you have to setup this tools : 
* An AWS account, with Aws-cli configured
* Terraform, with a free [Terraform Cloud](https://developer.hashicorp.com/terraform/cloud-docs) account
* NodeJS / NPM
* Python / Pipenv

```bash
npm install --global cdktf-cli@latest

# to see the available commands
cdktf help
```

## Usage

First, initialize the projet locally :
```bash
cdktf init --template="python"
# Then, there is a prompt, I specify "cdktf_example" 
# for the name of my workspace
```

Then, we will create a simple S3 bucket, according to the [API Reference](https://www.terraform.io/cdktf/api-reference/python) :
```python
#!/usr/bin/env python

BUCKET_NAME = "<< THE UNIQUE NAME FOR MY S3 BUCKET >>"
TFCLOUD_ORGA = "<< THE NAME OF MY ORGANIZATION >>"

from constructs import Construct
from cdktf import App, NamedRemoteWorkspace, TerraformStack, TerraformOutput, RemoteBackend
from cdktf_cdktf_provider_aws import AwsProvider, s3


class MyStack(TerraformStack):
    def __init__(self, scope: Construct, ns: str):
        super().__init__(scope, ns)

        AwsProvider(self, "AWS", region="eu-west-1")

        bucket = s3.S3Bucket(self, "cdktf-example",
                             bucket=BUCKET_NAME)

        TerraformOutput(self, "s3_arn",
                        value=bucket.arn,
                        )


app = App()
stack = MyStack(app, "cdktf_example")
RemoteBackend(stack,
              hostname='app.terraform.io',
              organization=TFCLOUD_ORGA,
              workspaces=NamedRemoteWorkspace('cdktf_example')
              )

app.synth()
```

Finally, we can manipulate the infrastructure with this commands :
```bash
# to install dependencies
pipenv install cdktf-cdktf-provider-aws
cdktf get

cdktf diff
# which is the same than `terraform plan`

cdktf deploy
# which is the same than `terraform apply`

cdktf destroy
# which is the same than `terraform destroy`
```
The code is available [here](https://github.com/jdxlabs/cdktf-example).

## My feedback

If, like me, you are used to store tfstates on S3 buckets, for collaborative work, I've found the `DataTerraformRemoteStateS3` object that's available, so you can avoid to use Terraform Cloud if you want.

At the time of writing the article, I had to use black magic to find simple informations, like the correct syntax to instanciate the S3 bucket (especially since the syntax slightly differs between CDK and CDKTF), but we can guess it will get better soon.

The really good point is that it actually works pretty well, and allows to write infrastructure code with Python and all its advantages, like structuration, unit testing and so on.

The other great advantage of CDKTF, over CDK, is that it can work with any Terraform provider, so it's not limited to AWS anymore.

CDKTF allows you to write your infrastructure code directly with your favorite language, so feel free to try it too !