Vault Provider

The Vault provider allows Terraform to read from, write to, and configure HashiCorp Vault.

This provider serves two pretty-distinct use-cases, which each have their own security trade-offs and caveats that are covered in the sections that follow. Consider these carefully before using this provider within your Terraform configuration.

Best Practices

We recommend that you avoid placing secrets in your Terraform config or state file wherever possible, and if placed there, you take steps to reduce and manage your risk. We have created a practical guide on how to do this with our opensource versions in Best Practices for Using HashiCorp Terraform with HashiCorp Vault:

Best Practices for Using HashiCorp Terraform with HashiCorp Vault

This webinar walks you through how to protect secrets when using Terraform with Vault. Additional security measures are available in paid Terraform versions as well.

Configuring and Populating Vault

Terraform can be used by the Vault administrators to configure Vault and populate it with secrets. In this case, the state and any plans associated with the configuration must be stored and communicated with care, since they will contain in cleartext any values that were written into Vault.

Currently, Terraform has no mechanism to redact or protect secrets that are provided via configuration, so teams choosing to use Terraform for populating Vault secrets should pay careful attention to the notes on each resource's documentation page about how any secrets are persisted to the state and consider carefully whether such usage is compatible with their security policies.

Please see the Vault Policies and the Terraform Vault Provider guide for details on token capabilites.

Using Vault credentials in Terraform configuration

Most Terraform providers require credentials to interact with a third-party service that they wrap. This provider allows such credentials to be obtained from Vault, which means that operators or systems running Terraform need only access to a suitably-privileged Vault token in order to temporarily lease the credentials for other providers.

Currently, Terraform has no mechanism to redact or protect secrets that are returned via data sources, so secrets read via this provider will be persisted into the Terraform state, into any plan files, and in some cases in the console output produced while planning and applying. These artifacts must therefore all be protected accordingly.

To reduce the exposure of such secrets, the provider requests a Vault token with a relatively-short TTL (20 minutes, by default) which in turn means that where possible Vault will revoke any issued credentials after that time, but in particular it is unable to retract any static secrets such as those stored in Vault's "generic" secret backend.

The requested token TTL can be controlled by the max_lease_ttl_seconds provider argument described below. It is important to consider that Terraform reads from data sources during the plan phase and writes the result into the plan. Thus, a subsequent apply will likely fail if it is run after the intermediate token has expired, due to the revocation of the secrets that are stored in the plan.

Except as otherwise noted, the resources that read secrets from Vault are designed such that they require only the read capability on the relevant resources.

Provider Arguments

The provider configuration block accepts the following arguments. In most cases it is recommended to set them via the indicated environment variables in order to keep credential information out of the configuration.

The client_auth configuration block accepts the following arguments:

The headers configuration block accepts the following arguments:

Vault Authentication Configuration Options

The Vault provider supports the following Vault authentication engines.

Userpass

Provides support for authenticating to Vault using the Username & Password authentication engine.

For more details see: Userpass Auth Method (HTTP API)

The auth_login_userpass configuration block accepts the following arguments:

AWS

Provides support for authenticating to Vault using the AWS authentication engine.

For more details see: AWS Auth Method (API)

The auth_login_aws configuration block accepts the following arguments:

TLS Certificate

Provides support for authenticating to Vault using the TLS Certificate authentication engine.

For more details see: TLS Certificate Auth Method (API)

The auth_login_cert configuration block accepts the following arguments:

This login configuration honors the top-level TLS configuration parameters: ca_cert_file, ca_cert_dir, skip_tls_verify, tls_server_name

GCP

Provides support for authenticating to Vault using the Google Cloud Auth engine.

For more details see: Google Cloud Auth Method (API)

The auth_login_gcp configuration block accepts the following arguments:

This login configuration will attempt to get a signed JWT token if jwt is not specified. It supports both the IAM and GCE meta-data services as the token source.

Kerberos

Provides support for authenticating to Vault using the Kerberos Auth engine.

For more details see: Kerberos Auth Method (API)

