Terraform

reading time 5 mins

This guide will show you how to use Doppler to securely read and write secrets in Terraform.

Prerequisites

  • You have created a project in Doppler
  • You are familiar with using Terraform to manage infrastructure

Passing Doppler Secrets as Input Variables

The Doppler CLI can be used to pass secrets directly to Terraform without the need for a provider at all.

# Automatically converts Doppler secrets to lowercase Terraform variables
doppler run --name-transformer tf-var -- terraform plan

The --name-transformer tf-var flag will transform the names of your Doppler secrets into the Terraform Environment Variable format.

For example, a variable named API_KEY will be converted into TF_VAR_api_key by the name transformer and Terraform will automatically find this value if you have a Terraform variable named api_key.

Read on to learn about the Doppler Terraform provider and more complex use cases.

Provider Overview

There are two main ways to use the Doppler Terraform Provider:

  • Reading Secrets
    • Fetch secrets from Doppler and use them in your Terraform configuration
    • Use the doppler_secrets Data Source
  • Managing Resources
    • Create, update, and delete Doppler secrets, projects, environments, configs, or service tokens from Terraform
    • Use the doppler_secret, doppler_project, doppler_environment, doppler_project, and doppler_service_token Resources
  • Managing Projects

Reading Secrets

In this example, we'll use the doppler_secrets data source to read secrets from a config:

# Install the Doppler provider
terraform {
  required_providers {
    doppler = {
      source = "DopplerHQ/doppler"
    }
  }
}

# Define a variable so we can pass in our token
variable "doppler_token" {
  type = string
  description = "A token to authenticate with Doppler"
}

# Configure the Doppler provider with the token
provider "doppler" {
  doppler_token = var.doppler_token
}

# Define our data source to fetch secrets
data "doppler_secrets" "this" {}

# Access individual secrets
output "stripe_key" {
  # nonsensitive used for demo purposes only
  value = nonsensitive(data.doppler_secrets.this.map.STRIPE_KEY)
}

To read Doppler secrets from Terraform, we just need to create a Service Token to provide read-only access to a specific config.

There are several ways to pass variables to Terraform, but for now, we can just provide our Doppler token when prompted by Terraform.

First, run terraform init to setup your Terraform project, then run terraform apply to test the configuration:

Terraform used the Doppler provider to fetch the secrets from your config.

All of the secrets that you load from Doppler will be loaded into Terraform as strings. You can use Terraform's built-in parsing functions to convert secrets to their Terraform types:

# Use `tonumber` and `tobool` to parse string values into Terraform primatives
output "max_workers" {
  value = nonsensitive(tonumber(data.doppler_secrets.this.map.MAX_WORKERS))
}

# JSON values can be decoded direcly in Terraform
# e.g. FEATURE_FLAGS = `{ "AUTOPILOT": true, "TOP_SPEED": 130 }`
output "json_parsing_values" {
  value = nonsensitive(jsondecode(data.doppler_secrets.this.map.FEATURE_FLAGS)["TOP_SPEED"])
}

Reading Secrets from Multiple Projects

In this example, we use multiple Doppler providers with aliases to access secrets from two separate configs. This can be useful when you need to use multiple access tokens that are scoped to a single project and config in your Terraform run.

terraform {
  required_providers {
    doppler = {
      source = "DopplerHQ/doppler"
    }
  }
}

variable "doppler_token_dev" {
  type = string
  description = "A token to authenticate with Doppler for the dev config"
}

variable "doppler_token_prd" {
  type = string
  description = "A token to authenticate with Doppler for the prd config"
}

provider "doppler" {
  doppler_token = var.doppler_token_dev
  alias = "dev"
}

provider "doppler" {
  doppler_token = var.doppler_token_prd
  alias = "prd"
}

data "doppler_secrets" "dev" {
  provider = doppler.dev
}

data "doppler_secrets" "prd" {
  provider = doppler.prd
}

output "port-dev" {
  value = nonsensitive(data.doppler_secrets.dev.map.PORT)
}

output "port-prd" {
  value = nonsensitive(data.doppler_secrets.prd.map.PORT)
}

Managing Secrets

In this example, we'll use the doppler_secret resource to write to secrets in our Doppler config:

# Install the Doppler provider
terraform {
  required_providers {
    doppler = {
      source = "DopplerHQ/doppler"
    }
  }
}

# Define a variable so we can pass in our token
variable "doppler_token" {
  type = string
  description = "A token to authenticate with Doppler"
}

# Configure the Doppler provider with the token
provider "doppler" {
  doppler_token = var.doppler_token
}

# Generate a random password
resource "random_password" "db_password" {
  length = 32
  special = true
}

