Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Arguments write-only Terraform

12 min de lecture

logo terraform

Les arguments write-only permettent de passer un secret à un provider Terraform sans que cette valeur soit jamais stockée dans le state ou le plan. C’est le dernier maillon de la chaîne de protection des données sensibles : une ressource éphémère génère ou récupère le secret, puis un argument write-only le transmet au provider lors de chaque opération. Terraform envoie la valeur au provider mais ne la conserve nulle part.

  • Déclarer un argument write-only : password_wo sur une ressource
  • Comprendre le mécanisme de version : _wo_version pour déclencher les mises à jour
  • Combiner ephemeral + write-only : zéro secret dans le state
  • Connaître le cycle complet : générer → stocker → récupérer → utiliser

Pensez à un coffre-fort avec une fente de dépôt :

# Argument classique : Terraform lit et écrit
state["password"] = "S3cr3t!" # stocké dans le state
current = state.get("password") # relu au plan suivant
# Argument write-only : Terraform écrit, mais ne stocke jamais
provider.set_password("S3cr3t!") # envoyé au provider
# rien dans le state — Terraform ne peut pas relire la valeur

Un argument write-only c’est une fente de dépôt : vous glissez la valeur, le provider la reçoit et l’utilise, mais personne ne peut la récupérer après. Terraform envoie la valeur à chaque opération parce qu’il ne la stocke pas.

Avec une resource classique, même marquée sensitive = true, le mot de passe d’une base de données se retrouve dans le state :

resource "aws_db_instance" "main" {
instance_class = "db.t3.micro"
engine = "postgres"
username = "admin"
password = var.db_password # ← stocké dans le state
}

Après un terraform apply, la valeur de password est lisible en clair dans terraform.tfstate. L’argument password_wo remplace ce comportement : le provider reçoit la valeur, mais Terraform ne la stocke jamais.

Les providers indiquent dans le Terraform Registry quels arguments sont write-only. Par exemple, la ressource aws_db_instance expose password_wo :

resource "aws_db_instance" "example" {
identifier = "my-database"
instance_class = "db.t3.micro"
allocated_storage = 5
engine = "postgres"
username = "admin"
skip_final_snapshot = true
password_wo = "MonMotDePasse!"
password_wo_version = 1
}

Deux points importants :

  • password_wo accepte la valeur du mot de passe. Terraform l’envoie au provider à chaque opération (plan, apply), mais ne la stocke ni dans le state ni dans le plan.
  • password_wo_version est un entier que Terraform stocke dans le state. Il sert à déclencher une mise à jour du mot de passe quand vous l’incrémentez.

Terraform ne stocke pas la valeur d’un argument write-only. Conséquence : il ne peut pas détecter si cette valeur a changé. Sans l’argument de version, Terraform enverrait toujours la même disposition au provider sans savoir qu’il faut déclencher une mise à jour.

Le pattern est simple :

  1. Déclarer : password_wo = <valeur> et password_wo_version = 1.

  2. Mettre à jour : changer la valeur et incrémenter la version à 2.

  3. Appliquer : Terraform détecte le changement de version dans le state et demande au provider d’appliquer la nouvelle valeur.

resource "aws_db_instance" "example" {
# ...
password_wo = "NouveauMotDePasse!"
password_wo_version = 2
}

Terraform voit que password_wo_version est passée de 1 à 2 dans le state. Il inclut la modification dans le plan et envoie la nouvelle valeur password_wo au provider AWS lors de l’apply.

Le pattern recommandé utilise une ressource éphémère pour générer le secret, puis le transmet via l’argument write-only :

