Managing secret metadata in Terraform

I would like to manage secret metadata and the existence of secrets, in terraform files that I store in Git.

Since the terraform files are in git readable by everyone, I do not want to put the actual secrets there – the intention is that they will be filled in and updated independently (through the CLI or UI).

I thought that maybe the “disable_read” argument would do the trick, but it doesn’t.

Currently, I define my secrets like this:

resource "vault_kv_secret_v2" "apitoken" {
  mount                      = vault_mount.secret.path
  name                       = "apitoken"
  data_json                  = "{}"

  disable_read               = true
  lifecycle {
    ignore_changes = [ data, metadata ]
  }

  custom_metadata {
    max_versions = 5
    data = {
      usage = "This credential contains an API token."
      change-instructions = "API tokens can be generated on the server",
      max-age = "365"
    }
  }
}

It almost works, but if I update the metadata, then a new (empty) secret version is created.

Is there a pattern to achieve what I want?

This code is telling Terraform that you want there to be a secret with an empty JSON map ({}), so Terraform will remove whatever is already there any time this resource is updated.

One of the code principles of Terraform is that is manages the resources it is asked to manage. This is an all or nothing decision, meaning that any change made outside of Terraform will get reverted to whatever the code says the next time Terraform does an update. I do not wanna to mess the existing infrastructure.The use of ignore_changes (The lifecycle Meta-Argument - Configuration Language | Terraform | HashiCorp Developer) can be used to try to prevent updates where sometime else has made changes outside of Terraform, but it doesn’t help in all situations (for example if a resource is going to be recreated).

SO in this case you could try adding data_json to the ignore_changes list. I’m not sure why you have data, metadata in there currently.

In this case, the necessary “pattern” would be to implement a new kind of resource within terraform-provider-vault, which managed KVv2 metadata without managing the actual secret data.

1 Like

I’d upvote such a resource.

Thanks everyone for the discussion.

Just to close the loop, I did try stuart-c’s suggestion, and expanded the ignore_changes statement:

  lifecycle {
    ignore_changes = [data, data_json, metadata]
  }

Even though it doesn’t show a change necessary in the output of terraform plan, a new data version is created.

  # vault_kv_secret_v2.apitoken will be updated in-place
  ~ resource "vault_kv_secret_v2" "apitoken" {
        data_json           = (sensitive value)
        delete_all_versions = false
        disable_read        = true
        id                  = "secret/data/apitoken"
        mount               = "secret"
        name                = "apitoken"
        path                = "secret/data/apitoken"

      ~ custom_metadata {
            cas_required         = false
          ~ data                 = {
                "change-instructions" = "This credential contains an API token"
                "max-age"             = "365"
              ~ "usage"               = "API tokens can be regenerated on the server"
            }
            delete_version_after = 0
            max_versions         = 5
        }
    }