How to manage Akamai resources using cdk for terraform (typescript)

Deepak
11 min readSep 6, 2022

In this post I am going to share my experience of managing Akamai as a code using cdk for terraform.

Akamai is popular content delivery, cybersecurity, and cloud service company, providing web and Internet security services. Akamai offer its customer range of choices to manage its various product, ranging from GUI to API, CLI and Infrastructure as a Code(IaaC) tool like terraform, pulumi and with possibility of using other tools like ansible, octoDNS etc.

Terraform is popular IaaC tool and it’s popularity lies in the fact of easiness to get started with. Being a declarative language it hides away many of the complexity, its user only need to tell what needs to be provisioned and not how to provision. Due to cultural shift brought by devops, many of the traditional operations guys who have hardly written any code in their entire career, are now going through(or have gone through) transition phase of learning developer or coding skills. And i believe terraform has been cherry to their eyes.

Photo by Kevin Ku on Unsplash

Having said that how easy it is for someone to learn terraform, we also need to remember that developer’s first choice is always what they already know 😃

The Cloud Development Kit for Terraform (CDKTF) is a tool built on top of Terraform that allows you to define infrastructure using a familiar programming language (such as TypeScript, Python, or Go) instead of HashiCorp Configuration Language (HCL). This tool can provide a faster learning curve for developers unfamiliar with HCL, while allowing developers to use native programming features like loops, variables, and functions.

Let’s see how can we use CDKTF to manage akamai delivery configuration. If you are familiar with akamai then delivery configuration equates to property manager i.e. CDN configuration. Please note that we are going to deploy and manage delivery configuration in this tutorial but nothing stops you to deploy Akamai security config or Akamai public cloud i.e. Linode(a developer friendly alternative to AWS) using CDKTF.

Prerequisites

  • Good (basic will also be fine 😃) understanding of Terraform. You can learn about terraform here
  • Familiar with Akamai Delivery Configuration(Property Manager)concept. You can learn about Akamai here
  • API Credential for managing Akamai delivery configuration. Refer https://techdocs.akamai.com/developer/docs/set-up-authentication-credentials
  • Learn how can you manage Akamai resources using terraform. Refer my other post https://medium.com/aws-tip/cloud-agnostic-secure-delivery-of-a-website-on-akamai-edge-platform-using-terraform-part-i-e2ce226231ed
  • Terraform installed on your local machine, check here to get started
  • Node.js installed on your local machine, check here
  • Familiarity with JavaScript and TypeScript. I assume that because you are reading this article so you do have rough idea about it.
  • A code editor or integrated development environment (IDE) that supports TypeScript. I am going to use Atom in this tutorial.

Step 1 — Prepare your dev machine

During this step we are going to install cdktf cli and setup akamai edgerc( for authenticating akamai api calls)

1.1 Install cdktf CLI

On mac easiest way is to use homebrew

brew install cdktf

if you want to use other method then for installation using NPM

npm install — global cdktf-cli@latest

Once done verify that cdktf is installed by running below command.

Verify cdktf cli installation

1.2 Setup Akamai edgerc File. — Follow below link for setting up API client and generating edgerc credentials:- https://techdocs.akamai.com/developer/docs/set-up-authentication-credentials

Make sure that you test api credentials by making sample calls using httpie or other methods mentioned here — https://techdocs.akamai.com/property-mgr/reference/api-summary

2. Build project directory — This is going to be our project directory that will be used for storing our code repo and other cdktf specific files, along with terraform statefile.

First, create a new directory

mkdir cdktf-demo

Next, Initialize the directory, cdktf provides an easy way to create a scaffolding for you, basically template files and folders that are downloaded based on your choice of language. If you know about pulumi then it is quite similar to it.

cdktf init — template=typescript — project-name=akamai — project-description=”First hand” — local

Let’s break down the above command to understand it

  • init — initialize the current directory
  • template — language that you want to code in
  • project-name — your choice of project name
  • local — signifies where we want to store terraform statefile, in this case we are using locally. Please note that in production environment you should use terraform backends.