ephemeral "random_password" "db_password" {
length = 16
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "aws_db_instance" "example" {
identifier = "my-database"
instance_class = "db.t3.micro"
allocated_storage = 5
engine = "postgres"
username = "admin"
skip_final_snapshot = true
password_wo = ephemeral.random_password.db_password.result
password_wo_version = 1
}

Avec ce pattern, aucune trace du mot de passe :

  • ephemeral "random_password" génère un mot de passe temporaire, jamais stocké dans le state ;
  • password_wo le transmet au provider AWS, qui l’utilise pour créer l’instance RDS ;
  • Terraform oublie la valeur dès que l’opération est terminée.

En production, vous ne voulez pas seulement générer un mot de passe : vous voulez aussi le stocker dans un coffre pour que les applications puissent le récupérer. Le cycle complet avec AWS Secrets Manager :

# 1. Générer un mot de passe éphémère
ephemeral "random_password" "db_password" {
length = 16
override_special = "!#$%&*()-_=+[]{}<>:?"
}
# 2. Stocker dans Secrets Manager (write-only aussi)
resource "aws_secretsmanager_secret" "db_password" {
name = "my-app/db-password"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string_wo = ephemeral.random_password.db_password.result
secret_string_wo_version = 1
}
# 3. Récupérer le secret via une ressource éphémère
ephemeral "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret_version.db_password.secret_id
}
# 4. Utiliser comme argument write-only sur l'instance RDS
resource "aws_db_instance" "example" {
identifier = "my-database"
instance_class = "db.t3.micro"
allocated_storage = 5
engine = "postgres"
username = "admin"
skip_final_snapshot = true
password_wo = ephemeral.aws_secretsmanager_secret_version.db_password.secret_string
password_wo_version = aws_secretsmanager_secret_version.db_password.secret_string_wo_version
}

Après un terraform apply sur cette configuration :

RessourceDans le state ?Contient le mot de passe ?
aws_secretsmanager_secretOuiNon (juste le nom et l’ARN)
aws_secretsmanager_secret_versionOuiNon (secret_string_wo est write-only)
aws_db_instanceOuiNon (password_wo est write-only)
ephemeral "random_password"Non
ephemeral "aws_secretsmanager_secret_version"Non

Le mot de passe n’apparaît nulle part dans les artefacts Terraform.

L’implémentation est spécifique à chaque provider. Voici les arguments write-only les plus courants :

ProviderRessourceArgument write-onlyArgument version
awsaws_db_instancepassword_wopassword_wo_version
awsaws_secretsmanager_secret_versionsecret_string_wosecret_string_wo_version
azurermazurerm_key_vault_secretvalue_wovalue_wo_version
azurermazurerm_mysql_flexible_serveradministrator_password_woadministrator_password_wo_version
ConceptArgument classiqueArgument write-only
Stocké dans le stateOuiNon
Stocké dans le planOuiNon
Accepte des valeurs éphémèresNonOui
Accepte des valeurs non-éphémèresOuiOui
Détection de changementAutomatique via le stateVia _wo_version
Envoi au providerUniquement si changementÀ chaque opération

1. Toujours utiliser un argument write-only quand il existe

Section intitulée « 1. Toujours utiliser un argument write-only quand il existe »

Si le provider expose password_wo, préférez-le à password. Même si vous protégez votre state avec du chiffrement, ne pas stocker le secret est toujours plus sûr que le chiffrer.

Ne changez jamais la valeur d’un argument write-only sans incrémenter la version correspondante. Sans cette discipline, le provider peut rater la mise à jour.

3. Garder la version dans une variable ou un local

Section intitulée « 3. Garder la version dans une variable ou un local »

Pour les cas complexes, centralisez la version :

locals {
db_password_version = 1
}
resource "aws_db_instance" "example" {
# ...
password_wo = ephemeral.random_password.db_password.result
password_wo_version = local.db_password_version
}
resource "aws_secretsmanager_secret_version" "db_password" {
# ...
secret_string_wo = ephemeral.random_password.db_password.result
secret_string_wo_version = local.db_password_version
}

Même si les arguments write-only acceptent des chaînes en dur, préférez toujours une ressource ephemeral comme source. Cela élimine aussi le risque d’un commit accidentel du secret dans le code.

SymptômeCause probableSolution
password_wo is not a valid argumentLe provider n’expose pas cet argument (version trop ancienne)Mettre à jour le provider AWS ≥ 5.87
Le mot de passe ne change pas après un applypassword_wo_version n’a pas été incrémentéeIncrémenter la version et relancer
password_wo et password définis en même tempsLes deux arguments sont mutuellement exclusifsSupprimer password et utiliser uniquement password_wo
Invalid reference to ephemeral valueValeur éphémère utilisée dans un attribut classiqueUtiliser dans un argument write-only ou un contexte autorisé
  1. Les arguments write-only (_wo) transmettent un secret au provider sans jamais le stocker dans le state ou le plan.
  2. Terraform envoie la valeur à chaque opération (plan et apply), puisqu’il ne la stocke pas.
  3. Le mécanisme de version (_wo_version) permet de déclencher une mise à jour quand le secret change.
  4. Le pattern recommandé est : ephemeral génère → write-only transmet → zéro secret dans le state.
  5. L’implémentation est spécifique au provider : consultez le Terraform Registry pour vérifier quels arguments sont write-only.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn