Write secret to Vault Enterprise with Terraform

I am trying to write a secret to my companies Vault (Enterprise) instance with the plan below.

provider "vault" {

resource "vault_generic_secret" "test" {
  path        = "kvtest/foo"
  data_json   = jsonencode(
      "test": "test"

terraform plan succeeds but terraform apply fails with Error: error writing to Vault: Error making API request. and permission denied. I have set TF_LOG=DEBUG and in that produced in the terraform plan see a request GET /v1/sys/internal/ui/mounts/kvtest/foo HTTP/1.1 to the correct namespace and host, however the response HTTP/2.0 403 Forbidden comes back.

The command vault kv put kvtest/foo test=test succeeds so I assume that permissions is setup ok and that something is missing in my Terraform configuration.

You may need to update the path to "kvtest/data/foo" as it looks like you’re using KVv2.

I’m assuming you’re attempting to write to a mount called “kvtest” in the root namespaces as well. Let me know if that’s not correct.

Thanks for the reply. I tried changing the the path to “kvtest/data/foo” and the result was still a 403 error.

I am trying to write to a namespace a couple of levels deeper than root, “cst/cio-automation/development” and VAULT_NAMESPACE is set to that. I can see the value as X-Vault-Namespace in the terraform debugging output.

It looks like the provider tries to determine whether the KV mount is v1 or v2 here, which may explain the particular error you’re getting.

Does your token have read capabilities on /sys/internal/ui/mounts and/or /sys/mounts/* in the target namespace?

1 Like

Thanks again for your reply. I’m able to get output from both the commands vault read -format json /sys/mounts/ and vault read -format json /sys/internal/ui/mounts. The outputs regarding kvtest are:


What about when calling vault read -format json sys/internal/ui/mounts/kvtest?

That works and produces …

  "request_id": "85335ba3-abed-04cb-b137-a2488c098e89",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": {
    "accessor": "kv_caa72584",
    "config": {
      "default_lease_ttl": 0,
      "force_no_cache": false,
      "max_lease_ttl": 0
    "description": "",
    "external_entropy_access": false,
    "local": false,
    "options": {
      "version": "2"
    "path": "kvtest/",
    "seal_wrap": false,
    "type": "kv",
    "uuid": "1cd87e2e-454c-60e7-8191-ed6931e988e1"
  "warnings": null

Does data.options.version = 2 mean that the engine KV-v2?

Yes, it does. You would not need the /data part of the path since it auto-detects the version.

You’re using the same token/namespace config to run these commands as Terraform would be using?

If so the only other thing I could think of offhand is that Terraform is failing to create a child-token but I think you’d see that error explicitly in the logs.