Vault
Manage Vault resources programmatically with Terraform
Use Terraform to manage policies, namespaces, and plugins in Vault.
Before you start
- You must have Terraform installed.
- You must have the Terraform Vault provider configured.
- You must have admin access to your Terraform installation. If you do not have admin access, you can still generate the relevant configuration files, but you will need to have someone else apply the changes.
- You must have a Vault server running.
Step 1: Create a resource file for namespaces
Terraform Vault provider supports a vault_namespace resource type for
managing Vault namespaces:
resource "vault_namespace" "<TERRAFORM_RESOURCE_NAME>" {
  path = "<VAULT_NAMESPACE>"
}
To manage your Vault namespaces in Terraform:
- Use the - vault namespace listcommand to identify any unmanaged namespaces that you need to migrate. For example:- $ vault namespace list Keys ---- admin/
- Create a new Terraform Vault Provider resource file called - vault_namespaces.tfthat defines- vault_namespaceresources for each of the new or existing namespaces resources you want to manage.- For example, to migrate the - adminnamespace in the example and create a new- devnamespace:- resource "vault_namespace" "admin_ns" { path = "admin" } resource "vault_namespace" "dev_ns" { path = "dev" }
Step 2: Create a resource file for secret engines
Terraform Vault provider supports discrete types for the different auth, secret, and database plugin types in Vault.
To migrate a secret engine, use the vault_mount resource type:
resource "vault_mount" "<TERRAFORM_RESOURCE_NAME>" {
  path = "<VAULT_NAMESPACE>"
  type = "<VAULT_PLUGIN_TYPE>"
}
To manage your Vault secret engines in Terraform:
- Use the - vault secret listcommand to identify any unmanaged secret engines that you need to migrate. For example:- $ vault secrets list | grep -vEw '(cubbyhole|identity|sys)' Path Type Accessor Description ---- ---- -------- ----------- transit/ transit transit_8291b949 n/a
- Use the - -namespaceflag to check for unmanaged secret engines under any namespaces you identified in the previous step. For example, to check for secret engines under the- adminnamespace:- $ vault secrets list -namespace=admin | grep -vEw '(cubbyhole|identity|sys)' Path Type Accessor Description ---- ---- -------- ----------- admin_keys/ kv kv_87edfc65 n/a
- Create a new Terraform Vault Provider resource file called - vault_secrets.tfthat defines- vault_mountresources for each of the new or existing secret engines you want to manage.- For example, to migrate the - transitand- admin_keyssecret engines in the example and enable a new- kvengine under the new- devnamespace called- dev_keys:- resource "vault_mount" "transit_plugin" { path = "transit" type = "transit" } resource "vault_mount" "admin_keys_plugin" { namespace = vault_namespace.admin_ns.path path = "admin_keys" type = "kv" options = { version = "2" } } resource "vault_mount" "dev_keys_plugin" { namespace = vault_namespace.dev_ns.path path = "dev_keys" type = "kv" options = { version = "2" } }
Step 3: Create a resource file for policies
Terraform Vault provider supports a vault_policy resource type for
managing Vault policies:
resource "vault_policy" "<TERRAFORM_RESOURCE_NAME>" {
  name = "<VAULT_POLICY_NAME>"
  policy = <<EOT
    <VAULT_POLICY_DEFINITION>
  EOT
}
To manage your Vault policies in Terraform:
- Use the - vault policy listcommand to identify any unmanaged policies that you need to migrate. For example:- $ vault policy list | grep -vEw 'root' default
- Create a Terraform Vault Provider resource file called - vault_policies.tfthat defines- vault_mountresources for each policy resource you want to manage in Terraform. You can use the following- bashcode to write all your existing, non-root policies to the file:- for vpolicy in $(vault policy list | grep -vw root) ; do echo "resource \"vault_policy\" \"vault_$vpolicy\" {" echo " name = \"$vpolicy\"" echo " policy = <<EOT" vault policy read $vpolicy echo "EOT" echo "}" echo "" done > vault_policies.tf
- Update the - vault_policies.tffile with any new policies you want to add. For example, to create a policy for the example- dev_keyssecret engine:- resource "vault_policy" "dev_team_policy" { name = "dev_team" policy = <<EOT path vault_mount.dev_keys_plugin.path { capabilities = ["create", "update"] } EOT }
Step 4: Update your Terraform configuration
- Create a - vaultdirectory wherever you keep your deployment configuration files for Terraform.
- Save your new resource files to your new Vault configuration directory. 
- Use - terraform fmtto adjust the formatting (if needed) of your new configuration files:- $ terraform fmt vault_namespaces.tf vault_secrets.tf vault_policies.tf
- Use - terraform validateto confirm the new configuration is valid:- $ terraform validate
Success! The configuration is valid.
```
Step 5: Import preexisting root-level resources
Use the terraform import command to import the preexisting root-level resources.
For example, import the admin namespace, default policy, and transit
plugin from the previous steps:
$ terraform import vault_namespace.admin_ns admin
vault_namespace.admin_ns: Importing from ID "admin"...
vault_namespace.admin_ns: Import prepared!
  Prepared vault_namespace for import
vault_namespace.admin_ns: Refreshing state... [id=admin]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
$ terraform import vault_policy.default_policy default
vault_policy.default_policy: Importing from ID "default"...
vault_policy.default_policy: Import prepared!
  Prepared vault_policy for import
vault_policy.default_policy: Refreshing state... [id=default]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
$ terraform import vault_mount.transit_plugin transit
vault_mount.transit_plugin: Importing from ID "transit"...
vault_mount.transit_plugin: Import prepared!
  Prepared vault_mount for import
vault_mount.transit_plugin: Refreshing state... [id=transit]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
Step 6: Import preexisting nested resources
To import resources that belong to a previously unmanaged namespace, you must
set the TERRAFORM_VAULT_NAMESPACE_IMPORT environment variable before importing.
For example, to import the admin_keys secret engine from the admin namespace:
- Set - TERRAFORM_VAULT_NAMESPACE_IMPORTto the- adminVault namespace:- $ export TERRAFORM_VAULT_NAMESPACE_IMPORT="admin"
- Import the - vault_mountresource- admin_keys:- $ terraform import vault_mount.admin_keys_plugin admin_keys vault_mount.admin_keys_plugin: Importing from ID "admin_keys"... vault_mount.admin_keys_plugin: Import prepared! Prepared vault_mount for import vault_mount.admin_keys_plugin: Refreshing state... [id=admin_keys] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform.
- Unset the - TERRAFORM_VAULT_NAMESPACE_IMPORTvariable when you finish importing child resources:- $ unset TERRAFORM_VAULT_NAMESPACE_IMPORT
Step 6: Verify the import
- Use the - terraform state showcommand to check your Terraform state file and verify the resources imported successfully. For example, to check the- admin_keysresource:- $ terraform state show vault_mount.admin_keys_plugin # vault_mount.admin_keys_plugin: resource "vault_mount" "admin_keys" { accessor = "kv_87edfc65" allowed_managed_keys = [] audit_non_hmac_request_keys = [] audit_non_hmac_response_keys = [] default_lease_ttl_seconds = 0 description = null external_entropy_access = false id = "admin_keys" local = false max_lease_ttl_seconds = 0 namespace = "admin" options = { "version" = "2" } path = "admin_keys" seal_wrap = false type = "kv"
- For each of the migrated resources, compare the - accessorvalue from your Terraform state to the accessor value in Vault. For example, to confirm the accessor for- admin_keys:- $ vault secrets list -namespace="admin" | grep -vEw '(cubbyhole|identity|sys)' Path Type Accessor Description ---- ---- -------- ----------- admin_keys/ kv kv_87edfc65 n/a
Step 7: Add new Vault resources
- Run - terraform planto confirm the new resources that Terraform will manage:- $ terraform plan vault_policy.default_policy: Refreshing state... [id=default] vault_namespace.admin_ns: Refreshing state... [id=admin/] vault_mount.transit_plugin: Refreshing state... [id=transit] vault_mount.admin_keys_plugin: Refreshing state... [id=admin_keys] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # vault_mount.dev_keys_plugin will be created + resource "vault_mount" "dev_keys" { + accessor = (known after apply) + audit_non_hmac_request_keys = (known after apply) + audit_non_hmac_response_keys = (known after apply) + default_lease_ttl_seconds = (known after apply) + external_entropy_access = false + id = (known after apply) + max_lease_ttl_seconds = (known after apply) + namespace = "dev" + options = { + "version" = "2" } + path = "dev_keys" + seal_wrap = (known after apply) + type = "kv" } # vault_namespace.dev_ns will be created + resource "vault_namespace" "dev" { + custom_metadata = (known after apply) + id = (known after apply) + namespace_id = (known after apply) + path = "dev" + path_fq = (known after apply) } # vault_policy.dev_team will be created + resource "vault_policy" "dev_team" { + id = (known after apply) + name = "dev_team" + policy = <<-EOT path vault_mount.dev_keys_plugin.path { capabilities = ["create", "update"] } EOT } Plan: 3 to add, 0 to change, 0 to destroy.
- Run - terraform applyto create the new resources:- $ terraform apply vault_namespace.dev_ns: Creating... vault_namespace.dev_ns: Creation complete after 0s [id=dev/] vault_mount.dev_keys_plugin: Creating... vault_mount.dev_keys_plugin: Creation complete after 0s [id=dev_keys] vault_policy.dev_team: Creating... vault_policy.dev_team: Creation complete after 0s [id=dev_team] Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
- Use the - terraform state showcommand to check your Terraform state file and verify the new resources created successfully. For example, to check the- dev_keysresource:- $ terraform state show vault_mount.dev_keys_plugin # vault_mount.dev_keys_plugin: resource "vault_mount" "dev_keys" { accessor = "kv_b3d2dd6f" allowed_managed_keys = [] audit_non_hmac_request_keys = [] audit_non_hmac_response_keys = [] default_lease_ttl_seconds = 0 description = null external_entropy_access = false id = "dev_keys" local = false max_lease_ttl_seconds = 0 namespace = "dev" options = { "version" = "2" } path = "dev_keys" seal_wrap = false type = "kv" }
- Confirm that your Vault instance can use the new resources. For example, to confirm the - dev_keysresources:- $ vault secrets list -namespace="dev" | grep -vEw '(cubbyhole|identity|sys)' Path Type Accessor Description ---- ---- -------- ----------- dev_keys/ kv kv_b3d2dd6f n/a
Next steps
- Review the best practices for programmatic Vault management.