Mount several kvv2 secret engine with Terraform

Hello there,

I have a K8S cluster with several namespaces representing different environments and in it, services that will fetch secrets in Vault.

To simplify the configuration of Vault, I am using Terraform and I want to create the following:

Secrets Engines
|- apps/
     |- dev/
          |- app1/
          |- app2/
     |- beta/
          |- app1/
          |- app2/

Here is my Terraform code:

resource "vault_mount" "kvv2-apps" {
  for_each = toset(keys(data.external.get_apps_tree.result))
  path        = "apps/${each.key}"
  type        = "kv-v2"
  description = "KV Version 2 secret engine mount for Apps"
}

data "external" "get_apps_tree" {
  program = ["python3", "${path.module}/script/fetch-apps.py"]

  query = {
    username = local.settings.bitbucket_tf_user
    password = local.settings.bitbucket_tf_token
  }
}

Output of my Python script:

apps_tree = tomap({                                       
  "dev/app1" = ""
  "dev/app2" = ""
  "beta/app1" = ""
  "beta/app2" = ""
})

When I apply Terraform, I am getting the following:

Secrets Engines
|- apps/dev/app1
|- apps/dev/app2
|- apps/beta/app1
|- apps/beta/app2

It is mounting a secrets engine for each key in my map, instead of using existing path created by a previous key.

If I mount my different secrets engines via Vault CLI, it works as wanted.

What am I missing here?

Thank you

The output you have shown looks reasonable and expected. I don’t understand what it is you see differently when using the CLI. Please explain further, preferably with an example.

Hey, thank you for your quick answer!

I think I a mixing everything… I just need to mount an engine apps and then create secrets within and setting the path for each app like that: dev/app1, dev/app2, etc.

resource "vault_mount" "kvv2-apps" {
  path        = "apps"
  type        = "kv-v2"
  description = "KV Version 2 secret engine mount for Apps"
}

resource "vault_kv_secret_v2" "kube_apps_secrets" {
  for_each  = toset(keys(data.external.get_apps_tree.result))
  mount     = vault_mount.kvv2-apps.path
  name      = each.key
  data_json = jsonencode(
  {
    name = each.key
  }
  )
}

It works as wanted!

I recommend against creating these “placeholder” secret values. There is no need to do so. Vault KV stores work a bit like Amazon S3 buckets - there is no such thing as a directory physically in storage, directories are just implied, based on prefixes ending with a slash character, in the full path to “files” stored.

Provided your Vault ACL policies allow it, the application will be able to write to apps/dev/app1/foo/bar/baz directly, whether or not these placeholder secrets exist.

Furthermore, if you do create these placeholder secrets, and the app creates the previously suggested apps/dev/app1/foo/bar/baz, and then you do vault list apps/dev/, then the result will be:

app1
app1/
...

i.e. it is permitted - but sometimes confusing! - for a “file” and a “directory” to exist at the same path, in a Vault KV store (or indeed S3 bucket).

Thank you for the advice, I will keep it mind.

But at the moment, our application will only have read permission on existing secrets stored on Vault.