Aller au contenu
Infrastructure as Code medium

Quand utiliser (ou éviter) les workspaces Terraform

35 min de lecture

logo terraform

« Workspaces ou configurations séparées ? » se tranche par une propriété technique, pas par une préférence d'organisation : un bloc backend n'accepte aucune valeur nommée. Tout ce qui découle de là, ce que les workspaces peuvent faire varier et ce qu'ils ne pourront jamais, se déduit de cette seule phrase.

Tout ce qui suit a été exécuté sur Terraform v1.15.4. Deux idées répandues n'y survivent pas, celle des workspaces qui ne sépareraient pas l'état en backend distant, et celle de l'impossibilité d'avoir des droits différents par workspace.

  • Ce qu'un workspace isole, et surtout ce qu'il n'isole pas
  • La contrainte technique qui borne l'usage des workspaces
  • Ce qui peut varier par workspace, et ce qui ne le peut jamais
  • Les deux motifs de rejet que la documentation énonce
  • Comment faire circuler une donnée entre deux configurations séparées
  • Ce que coûte la découpe, chiffres à l'appui

C'est le point de départ, et il est plus étroit qu'on ne le croit. Un workspace donne à la configuration un état séparé. Les objets réels, eux, ne savent rien des workspaces.

La démonstration tient en trois commandes. On applique dans dev, qui produit un fichier, puis on bascule sur prod :

Fenêtre de terminal
terraform workspace new dev && terraform apply -auto-approve
terraform workspace new prod
terraform plan
# local_file.app will be created
Plan: 1 to add, 0 to change, 0 to destroy.

Le fichier produit par dev est pourtant toujours sur le disque. prod ne le voit pas, parce qu'il n'est pas dans son état, et l'apply suivant l'écrase sans jamais l'avoir vu.

Un bloc backend ne peut référencer aucune valeur nommée, ni variable, ni local, ni terraform.workspace. Mesuré :

terraform {
backend "local" {
path = "etats/${terraform.workspace}.tfstate"
}
}
Error: Variables not allowed
on main.tf line 3, in terraform:
3: path = "etats/${terraform.workspace}.tfstate"
Variables may not be used here.

L'init sort en 1. Tous les workspaces d'un répertoire de travail écrivent donc forcément dans le même backend, avec les mêmes droits d'accès à l'état. Aucune option ne contourne cela, et c'est de là que vient toute la liste des impossibilités qu'on lit ailleurs sans explication.

Voici la dissymétrie que beaucoup de guides ratent, et elle change la conclusion. Le même terraform.workspace, placé dans un bloc provider, passe sans broncher :

provider "tls" {
proxy {
url = "http://proxy-${terraform.workspace}.exemple.invalide:3128"
}
}

Mesuré : init et plan sortent en 0.

La conséquence est importante. On peut faire varier par workspace le rôle avec lequel les ressources sont créées, ce que la documentation du backend S3 montre elle-même :

provider "aws" {
assume_role = {
role_arn = var.workspace_iam_roles[terraform.workspace]
}
}

Une même infrastructure, des variations légères, les mêmes droits, le même backend. Le motif habituel est une map indexée par le workspace, avec un repli :

locals {
tailles = {
default = 1
dev = 2
prod = 8
}
}
output "taille" {
value = lookup(local.tailles, terraform.workspace, local.tailles["default"])
}

Mesuré : dev rend 2, prod rend 8. Le troisième argument de lookup est la valeur de repli, employée quand le workspace ne figure pas dans la map. C'est bien la forme à trois arguments qu'il faut : seule celle à deux arguments est dépréciée.

L'autre cas mis en avant par la documentation est le court terme, adossé au contrôle de version : « Non-default workspaces are often related to feature branches in version control. The default workspace might correspond to the main or trunk branch. » Le workspace naît avec la branche, et disparaît avec elle.

La documentation en énonce deux, et les guides n'en retiennent souvent qu'un.

Le premier concerne les droits : « Workspaces are not appropriate for [...] deployments requiring separate credentials and access controls. » Un état de production protégé par des droits distincts ne peut pas partager le backend d'un environnement de développement.

Le second concerne la décomposition : « Workspaces alone are not a suitable tool for system decomposition because each subsystem should have its own separate configuration and backend. » Découper un grand système ne se fait donc pas avec des workspaces, quels que soient les droits.

La réponse recommandée n'est pas « des répertoires », c'est un module réutilisable pour le code commun, plus une configuration racine par déploiement, chacune avec son propre backend. La racine, dit la documentation, « consists only of a backend configuration and a small number of module blocks ».

Ce n'est pas gratuit, et la documentation le chiffre elle-même : « Terraform installs a separate cache of plugins and modules for each working directory, so maintaining multiple directories can waste bandwidth and disk space. » Mesuré sur deux répertoires de ce guide, chaque cache pèse de l'ordre de 18 Mo. S'y ajoutent la mise à jour depuis le contrôle de version et la réinitialisation de chaque répertoire séparément.

Une fois séparées, deux configurations ne se parlent plus. La passerelle officielle est la lecture de l'état distant de l'autre, par ses outputs déclarés :

data "terraform_remote_state" "socle" {
backend = "local"
config = {
path = "../socle/terraform.tfstate"
}
}

La documentation signale le prix de ce couplage : terraform_remote_state « creates a tighter coupling between configurations ». Les alternatives qu'elle cite évitent ce couplage direct, en passant par des conventions : une ressource étiquetée puis retrouvée par une source de données sur ses tags, un enregistrement DNS à nom prévisible, ou un espace de clés partagé.

Non, les workspaces ne partagent pas un seul état en backend distant

Section intitulée « Non, les workspaces ne partagent pas un seul état en backend distant »

C'est l'erreur de modèle mental la plus coûteuse. « For remote state, the workspaces are stored directly in the configured backend » : les états sont bien séparés. Sur S3, chaque workspace non default vit sous <workspace_key_prefix>/<workspace_name>/<key>, le préfixe valant env: par défaut.

Ce qui est partagé, ce n'est pas l'état, c'est le backend : le bucket, et les droits qui y donnent accès.

Dix seulement le font : AzureRM, Consul, COS, GCS, Kubernetes, Local, OSS, Postgres, Remote et S3. Raisonner comme si tout backend distant les supportait mène à une architecture qui ne tient pas.

On lit souvent que les workspaces CLI seraient réservés au poste de travail. La documentation dit l'inverse : « Workspaces are also meant to be a shared resource. They are not private, unless you use purely local state and do not commit your state to version control. »

Ce qui est local, c'est seulement la sélection courante, rangée dans le répertoire ignoré .terraform. Mesuré, c'est le fichier .terraform/environment.

Ce que vous devez faire varierWorkspacesConfigurations séparées
une taille, un nombre d'instancesouipossible, mais lourd
un nom dérivé de l'environnementouipossible
le rôle qui crée les ressourcesoui, par le provideroui
l'emplacement de l'étatnon, jamaisoui
les droits d'accès à l'étatnon, jamaisoui
le découpage en sous-systèmesnonoui
une branche de fonctionnalité éphémèreouidisproportionné
SymptômeCause probableCorrection
Error: Variables not allowed dans un backendune valeur nommée dans le bloc, terraform.workspace comprisce besoin ne relève pas des workspaces : découper en configurations
un workspace écrase les objets d'un autreles noms ne dérivent pas de terraform.workspacedériver tous les noms d'objets du workspace
deux équipes voient l'état l'une de l'autreun backend unique pour tous les workspacesune racine par périmètre, chacune son backend
une valeur d'un autre périmètre est recopiée à la mainpas de passerelle entre racinesterraform_remote_state, ou une convention de tags
le backend choisi ne connaît pas les workspacesbackend hors des dix pris en chargechanger de backend, ou séparer les configurations
init réinstalle tout dans chaque répertoirecache de plugins par répertoire de travailc'est le coût attendu de la découpe

Le lab workspaces ou configurations séparées livre une configuration à arbitrer, dont l'en-tête précise que la production est gérée par une autre équipe avec ses propres droits. Vous découpez ce qui doit l'être en deux racines, vous rebranchez leur dépendance par terraform_remote_state, et vous laissez dans un workspace ce qui ne varie que par la taille. Le contrôle décisif rejoue le socle avec une autre plage réseau : une valeur recopiée à la main reste figée et tombe. Il se joue hors ligne.

  • Un workspace isole un état, jamais les objets : deux workspaces qui visent le même objet s'écrasent en silence.
  • Un bloc backend n'accepte aucune valeur nommée : c'est la contrainte dont tout le reste découle.
  • Un bloc provider, lui, accepte les expressions : le rôle qui crée les ressources peut varier par workspace.
  • Les credentials du backend et ceux du provider ne protègent pas la même chose.
  • En backend distant, les états sont séparés (env: sur S3) ; c'est le backend qui est commun.
  • La documentation pose deux motifs de rejet : droits distincts, et décomposition de système.
  • L'alternative est un module commun plus une racine par déploiement, pas « des répertoires ».
  • La découpe coûte : un cache de plugins par répertoire, et une réinitialisation par répertoire.
  • Entre deux racines, la donnée passe par les outputs d'un état distant, au prix d'un couplage plus fort.
  • Dix backends seulement gèrent des workspaces nommés.

Les questions ci-dessous portent sur ce qui tranche en pratique : ce que le backend refuse, ce que le provider accepte, et ce que la séparation coûte vraiment.

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