Aller au contenu
Infrastructure as Code medium

Les workspaces Terraform

35 min de lecture

logo terraform

Un workspace Terraform donne à une même configuration plusieurs instances d'état. Un seul jeu de fichiers .tf, plusieurs états logiques, et une expression, terraform.workspace, pour adapter ce que la configuration produit. Le workspace default existe dès le premier init, et les autres se créent avec terraform workspace new.

Tout ce qui suit a été exécuté sur Terraform v1.15.4, messages et codes de retour compris. Deux idées répandues n'y survivent pas, celle du workspace default qu'on pourrait supprimer en basculant ailleurs, et celle du delete qui détruirait ce que le workspace gérait.

  • Créer, lister et sélectionner un workspace
  • Terraform range les états, et il range la sélection courante
  • Dériver le nommage et le dimensionnement de terraform.workspace
  • Sélectionner un workspace sans interaction, pour une chaîne d'intégration
  • Retirer un workspace sans laisser d'objet orphelin
  • Reconnaître les cas où un workspace n'est pas la bonne réponse
  • Terraform 1.11 ou plus récent (installer Terraform). Les mesures viennent de la branche stable du moment, 1.15.x.
  • Comprendre le state Terraform, puisqu'un workspace n'isole que cela.

Trois commandes suffisent à tout le cycle courant.

  1. Lister ce qui existe. L'astérisque marque le workspace courant :

    Fenêtre de terminal
    terraform workspace list
    * default
  2. Créer un workspace, ce qui bascule dessus dans la foulée :

    Fenêtre de terminal
    terraform workspace new dev
    Created and switched to workspace "dev"!
    You're now on a new, empty workspace. Workspaces isolate their state,
    so if you run "terraform plan" Terraform will not see any existing state
    for this configuration.
  3. Basculer vers un workspace existant :

    Fenêtre de terminal
    terraform workspace select default
    Switched to workspace "default".

    Si le workspace n'existe pas, Terraform le dit et propose les deux issues :

    Workspace "recette" doesn't exist.
    You can create this workspace with the "new" subcommand
    or include the "-or-create" flag with the "select" subcommand.

L'option -or-create évite justement de tester l'existence avant de basculer, ce qui en fait le motif employé en intégration continue :

Fenêtre de terminal
terraform workspace select -or-create recette

Deux emplacements différents, et les confondre est une source d'erreurs. Sur un backend local, default garde son état à la racine, et chaque autre workspace reçoit le sien sous terraform.tfstate.d/ :

terraform.tfstate <- default
terraform.tfstate.d/dev/terraform.tfstate
terraform.tfstate.d/prod/terraform.tfstate

Le workspace sélectionné, lui, ne fait partie d'aucun état : il est écrit dans .terraform/environment, un fichier local, dans un répertoire ignoré par git.

Fenêtre de terminal
cat .terraform/environment
dev

C'est l'expression terraform.workspace qui rend une configuration unique utilisable par plusieurs environnements, sans y écrire aucun nom en dur :

resource "local_file" "app" {
count = terraform.workspace == "prod" ? 3 : 1
filename = "${path.root}/sorties/app-${terraform.workspace}-${count.index}.conf"
content = "environnement ${terraform.workspace} instance ${count.index}\n"
}

Le même code produit un fichier dans dev et trois dans prod. Une variable ne ferait pas ce travail : elle garde la même valeur d'un workspace à l'autre tant qu'on ne la surcharge pas à chaque commande.

En automatisation, on ne veut pas dépendre d'une sélection implicite laissée par une commande précédente. La variable d'environnement TF_WORKSPACE désigne le workspace pour la commande en cours, sans toucher à la sélection locale :

Fenêtre de terminal
TF_WORKSPACE=prod terraform output
workspace_actif = "prod"

Mesuré : après cette commande, .terraform/environment contient toujours sa valeur précédente. C'est précisément ce qu'on attend d'une chaîne d'intégration, qui ne doit rien laisser derrière elle.

Quand on scinde un projet, on veut parfois qu'un nouveau workspace démarre avec un état déjà rempli plutôt que vide :

Fenêtre de terminal
terraform workspace new -state=ancien.tfstate reprise

C'est l'opération qui demande le plus d'attention, parce que la commande porte un nom qui suggère plus qu'elle ne fait.

Fenêtre de terminal
terraform workspace delete bac-a-sable
Error: Workspace is not empty
Workspace "bac-a-sable" is currently tracking the following resource instances:
- local_file.app[0]
- random_pet.temoin
Deleting this workspace would cause Terraform to lose track of any associated
remote objects, which would then require you to delete them manually outside
of Terraform. You should destroy these objects with Terraform before deleting
the workspace.

Le garde-fou est clair, et l'ordre correct l'est tout autant : on détruit, puis on supprime.

Fenêtre de terminal
TF_WORKSPACE=bac-a-sable terraform destroy -auto-approve
terraform workspace delete bac-a-sable

Trois conditions doivent donc être réunies pour qu'un delete aboutisse : le workspace existe, il ne suit aucune ressource, et ce n'est pas le workspace courant.

Le mécanisme est en place. Voici ce qui casse en pratique, chaque cas ayant été rejoué.

C'est le piège le plus coûteux, parce que la commande réussit :

Fenêtre de terminal
terraform workspace delete -force prod
Deleted workspace "prod"!
WARNING: "prod" was non-empty.
The resources managed by the deleted workspace may still exist,
but are no longer manageable by Terraform since the state has been deleted.

