I am working on a Terraform project where I generate a random password for a mongodb user and additionally create a secret in the AWS secrets manager that provides a mongodb connection string based on both that ephemeral random password and other connection information like a hostname. In this situation, I want the connection string secret to update any time that either someone wants the password to be regenerated (prompted by a version number being bumped in a Terraform file) or when the mongodb connection information changes. In this scenario, with all resources containing passwords being ephemeral, I cannot find a reasonable way to have Terraform resolve these dependencies automatically. Here is an example Terraform file, simplified for clarity:
// First, we create an AWS secret to store the user credentials in so they can stay the same even when the connection string changes.
resource "aws_secretsmanager_secret" "db_user" {
name = "db-user"
}
ephemeral "random_password" "db_user" {
length = 16
special = false
}
// The database credentials are stored here. We can't store the mongo connection string here because then we couldn't update the connection string without also generating a new random password.
resource "aws_secretsmanager_secret_version" "db_user" {
secret_id = aws_secretsmanager_secret.db_user.id
secret_string_wo = jsonencode({
db_user = "foo"
db_pass = ephemeral.random_password.db_user.result
})
secret_string_wo_version = 1
}
// We can get an ephemeral version of the previously created secret for use in the rest of the file.
ephemeral "aws_secretsmanager_secret_version" "db_user" {
secret_id = aws_secretsmanager_secret_version.db_user.secret_id
}
// A new secret to store a mongodb connection string.
resource "aws_secretsmanager_secret" "db_conn" {
name = "db-conn"
}
locals {
db_creds_ephemeral = jsondecode(aws_secretsmanager_secret_version.db_user.secret_string)
}
resource "aws_secretsmanager_secret_version" "db_conn" {
secret_id = aws_secretsmanager_secret.db_conn.id
secret_string_wo = jsonencode({
db_conn = "mongodb://${local.db_creds_ephemeral.db_user}:${local.db_creds_ephemeral.db_pass}@${var.mongo_host}"
})
// What do we do here??
secret_string_wo_version = TODO
}
The problem here is that I don’t have a good value to set aws_secretsmanger_secret_version.db_conn.secret_string_wo_versionto so that the secret is updated when either aws_secretsmanager_secret_version.db_useror var.mongo_host change.
The solution I’m trying is to use a time_static resource to generate a version as a UNIX timestamp, and triggering a replace of that resource when the dependent attributes change. This is an awkward solution (notably because you have to wait at least one second between updates to this secret or state will break).
resource "time_static" "db_conn_secret_wo_version" {
triggers = {
db_user = aws_secretsmanager_secret_version.db_user.version_id
domain_name = var.mongo_host
}
}
resource "aws_secretsmanager_secret_version" "db_conn" {
(...)
secret_string_wo_version = time_static.db_conn_secret_wo_version.unix
}
Another solution I could see is to use a replace_triggered_by lifecylcle argument in aws_secretsmanager_secret_version.db_conn, and to use a static secret_string_wo_version, but although that might work here it doesn’t work in any resource that needs to be updated in-place when a write-only value is changed instead of being replaced.
Another solution would be a per-provider change to how _wo_version attributes work. There is an issue for the terraform AWS provider that I think would allow for a more robust solution to this particular problem where the `secret_string_wo_version` is just set to an object containing all the values that the secret might depend on. However, that solution relies on every provider considering this best-practice and puts the responsibility on them to implement, so I don’t feel like this is a good solution for Terraform as a whole either.
Maybe a feature addition for Terraform could be a solution to this, where you could for example wrap a value in a function before assigning it to a write-only attribute that attaches dependency values to it so the provider knows when the write-only value is supposed to update.
So, what’s the best way of handling this situation currently? Are there any ideas that I haven’t thought of? Is this a gap in functionality that could be deserving of a new feature in Terraform?