While running above command you would be prompted with below questions, they are self explanatory and you can answer as needed. I answered No to both questions.

Initialize cdktf

Upon successful execution you would see below files created in your current directory

cdktf initialize output

We will look into few important files

a) cdktf.json — is the configuration file for the CDKTF project. Content of the file is as mentioned below

ctktf.json

Most important line in above file is second line i.e.

 “app”: “npx ts-node main.ts” 

It basically highlights that main.ts is going to be entrypoint for this project.

Now, it takes us to another important file i.e. main.ts

b) main.ts — This is the file where we are going to write our code. Basically define resources to be manage via. cdktf. This file is going to be used for converting constructs written in typescript to terraform HCL.

Let’s take a look at content of this file.

Initial main.ts

Before learning about each line of above file it is important that we know few concepts that are unique to CDKTF.

  • Stack — a collection of related infrastructure resources can be grouped into a stack. Each stack keeps its own state and can be deployed, modified, or destroyed independently from other stacks. A common use of stacks is to have one stack for production and a separate stack for development. Typically an organization have many public facing websites and each website most probably would have different delivery configuration within akamai. From perspective of management of akamai resources it means that you can have one stack per delivery configuration.
  • Application — An application is container that can have one or more stacks. This concept is more applicable to microservices or decoupled environment.

Currently our main.ts has only one app and one stack with the name of akamai and no resources defined into it. In the further steps we will define resources within MyStack class for defining akamai stack. Sharp eyes would have noticed “//define resources here” that tells us where our resources would be defined.

3. Install Akamai terraform provider — for installing or downloading akamai terraform provider we need to make below changes to our cdktf.json

“terraformProviders”: [“akamai/akamai”]
cdktf.json updated with terraform provider details

Akamai terraform provider is located at akamai/akamai on terraform registry and that is what we updated in cdktf.json

Next run below command to get the provider on your dev machine.

cdktf get

After running this command you would see a hidden folder called

.gen 

created in your project directory i.e. .gen/providers/akamai. You will see that cdktf has downloaded various resources and data sources from akamai terraform registry and converted it into typescript classes.

4. Code your stack — In this step we are going to define akamai specific resources with in our main.ts file.

/**  Section 1 Start*/
import { Construct } from "constructs";
import { App, TerraformStack, TerraformVariable } from "cdktf";
import * as path from "path"
import { DataAkamaiGroup, DataAkamaiContract, DataAkamaiPropertyRulesTemplate, PropertyActivation, Property, AkamaiProvider, EdgeHostname } from "./.gen/providers/akamai";
/** Section 1 End*/
class MyStack extends TerraformStack {
constructor(scope: Construct, name: string) {
super(scope, name);
new AkamaiProvider(this, 'provider')/** Section 2 Start - Defining input variables */
const groupName = new TerraformVariable(this, "groupName", {
type: "string",
default: "<Your Group Name>",
description: "Akamai Group Name for provisioning resources"
});
const contractId = new TerraformVariable(this, "contractId", {
type: "string",
default: "<Your Contract Name>",
description: "Akamai Contract Id for provisioning resources"
});
const productId = new TerraformVariable(this, "productId", {
type: "string",
default: "prd_Site_Accel",
description: "Akamai Product Id for provisioning resources"
});
const propertyName = new TerraformVariable(this, "propertyName", {
type: "string",
default: "cdktf-demo",
description: "Akamai Property Name"
});
const ehnHostname = new TerraformVariable(this, "ehnHostname", {
type: "string",
default: "<Your Edge Hostname>",
description: "Akamai EHN hostname"
});
/** Section 2 End - Defining input variables */
/** Section 3 - Defining Data Sources*//** Defining Akamai Group DS */
const group = new DataAkamaiGroup(this, 'group', {
groupName: groupName.value,
contractId: contractId.value
})
/** Defining Akamai Contract DS */
const contract = new DataAkamaiContract(this, 'contract', {
groupName: group.groupName
})
/** Defining Akamai Property Rule Template DS */
const propertyRuleTemplate = new DataAkamaiPropertyRulesTemplate(this, 'propertyRuleTemplate',{
templateFile: path.join(__dirname, 'property-snippets/main.json')
})
/** Section 4- Defining Resource block*//** Defining Akamai EHN Resource */
const ehn = new EdgeHostname(this, 'ehn', {
productId: productId.value,
contractId: contract.id,
groupId: group.id,
edgeHostname: ehnHostname.value,
ipBehavior: 'IPV4'
})
/** Defining Akamai Property Resource */
const property = new Property(this, 'property',{
name: propertyName.value,
contractId: contract.id,
groupId: group.id,
productId: productId.value,
ruleFormat: 'latest',
hostnames: [{
cnameFrom: "cdktf-demo.akamaidevelopers.com",
cnameTo: ehn.edgeHostname,
certProvisioningType: "CPS_MANAGED"
}],
rules: propertyRuleTemplate.json
})
/** Defining Akamai Property Activation Resource */
new PropertyActivation(this, 'propertyActivation', {
propertyId: property.id,
contact: ["your@email.id"],
version: property.latestVersion,
network: "STAGING",
note: "Created via. CDK"
})
}
}
const app = new App();
new MyStack(app, "akamai");
app.synth();