Mesuré : les trois fichiers produits par prod sont toujours sur le disque après cette commande, et plus aucun état ne les connaît. Ce sont des orphelins, qu'il faudra retrouver et supprimer à la main.

Beaucoup de guides expliquent qu'il suffit de basculer ailleurs avant de supprimer. C'est vrai pour un workspace ordinaire, et faux pour default. Depuis default lui-même :

Workspace "default" is your active workspace.
You cannot delete the currently active workspace. Please switch
to another workspace and try again.

Ce message invite bien à basculer. Mais une fois basculé, le contrôle suivant répond :

Cannot delete the default workspace

Basculer ne débloque donc rien : default n'est pas supprimable, et le conseil habituel envoie le lecteur dans un mur.

Voici le fait le plus contre-intuitif de cette page, vérifié sur un projet neuf. Si TF_WORKSPACE désigne un workspace qui n'existe pas, la commande ne proteste pas : elle le crée.

Fenêtre de terminal
TF_WORKSPACE=prd terraform plan

Après ce simple plan, terraform.tfstate.d/prd/ existe, et un apply qui suivrait y écrirait son état. Une faute de frappe dans une variable d'environnement suffit donc à fabriquer un environnement parallèle, sans le moindre avertissement.

Deux commandes refusent de tourner tant que TF_WORKSPACE est posé

Section intitulée « Deux commandes refusent de tourner tant que TF_WORKSPACE est posé »

L'effet de bord est documenté, et il déroute quand on l'oublie dans un shell interactif :

The selected workspace is currently overridden using the TF_WORKSPACE
environment variable.
To select a new workspace, either update this environment variable or unset
it and then run this command again.

workspace select et workspace new sortent en 1 dans ce cas. La documentation déconseille formellement de poser cette variable dans un shell interactif, précisément parce qu'on oublie qu'elle est là.

Parler de « backend distant » en général induit en erreur. Dix backends seulement gèrent plusieurs workspaces nommés : AzureRM, Consul, COS, GCS, Kubernetes, Local, OSS, Postgres, Remote et S3. Les autres n'exposent qu'un seul état, et la question ne se pose donc pas.

Le nom doit rester valide dans un segment d'URL, pour que tous les backends puissent le stocker sans échappement :

The workspace name "avec/slash" is not allowed. The name must contain only URL
safe characters, contain no path separators, and not be an empty string.

Le cas d'usage que la documentation met en avant est le court terme : un workspace adossé à une branche de fonctionnalité, default portant la production, le workspace temporaire étant détruit puis supprimé après la fusion.

Pour des environnements durables, la réponse officielle est différente, et elle tient en une phrase : « Workspaces are not appropriate for system decomposition or deployments requiring separate credentials and access controls », parce que les workspaces d'un répertoire de travail utilisent le même backend. L'alternative recommandée est un module réutilisable appelé par une configuration racine par déploiement, chacune avec son propre backend, ce que détaille séparer dev, staging et prod.

SymptômeCause probableCorrection
Workspace is not emptyle workspace suit encore des ressourcesterraform destroy d'abord, puis delete
Cannot delete the default workspacetentative de supprimer defaultil n'est pas supprimable, basculer ne change rien
You cannot delete the currently active workspacesuppression depuis le workspace lui-mêmebasculer sur un autre workspace, puis supprimer
Workspace "x" doesn't existsélection d'un workspace absentterraform workspace new x, ou select -or-create x
The selected workspace is currently overriddenTF_WORKSPACE est poséla retirer de l'environnement, ou l'aligner sur la cible
un environnement inconnu apparaît dans workspace listTF_WORKSPACE mal orthographié : la commande l'a créésupprimer le workspace fantôme, et fixer la variable
des ressources ne sont plus gérées par personnedelete -force sur un workspace non videles retrouver et les supprimer à la main : l'état est perdu
deux personnes appliquent au mauvais endroitla sélection vit dans .terraform/, non partagéeposer TF_WORKSPACE explicitement dans les commandes

Le lab un seul répertoire, trois états livre une configuration troué et un workspace hérité déjà appliqué, à retirer sans laisser d'orphelin. Les tests n'ouvrent aucun .tf : ils interrogent chaque workspace par TF_WORKSPACE, sans toucher à votre sélection, et lisent l'inventaire dans terraform.tfstate.d/. Le contrôle décisif applique la configuration dans un workspace témoin que vous n'avez jamais vu : une valeur en dur y produit le mauvais nom et tombe. Il se joue hors ligne.

  • Un workspace isole un état, et rien d'autre : ni backend, ni credentials, ni répertoire.
  • Sur backend local, default reste à la racine, les autres vont sous terraform.tfstate.d/.
  • La sélection courante vit dans .terraform/environment, fichier local jamais partagé.
  • terraform.workspace est ce qui adapte la configuration ; une variable ne suit pas le workspace.
  • select -or-create évite le test d'existence, et new -state= démarre un workspace depuis un état existant.
  • delete ne détruit rien : il refuse tant que le workspace suit des ressources.
  • -force réussit et laisse des orphelins : l'état disparaît, les objets restent.
  • default n'est jamais supprimable, et basculer ailleurs n'y change rien.
  • TF_WORKSPACE crée le workspace s'il est absent, et bloque select comme new tant qu'il est posé.
  • Pour des environnements durables à droits distincts, la réponse est une racine par déploiement, pas un workspace.

Les questions ci-dessous portent sur ce qui surprend en pratique : la suppression qui ne détruit rien, le workspace default qui résiste, et la variable d'environnement qui fabrique un environnement.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn