Terraform is used by thousands of teams across the world to provision and manage their infrastructure. It is a powerful tool - which enables platform teams to build valuable, but also complicated, environments and services for their application teams.
Many platform teams want to make those environments and services available on-demand to application teams. This is where Kratix can help - by providing the right abstraction on top of existing Terraform, application teams can self-serve the infrastructure resources they need, when they need them without having to understand the complexity of the Terraform serving up those resource behind the scenes.
The team at Syntasso has recently introduced a few features to Kratix that make integrating with Hashicorp Cloud Platform or Terraform Enterprise an amazing experience.
In this blog post, we will cover how you can get the integration up and running.
How does Kratix scheduling work?
Before we jump into the integration itself, let's quickly recap how Kratix schedules resources.
A Promise contains Workflows that are triggered in response to an event - like the Promise itself being installed or a user requesting a new instance of the promised service.
A Workflow comprises a series of containers that execute arbitrary code. They can output a set of documents. Any documents they do output will be written to the State Store (usually a Git repository) associated with the Destination that matches the Promise's scheduling rules.
A Kratix Destination represents a system that can reconcile on the documents that are being written by Kratix into the State Store. Destinations are often other Kubernetes Clusters, but they can be anything: VMs, Cloud Providers, PaaS, etc.
Integrating Kratix with HCP is not different than integrating Kratix with any other Destination and it's done in two steps:
You must configure Kratix to write Terraform documents to a Git repository,
You must configure HCP to reconcile on changes to that Git repository.
The next sections will cover the above steps in more details.
Configuring Kratix to write to Terraform destinations
First, deploy Kratix in your Kubernetes cluster. You can find installation guides for the different clouds providers here. With Kratix deployed, create a GitStateStore pointing to a Git repository using your provider of choice. Details on how to create a GitStateStore can be found here.
With your GitStateStore created, you can create a new Kratix Destination representing HCP. You can find out how to create a Destination in the Kratix docs. Make sure to configure your Destination with spec.filepath.mode: none since HCP doesn't support nested directory structures.
Your Destination and GitStateStore configuration will look like the following:
The label in the Destination, in conjunction with the spec.strictMatchLabels ensure Kratix will only schedule workloads with matching labels. The Promise we will install later will contain the same values in its Destination Selector.
Setting up your HCP Workspace
You can now configure your HCP workspace to reconcile on the same Git repository you registered the Destination.
Navigate to your HCP page and create a new Workspace. Configure the Workspace workflow to use Version Control and follow the steps to select the correct repository.
When you configure the Destination, Kratix creates a directory with the Destination name in your Git repository. All documents scheduled to that Destination will be written to this subdirectory. So, when configuring your Terraform workflow, make sure to click in Advanced Options and set the Terraform Working Directory to the subdirectory within your Git repository.
You can also configure it to Auto-apply API, CLI, & VCS runs; this ensures no manual approval is needed to execute new plans.
With all of that setup, your system should look like the following:
Great! You can now install and use a Promise that schedules workloads to HCP.
Installing a Terraform Promise
The Promise used in this blog post is available at here. This Promise is providing S3 buckets via Terraform, that will in turn be scheduled to HCP. Let's look at this Promise in details.
Promise in details
Destination Selectors
As mentioned previously, the Destination Selectors in the Promise are set to the same values as in the destination. You can verify that by checking the spec.destinationSelectors in the Promise definition:
Promise API
The API for this Promise is quite simple: users can configure whether the S3 bucket is public or not. It also adds to Additional Printer Columns to inform the user of the bucket name and whether it is ready or not. Later, in the Workflows, you will see how those fields are going to be populated.
Workflows
This Promise includes two workflows: one for create/update events on the Promise itself, another for create/update events on the resources requested. Let's start with the Promise workflow.
Promise Configure Workflow
The Promise Configure workflows is executed whenever the Promise is installed or updated. It is not executed in response to a user's request for a new resource being promised.
The most common use-case for Promise Configure workflow is to create resources that need to exist prior to a user sending a request. For example, you may want to create a Route53 or a Security Group for the Promise, and then just refer to those on the Resource workflows.
For Terraform specifically, you can use the workflow to configure provider, or set up any custom module you will later refer in the Resource workflow, for example. In the S3 Promise, the Configure Workflow is made of a single Kratix Pipeline that executes the following:
The script is very straightforward. Since it's the same script used for both Promise and Resource Workflows, it starts by verifying if it's a Promise Workflow. If so, it outputs a single Terraform file which configures the AWS region.
Resource Configure Workflows
The Resource Configure Workflow is executed whenever the platform user sends a new request to the Promise, or whenever an existing resource is updated. This is the workflow that really defines what it means to deliver X-as-a-Service. The S3 Promise defines two Kratix Pipelines as part of the Resource Configure workflow.
The first Pipeline will execute the following script:
The Pipeline script is doing the following:
On lines 5~7, it reads the values provided by the user in their request
On lines 9~19, it sets the variables to be used in the generated Terraform file
On lines 21~46, it generates the Terraform document to be scheduled to HCP
On lines 48~52, it sets the new resource status
Note how the name of the bucket that the pipeline generates is randomised (line 13). This pipeline may run multiple times (for example, if the user updates the resource request). Therefore, it must ensure it's idempotent: running it twice must not produce two buckets with different names.
To ensure idempotency, the pipeline stores the generated name in the resource status (line 51). Next time the pipeline is executed, it first checks the presence of the bucket name in the resource status (line 10). It only generates a new name if it doesn't find the name there. You can find more on Pipeline Design Principles in the Kratix Docs.
Note how the name of the Terraform file the pipeline outputs is also unique (line 22). That is necessary to ensure that two requests won't interfere with each other's files at the State Store.
Once this pipeline executes, it will output the Terraform file that will be scheduled to HCP. However, at this stage, the user may not know when the bucket is actually ready, since they must wait for HCP to pick it up and apply the plan. The next pipeline defined in the Resource Configure Workflow exists to address this issue. Let's take a look at the pipeline script:
The script is very simple:
On line 5, it reads the bucket name from the resource status
On line 6, it builds the URL to check, derived from the bucket name
On lines 8~12, it continuously reaches the URL, until a non-404 status code is returned.
On lines 14~16, it sets the ready status on the resource to True.
Again, since the Bucket name is not deterministic, the second Pipeline has to rely on the resource status (as set by the previous pipeline) to get it. With that in hand, it can execute the logic to determine when the bucket is actually created. Finally, it updates the status so the user gets the feedback that their bucket is ready to be used.
Note that this is just an example. Regional buckets may have a different URL. You may get a non-404 status code, but the bucket itself may not be ready. You may want to verify if the desired Bucket ACL is being respected. We'll leave fixing those issues as an exercise to the reader.😉.
Applying the Promise
Now that you have a good understanding of how the Promise works, you can go ahead and install it in your Platform cluster:
Which will, in turn, run the Promise Configure Workflow:
At this stage, if you check your Git State Store, you should see a new main.tf file:
Similarly, you should see that this new file was picked up by HCP by navigating to your Workspace:
Great! That means that it's all wired up nicely. Go ahead and request a new Bucket:
Similar to when you installed the Promise, the Resource Configure Workflow will start and execute the two pipelines
Once the First pipeline completes, you should see a new file in your State Store:
Since your HCP Workspace is configured to reconcile on changes to that repository, you should also see it executing the reconciliation loop, which will eventually create the requested Bucket.
Finally, on the Platform, the user should eventually see the Bucket as Ready:
When the user deletes the bucket, Kratix will remove the Terraform file from the State Store, which in turn will trigger Terraform to destroy the bucket.
Taking it to the next level
Customers of Syntasso Kratix Enterprise can take their integration with HCP to the next level by leveraging the Enterprise HCP Aspects built by Syntasso. They enable Promise Writers to wait on Terraform plans being planned and/or executed, and provide an array of utilities to surface Terraform outputs to the platform users. To know more about the offering, reach out to the team or explore the docs.
Conclusion
In this blog post, we covered how you can take advantage of Kratix's latest features to integrate your Promises with HashiCorp Terraform Cloud (or Terraform Enterprise). The ability to schedule workloads to any type of destination is one of the most powerful features of Kratix, enabling Promise Writers to continue using the technologies they know and love while providing a consistent interface to the platform users.
If you have any questions or would like to learn more about integrating Kratix with HCP (or any type of destination, really), let us know in the comments below or send us a message on our community Slack.