DEV Community

drewmullen
drewmullen

Posted on

Fully Migrate Secrets Out Of Terraform Module State Without Breaking Existing Users

A few weeks ago I published a similar blog that shows how you can update your modules to optionally utilize ephemeral secrets, removing secrets from state for all new deployments. However, to maintain totally programmatic, older deployments still retained the secret.

This blog explores a method to fully remove secrets from state, even on old deployments. However, it requires manual effort on behalf of users and involves some risk.

Setup

V1 of your module had a resource which introduced a secret into state:

resource "tls_private_key" "legacy" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "vault_kv_secret_v2" "legacy" {
  mount     = "kvv2"
  name      = "mytls"
  data_json = jsonencode({ 
    private_key = tls_private_key.legacy.private_key_pem
  })
}
Enter fullscreen mode Exit fullscreen mode

tls_private_key.legacy.private_key_pem contains a secret value that is stored in state.

Update

By updating your module (V2) to either build new with a ephemeral from the start or sourcing the legacy private_key_pem from an ephemeral variable, we can remove the secret from state in both circumstances.

The kick is getting the secret from state to a tf variable

variable "secret_version" {
  description = "Increment to trigger a re-write of the Vault secret. Only relevant when use_ephemeral_key = true."
  type        = number
  default     = 1
}

variable "private_key_data" {
  description = "The private key data, sourced from a legacy private_key resource depending on use_ephemeral_key."
  type        = string
  ephemeral   = true
  sensitive   = true
  default     = null
}

removed {
  lifecycle {
    destroy = false
  }

  from = tls_private_key.legacy
}

ephemeral "tls_private_key" "ephemeral" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "vault_kv_secret_v2" "legacy" {
  mount        = "kvv2"
  name         = "mytls"
  data_json_wo = var.private_key_data != null ? jsonencode(
    { private_key = var.private_key_data }) : jsonencode(
    { private_key = ephemeral.tls_private_key.ephemeral.private_key_pem })
  data_json_wo_version = var.secret_version
}
Enter fullscreen mode Exit fullscreen mode

The code above offers 2 branches:

  • Legacy
  • New build

Legacy

For v1 users, if they extract the tls_private_key.legacy.private_key_pem value to var.private_key_data, the next run will remove the old resource from state and update the resource to use the write-only value!

New builds

For net new builds, do not set var.private_key_data, the new ephemeral resource will write to data_json_wo.

Final thoughts

Its possible you'll want to condition your secret producing resource (what I have as ephemeral.tls_private_key) you can add a conditional but keep these notes in mind.

Top comments (0)