HashiCorp Terraform has become the go-to tool in the world of infrastructure provisioning. But one of the superpowers of Terraform is that its capabilities go beyond just infrastructure. Organizations can choose from hundreds of providers to provision or manage just about anything, including ordering pizzas or building your Spotify playlists.
LaunchDarkly also has a Terraform provider and we tend to get a lot of questions from users about how they should utilize it for their feature management needs. In this article, we’ll walk through tips and tricks to help you get started and some important considerations when using the LaunchDarkly Terraform provider.
Keep it secret, keep it safe
One of the most important things to remember when getting started with Terraform is that it needs permissions in order to provision resources. These permissions often come in the form of API keys, which allow the providers to retrieve the necessary resources to build whatever the user requests. What users sometimes forget is that Terraform works off a notion of “state,” or “what does the environment look like at the time of provisioning.”
Essentially, when you run a terraform apply
for the first time, Terraform will construct a state file with all the inputted values, including any variables you may have set. Terraform uses this file as a reference for future applies
to avoid building redundant resources and improve provisioning time. Unfortunately, if you haven’t taken the step to secure your variables, they’re going to appear in the output whenever you run a plan
or apply
.
Luckily, the good folks over at HashiCorp have thought about this and have a few different recommendations for protecting sensitive credentials.
Sensitive variables
In order to use the LaunchDarkly Terraform provider, you will need to create an API key for Terraform to use when you first run a terraform init
. A good practice for doing this, especially if you plan on sharing your Terraform configurations with other teams, is to create a variable file and define a variable for the LaunchDarkly access token, something like this:
variable "launchdarkly_access_token" {
type = string
description = "The Access Token for our LaunchDarkly account"
default = ""
sensitive = true
}
Now, LaunchDarkly API access tokens can be scoped to narrow their capabilities, but ultimately this is still a sensitive credential that only a select few should have access to. By adding the sensitive = true
line to our variable definition, we are instructing Terraform not to output this value when running a Terraform plan
or apply
. This is good if we are considering sharing the outputs of Terraform runs for auditing or tracking purposes, but unfortunately there’s another challenge we need to consider. The Terraform state file.
Remote state storage
Even when marking variables as sensitive, they will be readable to anyone with access to the Terraform state file. As a refresher, the Terraform state file is the ultimate source of truth for infrastructure deployments. It’s a record of all the provisioned resources and configurations from the first apply
and is updated to reflect any changes on subsequent deployments. This file may contain sensitive information, like LaunchDarkly API access tokens, and should be protected. One of the ways to handle state files that contain sensitive information is by storing them in a secured, remote location instead of on a local machine.
For remote state file storage there are two options: storing it in a secured storage solution (like Amazon S3) or using Terraform Cloud. One of the primary benefits of opting for Terraform Cloud is that the user is not responsible for any of the encryption or storage configurations, because it’s done automatically. Additionally, there are some added guardrails around access to the account and collaboration capabilities. For a full breakdown of the plans and features, check out the Terraform Cloud documentation.
Put it in Vault
For some organizations though, there is a need for an added layer of security for API keys. Highly-regulated industries with strict compliance guidelines might be looking for ways to further protect sensitive credentials and for that, you could use another HashiCorp product: Vault. HashiCorp Vault is a secrets management solution for protecting sensitive data and controlling access to credentials, among other things. Using the Vault K/V Secrets Backend, organizations can store and retrieve LaunchDarkly API access tokens as needed. This method also provides the ability to version access tokens, set revocation and rotation, policies and more. Vault also offers a Terraform provider, which enables Terraform to read secrets stored in Vault directly.
Don’t make one file to rule them all
Oftentimes when looking at intro Terraform examples, the user is instructed to create a main.tf
file and add their configurations there. This is great for testing and building some initial resources, but as the complexity of your deployment grows, the less beneficial this approach becomes. Fortunately, Terraform does not require that everything be in the same file for an apply
, just the same directory. The way that Terraform operates, directories need to contain three things: variable definitions, required providers, and resource/module configurations. As an example, this directory structure would be a valid Terraform configuration:
LaunchDarkly
|-- ld-project.tf
|-- ld-flags.tf
|-- ld-segments.tf
|-- variables.tf
|-- providers.tf
When you run the terraform init
and terraform plan
, Terraform will look through each of these directory files, install the necessary providers, and generate an output.
Why do this though? Well a couple of reasons. First, as we mentioned above, as an environment starts to scale out, the complexity is going to increase. The bigger the main.tf
file, the more time it’s going to take to debug in case things go wrong. Additionally, that main.tf
may include a bunch of resources from different tools.
It’s entirely possible that I could have a Terraform configuration that:
- Builds an Amazon EKS cluster
- Configures the underlying VPC security groups and networking policies
- Installs an application via the Kubernetes provider
- Hooks it up to a Datadog environment
- And creates a new LaunchDarkly project
Breaking apart each of those configurations into a separate file makes it easier for me to make adjustments or corrections when things go wrong.
The second reason for creating individual files for each resource is that I may add things later that have nothing to do with my initial configurations. And it provides me with a cleaner layout when making adjustments to those resources.
Setting up different files for flags, environments, segments, or anything else allows me to customize those files without having to comb through lines and lines of code. Also, if I share that configuration with someone else, it’s easier for them to identify what resources will be constructed when an apply
is run.
Be descriptive
The LaunchDarkly Terraform provider supports multiple fields for adding descriptions to the various resources you provision. It’s good practice to take advantage of these fields and add tags or descriptions that will be helpful to users that may see the changes in the UI and not know where they originated from. As an example, adding in the tag: created by Terraform
to flags will help in the clean up process. To avoid creating drift, it’s better to clean these flags up so the changes reflect in the state file instead of making changes to the UI directly.
Going beyond provisioning
Another thing that sometimes gets overlooked with Terraform is that you can go beyond just the initial provisioning phase. Terraform is great for building repeatable environments, but we know that in most cases we’ll need to make additional changes beyond just provisioning or destroying. The benefit of using the LaunchDarkly Terraform provider is that we can make these changes in code and see them reflected in our LaunchDarkly UI.
Remember that Terraform state keeps an updated record of the existing deployment. When changes are made to the code, Terraform checks these changes against the state file and then applies the changes. Typically, when you run a terraform apply
, the output generated will give you an overview of the resources that will be created, changed, or destroyed should you apply those changes. This means that we can make adjustments to some elements of our LaunchDarkly project using Terraform. Sometime examples could include:
- Adding new users to an existing segment
- Adding additional flags
- Creating flag triggers or webhooks
- Creating metrics for experimentation
As a caution, it’s best to think about adding or removing resources with the LaunchDarkly Terraform provider. Modifications to existing resources may result in needing to completely rebuild the resource instead of just applying the changes, i.e. changing the project key will result in a new project. If there are downstream dependencies in your LaunchDarkly configuration, rebuilding of resources could create additional problems. It’s a good practice to always check the output of your apply
to see exactly what will be changed, created, or destroyed if you complete the run.
Consider the cloud
As mentioned earlier, HashiCorp offers a tool called Terraform Cloud. This tool is built around collaboration for teams that are going to be working off the same environments being managed by Terraform. In addition to the earlier benefit of remote state storage, one of the other core benefits is the ability to hook it up to a VCS like GitHub or GitLab.
Why would this matter for our LaunchDarkly users? You could hook a GitHub repository up to Terraform Cloud which will then automatically plan out any proposed changes as you’re working on the code base. These repositories are not required to only contain Terraform configuration files either. You could connect Terraform Cloud to subdirectories within your broader application folder that contain Terraform code, like this:
From a LaunchDarkly perspective, this means we could have a dedicated folder within our code base that handles the Terraform configurations and then work on our application business as usual. As new flags or features are added to our application, we can make the necessary changes to our Terraform manifest and push the changes to LaunchDarkly all via a pull request.
As you can see there is so much to the world of Terraform and this is just a starting point for users. If you’re looking for additional information about what aspects of LaunchDarkly can be managed with Terraform make sure to read through our provider documentation.