Intro

In a previous post I setup CDK for Terraform using a local state backend.

In this post, I will show you how to utilize an Azure Blob storage container as a backend for CDKTF. The post, follows on from the previous post, so check that out for details on setting up the environment.

Software

The following software was used in this post.

  • Ubuntu - 24.04
  • NodeJS - v22.14.0
  • NPM - v10.9.2
  • fnm - v1.38.1
  • Typescript - v5.8.2
  • TerraformCLI - v1.10.5
  • TerraformCDK - v0.20.11
  • Azure CLI - v2.68.0

Setup

Azure

You will need to create a storage account to store your TF State in Azure. There are a few ways to do this, you can click through the web UI or use the Azure CLI.

Once it is created, browse to [storage-account-name] > Security + Networking > Access Keys to get an authentication key.

Set the key as an environment variable named ARM_ACCESS_KEY. This key is used to authenticate Terraform to the Storage Account.

Project Directory

If you followed along on the last post, you will have a cdktf.out/ directory. Delete that before continuing.

cmd
rm -rf cdktf.out/

Define Config

Create a main.ts file with the following contents that will utilise Azure Blob storage as a backend and create a resource group and virtual network.

main.ts
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { App, TerraformStack, AzurermBackend, TerraformVariable } from "cdktf";
import { VirtualNetwork } from "@cdktf/provider-azurerm/lib/virtual-network"
import { ResourceGroup } from "@cdktf/provider-azurerm/lib/resource-group";

const resource_group = "rg-stuff-and-things";
const location = "australiaeast";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    // Environment variabled prefixed with TF_VAR_ eg: TF_VAR_ARM_SUBSCRIPTION_ID
    const armSubscriptionId = new TerraformVariable(this, "ARM_SUBSCRIPTION_ID", {
      type: "string",
      description: "Subscription UUID",
      sensitive: true,
    });

    new AzurermBackend(this, {
      resourceGroupName: "rg-tfstate",
      storageAccountName: "sa-tfstate-42069",
      containerName: "tfstate",
      key: "stuff-and-things.terraform.tfstate",
    });

    new AzurermProvider(this, "AzureRm", {
      features: [{}],
      subscriptionId: process.env.ARM_SUBSCRIPTION_ID,
    });

    const resourceGroup = new ResourceGroup(this, "RgStuffAndThing", {
      name: resource_group,
      location: location,
    });

    new VirtualNetwork(this, "StuffAndThingVnet", {
      name: "vnet-stuff-and-things",
      location: resourceGroup.location,
      resourceGroupName: resourceGroup.name,
      addressSpace: ["10.0.0.0/24"],
    });
  }
}

const app = new App();
new MyStack(app, "azurecdk-tf");
app.synth();
Note
This config uses two environement variables ARM_ACCESS_KEY and TF_VAR_ARM_SUBSCRIPTION_ID. Be sure that they are set in your environment.

Testing

Now with that in place, you can run a cdktf plan to confirm that access to the blog storage is working and deploy your infrastructure using an Azure Blob Storage backend.

Outro

In this post, we setup an Azure Blob Storage backend for use with CDKTF. A shared state backend allow multiple team members / platforms to work with a common centralised state file.

Peace out nerds. Stay weird ✌️