Above file might seems to be difficult to understand at first go but let’s break it down and pick up each section one by one.

/**  Section 1 Start*/
import { Construct } from "constructs";
import { App, TerraformStack, TerraformVariable } from "cdktf";
import * as path from "path"
import { DataAkamaiGroup, DataAkamaiContract, DataAkamaiPropertyRulesTemplate, PropertyActivation, Property, AkamaiProvider, EdgeHostname } from "./.gen/providers/akamai";
/** Section 1 End*/

In the above section we have added TerraformVariable in import command (line 2) , this is needed to work with input variables within terraform. Variables are great way to keep your code DRY and we would also try to achieve same. More about terraform input variables here. In later section we will look into how our code is going to utilize input variables.

Next, we have also added line to import path(line 3), this is basically need to combine directory paths, not compulsory but our code uses it for reducing hardcoded paths. Again when we will review further sections we will come to know how it is going to be used.

Next(line 4), importing various data sources and resources that we are going to work with.

Let’s discuss next section of the code i.e. about declaring variables.

/** Section 2 Start -  Declaring input variables  */
const groupName = new TerraformVariable(this, "groupName", {
type: "string",
default: "<Your Group Name>",
description: "Akamai Group Name for provisioning resources"
});

If you have worked with akamai configuration before then you might be aware that there are few attributes like groupId, contractId, productId, that are needed while working with most akamai resources. In Section 2 we are going to variablize these attributes plus few others that are repeated across our code.

Being familiar with JS you would find it quite straightforward. We are defining a variable using const and then passing various attributes like type, default and description.

Next, we are going to declare few data sources that are going to be used/referred within our terraform resources block. More about terraform data sources here.

/** Section 3 - Defining Data Sources*//** Defining Akamai Group DS */
const group = new DataAkamaiGroup(this, 'group', {
groupName: groupName.value,
contractId: contractId.value
})
/** Defining Akamai Contract DS */
const contract = new DataAkamaiContract(this, 'contract', {
groupName: group.groupName
})
/** Defining Akamai Property Rule Template DS */
const propertyRuleTemplate = new DataAkamaiPropertyRulesTemplate(this, 'propertyRuleTemplate',{
templateFile: path.join(__dirname, 'property-snippets/main.json')
})

Section 3 consist of data sources block for Akamai Group, Akamai Contract and Akamai Property rule template.

If you are familiar with terraform provider for these datasources then converting it into typescript is not going to be difficult. Let’s take an example, for finding out details about DataAkamaiPropertyRulesTemplate i will first go to akamai terraform registry to see how to use this data source. https://registry.terraform.io/providers/akamai/akamai/latest/docs/data-sources/property_rules_template and as per example we can see that most simplest way to use it is

data "akamai_property_rules_template" "example" {
template_file = abspath("${path.root}/property-snippets/main.json")
}

