“In many cases, what you’re working on doesn’t have an answer on the internet. That usually means the problem is hard or important, or both.”
– Matt Rickard, Reflections on 10,000 Hours of Programming
As a services firm, PeerIslands is focused on helping customers with cloud-native application development and modernizing legacy applications. To that effect, we’ve built several tools that accelerate this transformation journey, while making it predictable. Our tools are cloud-native and can be deployed across any cloud platform using Terraform scripts, which are now the defacto IaaC standard for building immutable infrastructure.
I’ve taken an opportunity to explore building a custom Terraform provider for our tool that will make provisioning easier, while considering the state of IaaC solutions and where they are headed.
This blog details how you can build your own Terraform provider, the challenges you need to be prepared for, how Terraform is improving and making it easier for developers to build new providers, provision for infrastructure and alternative solutions.
Terraform and immutable infrastructure
The last decade has seen the rise of DevOps and IaaC practices where you write and execute code to define, deploy and manage your infrastructure. Of these IaaC tools, Terraform is the most widely used in a multi-cloud environment that falls into the provisioning tools category as well. Other cloud-specific solutions include tools such as AWS CloudFormation, Azure Resource Manager.
Terraform allows infrastructure to be expressed as code in a simple, human-readable language called HCL (HashiCorp Configuration Language). It reads configuration files and provides an execution plan of changes that can be reviewed for safety and then applied and provisioned.
Terraform supports managing infrastructure across most cloud providers. Extensible providers also allow Terraform to manage a broad range of resources, including IaaS, PaaS, SaaS, and hardware services.
You write IaaC using a custom DSL (Domain Specific Langage) called HashiCorp Configuration Language (HCL) or JSON. HCL follows a declarative programming style where you specify what you want and the rest is done by the provider.
For example, to create an S3 bucket in AWS, you would create a “main.tf” that would look like this. (Note: These snippets are meant to explain the core technology feature and do not follow the idiomatic style of Terraform, for example, defining variables, marking sensitive data, etc.)
Terraform core would make API calls based on the “main.tf” you defined to one or more providers (in this case the AWS provider) which in turn provisions the infrastructure needed.
Terraform maintains the state of your infrastructure and allows you to manage your infrastructure based on the state.
However, if you need to provision a storage bucket in GCP or in Azure, you would write separate “.tf” files for each. For example:
“If your tool or solution is a multi-cloud solution, then writing and maintaining multiple “.tf” files will soon become a challenging proposition both for the provider of the solution as well as consumer of the solution.”
This is where building a custom Terraform provider will come in handy. We can write a custom provider “Universal Blob Provider” that can provision a storage bucket on any cloud platform. You could then use a simplified HCL configuration, such as below.
One provider to provision your solution (in this case, a storage bucket) across all cloud providers.
The rest of the blog explains how you can develop your own custom Terraform provider using “ublob” a universal blob provider as an example. The code for the same is available in Github here.
Terraform custom provider development
We need a Terraform provider that can abstract the API provided by each of the cloud providers to create a storage bucket, and provide a simplified schema that can be used by the consumers of the solution/provider.
Terraform provider development is done in Golang. You would need a reasonable understanding of Go. While I am yet to spend the 10,000 hours on the language to master it as Matt Rickard says, I spent a few days on “Learning Go” by Jon Bodner to get started. Go has 25 keywords so far and a limited number of constructs which help with a smaller learning curve. And knowledge of C / C++ always comes in handy.
And for Terraform plugin development, the Terraform Hashicups Provider learning path will help you get a good understanding of how to develop custom providers.
A typical provider project structure for Terraform would look like this:
“main.go” is the Go entry point function.
I have a folder internal/ublob to organize provider-related code.
“provider.go” will define the implementations for Resources, Data Sources, and provider Configuration.
Resources are organized under “resource_<<name>>.go” file.
Here we provide
- Schema definition
- Implementation for Create, Read, Update and Delete actions for the resource
Build and install the provider as follows
➜ terraform-provider-ublob git:(master) ✗ go mod init terraform-provider-ublob ➜ terraform-provider-ublob git:(master) ✗ go fmt ./... ➜ terraform-provider-ublob git:(master) ✗ go mod vendor ➜ terraform-provider-ublob git:(master) ✗ make install go build -o terraform-provider-ublob mkdir -p ~/.terraform.d/plugins/hashicorp.com/rrajesh1979/ublob/0.0.3/darwin_amd64 mv terraform-provider-ublob ~/.terraform.d/plugins/hashicorp.com/rrajesh1979/ublob/0.0.3/darwin_amd64
The examples folder has jumpstart “.tf” files that you can use to provision the resources. You can customize them as required.
These are written in HCL as discussed earlier.
The following is a Terraform configuration file that provisions a storage bucket in the cloud platform of choice; in this case, I created a storage resource in each of the cloud platforms to test the provider we built.
The following commands would initialize and provision resources based on your configuration.
➜ terraform-provider-ublob git:(master) ✗ cd examples ➜ examples git:(master) ✗ terraform init && terraform apply –auto-approve
Note: You need to have aws-cli, gcloud, Azure CLI installed and configured.
Snapshot of the output is shown below.
While all this works great with Terraform HCL enforcing a strict schema that results in consistent code structure, developers would like to build code in languages that are familiar to them. This would be the right time to look at Terraform CDK and what the future of IaaC looks like and some alternate solutions that are coming up.
Cloud Development Kit for Terraform (CDKTF) allows you to use familiar programming languages to define and provision infrastructure. This gives you access to the entire Terraform ecosystem without learning HashiCorp Configuration Language (HCL) and lets you leverage the power of your existing toolchain for testing, dependency management, etc.
CDK for Terraform is currently in beta. And Terraform currently supports TypeScript, Python, Java, C#, and Go (experimental).
This is useful when you have complex configurations that require for loops, switch statements, etc.
CDKTF synthesizes infrastructure that you define in a supported programming language into JSON configuration files that Terraform can use to manage infrastructure. CDKTF also automatically extracts the schemas from existing Terraform providers or modules and generates the necessary code bindings for your application.
Alternate solution – Pulumi
Pulumi is a fairly new solution. Pulumi emerged from stealth mode in 2018 as a competitor for Terraform. Terraform and Pulumi hold a lot of similarities – primarily in the way they manage the state. The major difference is that Terraform supports HCL as the primary configuration approach and Pulumi allows you to use familiar general-purpose languages and tools to accomplish the same goals. While Terraform has CDKTF which provides similar options as we saw in our earlier section, it is still in Beta.
For example, you can build an S3 bucket using Typescript as seen below:
Having said that, Terraform is a tried and tested, stable solution for production workloads. Pulumi is a fast-growing ecosystem that allows developers to build infrastructure in a programming language of their choice.
Another strong contender is AWS Cloud Development Kit (CDK). AWS CDK is an open-source software development framework to define your cloud application resources using familiar programming languages.
Future of IaaC
Regardless of the solution, Infrastructure as Code (IaC) and immutable infrastructure are seeing significant adoption in the DevOps space. I see solutions such as CDKTF, AWS CDK, and Pulumi maturing further, to allow developers build immutable infrastructure in their programming language of choice. It would be interesting to see how these tools would keep up with the pace of development in each of the cloud platforms.
- Reflections on 10,000 Hours of Programming (matt-rickard.com)
- How to Develop a Custom Provider in Terraform (infracloud.io)
- Introduction to cdktf | Terraform – HashiCorp Learn
- CDKTF Architecture – Terraform by HashiCorp
- Pulumi vs. Terraform | Pulumi
- AWS Cloud Development Kit – Amazon Web Services