Skip to main content

Managing Equinix resources with Terraform CDK

A guide on leveraging Terraform CDK (cdktf) to manage Equinix infrastructure.

Managing Equinix resources with Terraform CDK

Recently, Hashicorp updated their registry to allow example code, previously only available as HCL, to be provided in any supported language by Terraform CDK. Terraform CDK is Hashicorp's layer on top of Terraform that facilitates using standard programming languages to write your infrastructure as code.

Today, we're going to take a look at deploying an Equinix Metal server with cdktf, and then creating an abstraction to make it easier to spin up standard configurations; with a single line of code.

Let's dive in.

Creating a New Project

To get started, you'll need to install Terraform CDK. You can find the documentation for this here.

To create a new project, we can run:

cdktf init

You can then select your programming language of choice, as long as it's supported.

We'll be using TypeScript for today's tutorial.

Adding the Equinix Provider

In-order to add a Terraform provider to our project, we need to pop open the cdktf.json file. It'll look something like this:

{
  "language": "typescript",
  "app": "npx ts-node main.ts",
  "projectId": "edef377e-16c1-4e6b-91d1-d5d42f699629",
  "sendCrashReports": "false",
  "terraformProviders": [],
  "terraformModules": [],
  "context": { }
}

We want to change the terraformProviders block to add our new provider. To add Equinix, we make it looks like so:

"terraformProviders": [
  "equinix/equinix@~> 1.20"
],

We can then ask Terraform CDK to fetch the providers:

cdktf get

Alternatively, you can also use cdktf to add the provider too and fetch the latest version:

cdktf provider add equinix/equinix

Configuring the Provider

Now we need to ensure that the provider can speak to the Equinix APIs and this will need authentication. This is the exact same as standard Terraform, so we can do this with a few environment variables.

export METAL_AUTH_TOKEN=someEquinixMetalToken

Deploying a Server

Now that we want to provision a device, we need to start writing some code within main.ts.

First, we'll need to import the generated SDKs that we need. For this example, that's a provider and a device.

import { EquinixProvider } from "./.gen/providers/equinix/provider";
import { MetalDevice } from "./.gen/providers/equinix/metal-device";

By default, cdktf generates all the dynamic providers within the .gen directory. From here, we can begin to add resources to our Stack, which initially looks like this:

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

You can add any resource you like within this constructor and it'll be handled correctly. Of course, there are also routes to make this cleaner and we'll see those shortly.

For now, let's add the provider and our first device.

new EquinixProvider(this, "equinix");

new MetalDevice(this, "first-device", {
  projectId: "a1b2c3d4e5f6g7h8i9j0",
  hostname: "first-device",
  plan: "c3.small.x86",
  metro: "ams",
  operatingSystem: "ubuntu_20_04",
  billingCycle: "hourly",
});

We can now run cdktf deploy and within a few minutes, we'll have our first server. Sweet!

Adding an Abstraction

Now, let's take a look at some ways to avoid filling our constructor with tons of resources and ending up with a massive single function.

Let's create our own Construct to provide a default-first approach to provisioning servers.

interface ServerConfig {
  projectId: string
  size: string;
  metro: string;
}

class MyServer extends Construct {

  constructor(scope: Construct, id: string, config: ServerConfig) {
    super(scope, id);
    
    new MetalDevice(this, id, {
      projectId: config.projectId,
      hostname: id,
      plan: config.size,
      metro: config.metro,
      operatingSystem: "ubuntu_20_04",
      billingCycle: "hourly",
    })
  }
}

Now, we can modify our interface to container whatever parts of the server we want to make configurable; and default everything else.

We can then use our new abstraction with simply:

new Myserver(this, "second-server", {
  projectId: "123",
  size: "c3.small.x86",
  metro: "ams",
});

What's nice about this approach, is that we can now layer on top of this the extras that we likely need, such as BGP or IP reservations.

Let's continue and make it easy to add an IP reservation to the device. Instead of doing this by adding more to the constructor, we'll add a new method to the class and make it opt-in.

class MyServer extends Construct {
  ...
  public addIpReservation() {
  }
  ...
}

Summary

Terraform CDK provides a great alternative to HCL that allows developers to use the same language and toolchains that they're familiar with, allowing for use of LSPs and IDE refactoring tools, and even the ability to adopt testing strategies for infrastructure as code; something that has been difficult for many many years.

Last updated

03 June, 2024

Category