The auth_login_kerberos configuration block accepts the following arguments:

This login configuration will attempt to get a SPNEGO init token from the service domain if token is not specified. The following fields are required when token is not specified: username, service, realm, krb5conf_path, keytab_path

Radius

Provides support for authenticating to Vault using the Radius Auth engine.

For more details see: Radius Auth Method (API)

The auth_login_radius configuration block accepts the following arguments:

OCI

Provides support for authenticating to Vault using the OCI (Oracle Cloud Infrastructure) Auth engine.

For more details see: OCI Auth Method (API)

The auth_login_oci configuration block accepts the following arguments:

OIDC

Provides support for authenticating to Vault using the OIDC Auth engine.

For more details see the OIDC specific documentation here: OIDC/JWT Auth Method (API)

The auth_login_oidc configuration block accepts the following arguments:

JWT

Provides support for authenticating to Vault using the JWT Auth engine.

For more details see the JWT specific documentation here: OIDC/JWT Auth Method (API)

The auth_login_jwt configuration block accepts the following arguments:

Azure

Provides support for authenticating to Vault using the Azure Auth engine.

For more details see the Azure specific documentation here: Azure Auth Method (API)

The auth_login_azure configuration block accepts the following arguments:

Token File

Provides support for "authenticating" to Vault using a local file containing a Vault token.

The auth_login_token_file configuration block accepts the following arguments:

Generic

Provides support for path based authentication to Vault.

The path-based auth_login configuration block accepts the following arguments:

Provider Debugging

Terraform supports various logging options by default. These are documented here.

Example Usage

provider "vault" {
  # It is strongly recommended to configure this provider through the
  # environment variables described above, so that each user can have
  # separate credentials set in the environment.
  #
  # This will default to using $VAULT_ADDR
  # But can be set explicitly
  # address = "https://vault.example.net:8200"
}

resource "vault_generic_secret" "example" {
  path = "secret/foo"

  data_json = jsonencode(
    {
      "foo"   = "bar",
      "pizza" = "cheese"
    }
  )
}

Example auth_login Usage

With the userpass backend:

variable login_username {}
variable login_password {}

provider "vault" {
  auth_login {
    path = "auth/userpass/login/${var.login_username}"

    parameters = {
      password = var.login_password
    }
  }
}

Or, using approle:

variable login_approle_role_id {}
variable login_approle_secret_id {}

provider "vault" {
  auth_login {
    path = "auth/approle/login"

    parameters = {
      role_id   = var.login_approle_role_id
      secret_id = var.login_approle_secret_id
    }
  }
}

Example auth_login With AWS Signing

Sign AWS metadata for instance profile login requests:

provider "vault" {
  address = "http://127.0.0.1:8200"
  auth_login {
    path = "auth/aws/login"
    method = "aws"
    parameters = {
      role = "dev-role-iam"
    }
  }
}

If the Vault server's AWS auth method requires the X-Vault-AWS-IAM-Server-ID header to be set by clients, specify the server ID in header_value within the parameters block:

provider "vault" {
  address = "http://127.0.0.1:8200"
  auth_login {
    path = "auth/aws/login"
    method = "aws"
    parameters = {
      role = "dev-role-iam"
      header_value = "vault.example.com"
    }
  }
}

Namespace support

The Vault provider supports managing Namespaces (a feature of Vault Enterprise), as well as creating resources in those namespaces by utilizing Provider Aliasing. The namespace option in the provider block enables the management of resources in the specified namespace. In addition, all resources and data sources support specifying their own namespace. All resource's namespace will be made relative to the provider's configured namespace.

Importing namespaced resources

Importing a namespaced resource is done by providing the namespace from the TERRAFORM_VAULT_NAMESPACE_IMPORT environment variable.

Given the following sample Terraform:

provider vault{}

resource "vault_mount" "secret" {
  namespace = "namespace1"
  path      = "secrets"
  type      = "kv"
  options = {
    version = "2"
  }
}

One would run the following import command:

TERRAFORM_VAULT_NAMESPACE_IMPORT=namespace1 terraform import vault_mount.secret secrets

Simple namespace example

provider vault{}

resource "vault_namespace" "secret" {
  path = "secret_ns"
}

resource "vault_mount" "secret" {
  namespace = vault_namespace.secret.path
  path      = "secrets"
  type      = "kv"
  options = {
    version = "1"
  }
}

resource "vault_generic_secret" "secret" {
  namespace = vault_mount.secret.namespace
  path      = "${vault_mount.secret.path}/secret"
  data_json = jsonencode(
    {
      "ns" = "secret"
    }
  )
}

Using Provider Aliases

The configuration below is a simple example of using the provider block's namespace attribute to configure an aliased provider and create a resource within that namespace.

# main provider block with no namespace
provider vault {}

# create the "everyone" namespace in the default root namespace
resource "vault_namespace" "everyone" {
  path = "everyone"
}

# configure an aliased provider, scope to the new namespace.
provider vault {
  alias     = "everyone"
  namespace = vault_namespace.everyone.path
}

# create a policy in the "everyone" namespace
resource "vault_policy" "example" {
  provider = vault.everyone

  depends_on = [vault_namespace.everyone]
  name       = "vault_everyone_policy"
  policy     = data.vault_policy_document.list_secrets.hcl
}

data "vault_policy_document" "list_secrets" {
  rule {
    path         = "secret/*"
    capabilities = ["list"]
    description  = "allow List on secrets under everyone/"
  }
}

Using this alias configuration, the policy list_secrets is created under the everyone namespace, but not under the "root" namespace:

$ vault policy list -namespace=everyone
default
vault_everyone_policy

$ vault policy list
default
root

Nested Namespaces

The example below relies on setting the namespace per resource from a single provider{} block. See the vault_namespace documentation for a slightly less elaborate example.

provider vault {}

variable "everyone_ns" {
  default = "everyone"
}

variable "engineering_ns" {
  default = "engineering"
}

variable "vault_team_ns" {
  default = "vault-team"
}

data "vault_policy_document" "public_secrets" {
  rule {
    path         = "secret/*"
    capabilities = ["list"]
    description  = "allow List on secrets under everyone/ namespace"
  }
}

data "vault_policy_document" "vault_team_secrets" {
  rule {
    path         = "secret/*"
    capabilities = ["create", "read", "update", "delete", "list"]
    description  = "allow all on secrets under everyone/engineering/vault-team/"
  }
}

resource "vault_namespace" "everyone" {
  path = var.everyone_ns
}

resource "vault_namespace" "engineering" {
  namespace = vault_namespace.everyone.path
  path      = var.engineering_ns
}

resource "vault_namespace" "vault_team" {
  namespace = vault_namespace.engineering.path_fq
  path      = var.vault_team_ns
}


resource "vault_policy" "everyone" {
  namespace = vault_namespace.everyone.path
  name     = "vault_everyone_policy"
  policy   = data.vault_policy_document.vault_team_secrets.hcl
}

resource "vault_policy" "vault_team" {
  namespace = vault_namespace.vault_team.path_fq
  name     = "vault_team_policy"
  policy   = data.vault_policy_document.vault_team_secrets.hcl
}

Using this configuration, the namespace and policy structure looks like so:

<root>/
  default
  root
  /everyone/
   default
   vault_everyone_policy
    /engineering/
      default
      /vault-team/
      default
      vault_team_policy

Verify the structure with vault directly:

$ vault namespace list
Keys
----
everyone/

$ vault namespace list -namespace=everyone
Keys
----
engineering/

$ vault namespace list -namespace=everyone/engineering
Keys
----
vault-team/

$ vault namespace list -namespace=everyone/engineering/vault-team
No namespaces found

$ vault policy list -namespace=everyone/engineering/vault-team
default
vault_team_policy

Tutorials

Refer to the Codify Management of Vault Enterprise Using Terraform tutorial for additional examples using Vault namespaces.