# Save the random password to Doppler
resource "doppler_secret" "db_password" {
  project = "rocket"
  config = "dev"
  name = "DB_PASSWORD"
  value = random_password.db_password.result
}

# Access the secret value
output "resource_value" {
  # nonsensitive used for demo purposes only
  value = nonsensitive(doppler_secret.db_password.value)
}

To write Doppler secrets, we'll need to provide a Doppler Personal Token in our Terraform configuration. You can generate a new Personal Token from the Doppler dashboard on the "Tokens" page.

Similar to our example for reading secrets, we'll run terraform init and then terraform apply to test the configuration:

Terraform generated a new password and saved it to our Doppler config as DB_PASSWORD.

Terraform will still store all of this data in Terraform state, but having your secrets in Doppler allows for much easier access to these secrets for all developers on your team.

Managing Projects, Environments, and Configs

In this example, we'll use the doppler_project, doppler_environment, and doppler_config resources to manage a project in our Doppler workplace:

# Install the Doppler provider
terraform {
  required_providers {
    doppler = {
      source = "DopplerHQ/doppler"
    }
  }
}

# Define a variable so we can pass in our token
variable "doppler_token" {
  type        = string
  description = "A token to authenticate with Doppler"
}

# Configure the Doppler provider with the token
provider "doppler" {
  doppler_token = var.doppler_token
}

# Create and manage your project
resource "doppler_project" "rocket" {
  name        = "rocket"
  description = "To the moon and back."
}

# Create and manage your environments
resource "doppler_environment" "rocket_dev" {
  project = doppler_project.rocket.name
  name    = "Development"
  slug    = "dev"
}

resource "doppler_environment" "rocket_stage" {
  project = doppler_project.rocket.name
  name    = "Staging"
  slug    = "stage"
}

resource "doppler_environment" "rocket_ci" {
  project = doppler_project.rocket.name
  name    = "CI"
  slug    = "ci"
}

resource "doppler_environment" "rocket_prod" {
  project = doppler_project.rocket.name
  name    = "Production"
  slug    = "prod"
}

# Create and manage any branch configs you may want
resource "doppler_config" "rocket_ci_github" {
  project     = doppler_project.rocket.name
  environment = doppler_environment.rocket_ci.slug
  name        = "${doppler_environment.rocket_ci.slug}_github"
}

resource "doppler_config" "rocket_ci_circleci" {
  project     = doppler_project.rocket.name
  environment = doppler_environment.rocket_ci.slug
  name        = "${doppler_environment.rocket_ci.slug}_circleci"
}

We define a new Doppler project named rocket and set up four Environments in it (dev, stage, ci, and prod). We then specify two Branch Configs to manage under the ci environment.

Similar to previous examples, run terraform init and then terraform apply to test the configuration.

Managing Service Tokens

In this example, we'll use the doppler_service_token resource to manage a service token in our Doppler workplace:

# Install the Doppler provider
terraform {
  required_providers {
    doppler = {
      source = "DopplerHQ/doppler"
    }
  }
}

# Define a variable so we can pass in our token
variable "doppler_token" {
  type        = string
  description = "A token to authenticate with Doppler"
}

# Configure the Doppler provider with the token
provider "doppler" {
  doppler_token = var.doppler_token
}

# Create and manage your project
resource "doppler_project" "rocket" {
  name        = "rocket"
  description = "To the moon and back."
}

# Create and manage your environments
resource "doppler_environment" "rocket_ci" {
  project = doppler_project.rocket.name
  name    = "CI"
  slug    = "ci"
}

# Create and manage your service token
resource "doppler_service_token" "rocket_ci_token" {
  project = doppler_environment.rocket_ci.project
  config  = doppler_environment.rocket_ci.slug
  name    = "Rocket CI Token"
  access  = "read"
}

# Service token key available as `doppler_service_token.rocket_ci_token.key

We define a new Doppler project named rocket and set up a single ci environment. We then specify a service token for the rocket project under the ci environment. In this case, we're referencing the environment slug because this is the name for the root config in that environment. If you had Branch Configs under that environment, then you would want to reference a doppler_config resource for that config.

Similar to previous examples, run terraform init and then terraform apply to test the configuration.

Things to Watch Out For

Terraform Password Generation

Terraform has a built-in resource called random_password that can be used to seed passwords. If you use this to seed passwords that end up getting used in YAML configuration files, you may want to remove the # from the override_special list. Otherwise, you can end up with passwords with a # character in them which YAML will interpret as a comment when it gets parsed. In a worst case scenario, you may end up with a password like #1sjh13l91, which is stored in Doppler fine, but when fetched from Doppler and used in a YAML config file will end up loading as null.

πŸ‘

Awesome Work!

Now you know to use the Doppler Terraform provider to securely read and manage secrets for your infrastructure.


Did this page help you?