Let’s convert it into TS

  • data akamai_property_rule_template converts to DataAkamaiPropertyRulesTemplate — You can see that “_” removed, and everything starts from caps.
  • template_file changed to templateFile using above logic
  • abspath is not a method in typescript so we used similar logic using path.join

Another way is to look into ts file generated in provider folder on your dev machine, in my case it is .gen/provider/akamai to find resource/data source definition and attributes requirement.

Next, const block i.e.

const propertyRuleTemplate = new 

is just a way to instantiating the new object in JS or TS. We are going to refer or use this object in further section.

Also look at other data sources defined for e.g. DataAkamaiGroup where we are using variables groupName and contractId defined in earlier section i.e. where we declared variables.

const group = new DataAkamaiGroup(this, 'group', {
groupName: groupName.value,
contractId: contractId.value
})

Let’s explore the next section of code block i.e. about defining terraform resources. More about terraform resource block here.

/** Section 4- Defining Resource block*//**  Defining Akamai EHN Resource */
const ehn = new EdgeHostname(this, 'ehn', {
productId: productId.value,
contractId: contract.id,
groupId: group.id,
edgeHostname: ehnHostname.value,
ipBehavior: 'IPV4'
})
/** Defining Akamai Property Resource */
const property = new Property(this, 'property',{
name: propertyName.value,
contractId: contract.id,
groupId: group.id,
productId: productId.value,
ruleFormat: 'latest',
hostnames: [{
cnameFrom: "cdktf-demo.akamaidevelopers.com",
cnameTo: ehn.edgeHostname,
certProvisioningType: "CPS_MANAGED"
}],
rules: propertyRuleTemplate.json
})
/** Defining Akamai Property Activation Resource */
new PropertyActivation(this, 'propertyActivation', {
propertyId: property.id,
contact: ["your@email.id"],
version: property.latestVersion,
network: "STAGING",
note: "Created via. CDK"
})

This block consist of 3 resource sections namely, EdgeHostname, Property & PropertyActivation. Again you can look at resource definition and attribute details from akamai terraform registry. You might notice that how for some attribute like contractId we have used data source defined in earlier section.

Please note that in our example code we are creating 3 resources namely

  • new edgehostname(you cname your domain to this akamai hostname),
  • delivery configuration(property block)
  • activating the delivery configuration

There are other resources as well that you can manage as part of stack, for example content provider code(or cp code) or complete application security module but for sake of simplicity we are just picking minimum bare.

5. Build the stack — Run below command to build the stack

cdktf deployakamai  Initializing the backend...
akamai Initializing provider plugins...
akamai - Reusing previous version of akamai/akamai from the dependency lock file
akamai - Using previously-installed akamai/akamai v2.3.0
akamai Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
akamai data.akamai_group.group (group): Reading...
akamai data.akamai_property_rules_template.propertyRuleTemplate (propertyRuleTemplate): Reading...
akamai data.akamai_property_rules_template.propertyRuleTemplate (propertyRuleTemplate): Read complete after 0s [id=da39a3ee5e6b4b0d3255bfef95601890afd80709]
akamai data.akamai_group.group (group): Read complete after 4s [id=xxxxxxx]
akamai data.akamai_contract.contract (contract): Reading...
data.akamai_contract.contract (contract): Read complete after 0s [id=xxxxxx]
akamai akamai_edge_hostname.ehn (ehn): Refreshing state... [id=xxxxxx]
akamai akamai_property.property (property): Refreshing state... [id=xxxxxxx]
akamai akamai_property_activation.propertyActivation (propertyActivation): Refreshing state... [id=xxxxxxx:STAGING]

If all good then you will get a prompt to deploy the changes

Approve(press enter) the change to deploy resources. After this you will see that resources will get started to provisioned and aftersome time you can go into Akamai Control Centre(GUI) to cross check if resources has been provisioned or not.

Thank you for reading this article. I hope this article will encourage you to give it a try and explore managing your akamai configuration as code.

Code Repo — https://github.com/deepakjd2004/cdktf-akamai

--

--