C’est précisément le moment où l’Infrastructure as Code (l’IaC, le fait de définir ses ressources sous forme de fichiers que l’on peut versionner et qui font office d’autorité) devient une évidence, et où l’on découvre que Terraform ne sait rien de ce qui existe déjà.
Jusqu’ici, rattraper ce retard coûtait cher : une commande terraform import par ressource, un bloc HCL à écrire à la main pour chacune, attribut par attribut, et un plan qui propose de tout détruire à la moindre ligne oubliée. Quarante ressources, une journée perdue. Clever Tools 5.3 et la version 3.0 de notre provider Terraform ramènent l’opération à trois commandes, clever features, clever terraform generate et terraform plan. Voici comment les enchaîner, et surtout ce qu’il faut vérifier avant d’appliquer.
Ce dont vous avez besoin
Trois outils suffisent : Terraform 1.5 ou plus récent (ou OpenTofu 1.6, le fork de Terraform, ou plus récent, qui implémente les fameux blocs d’import (import blocks)), Clever Tools 5.3 et le provider Clever Cloud en version 3.0. La commande qui nous intéresse est encore en bêta, il faut donc l’activer explicitement :
npm install -g clever-tools@latest
clever login
clever features enable terraform
Un mot sur notre provider
Open source dès ses débuts, publié sur les registries Terraform et OpenTofu, il couvre aujourd’hui l’essentiel de la plateforme : les runtimes, les bases de données et les add-ons managés, le stockage objet et les FS Buckets, les drains de logs, les Network Groups, les clusters Kubernetes et jusqu’aux consumers OAuth. Au-delà du provisionnement, il sait déployer votre code depuis un dépôt Git et épingler un commit, lier une application à ses add-ons comme le fait clever service link-addon, lire l’adresse du load balancer pour vos entrées DNS ou parcourir les sauvegardes d’une base. Depuis la version 3.0, il accepte un token d’API pour s’intégrer à une CI, et avec Terraform 1.14 et plus, ses actions redémarrent une application, exécutent une requête SQL sur un PostgreSQL ou envoient des fichiers dans un FS Bucket sans sortir d’un terraform apply. 48 ressources, deux data sources, trois actions : de quoi décrire une organisation entière, et pas seulement l’importer.
Étape 1 : déclarer le provider Terraform Clever Cloud
Dans un répertoire vide, un fichier provider.tf :
terraform {
required_providers {
clevercloud = {
source = "CleverCloud/clevercloud"
version = "~> 3.0"
}
}
}
provider "clevercloud" {}
Le bloc provider reste vide : après un clever login, le provider réutilise les identifiants du profil actif de Clever Tools. Il ne lui manque que l’organisation à cibler, passée par une variable d’environnement (un identifiant user_… fonctionne aussi pour un espace personnel) :
export CC_ORGANISATION="orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
terraform init
Étape 2 : générer les blocs import avec clever terraform generate
C’est ici qu’intervient la nouveauté de Clever Tools 5.3. La commande clever terraform generate parcourt une organisation et écrit, pour chaque application et chaque add-on, le bloc import que Terraform attend :
clever terraform generate --org orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx > imports.tf
Le fichier obtenu ressemble à ceci :
# mon-api
import {
to = clevercloud_nodejs.mon-api
id = "app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
# ma-base
import {
to = clevercloud_postgresql.ma-base
id = "postgresql_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Chaque application est rattachée à la ressource de son runtime (clevercloud_nodejs, clevercloud_php, clevercloud_docker, clevercloud_java_maven…), chaque add-on à la sienne (clevercloud_postgresql, clevercloud_redis, clevercloud_cellar…).
La commande fait aussi le tri à votre place. Un Keycloak, un Matomo, un Metabase ou un Otoroshi provisionnent leur propre application et leurs propres bases ; un Elasticsearch vient avec son Kibana. Ces ressources apparaissent dans votre organisation comme les autres, mais elles appartiennent à l’add-on qui les gère : les importer reviendrait à les confier à Terraform, qui se mettrait à les modifier en parallèle. clever terraform generate les ignore et le signale sur la sortie d’erreur, comme il signale les quelques runtimes qui n’ont pas encore d’équivalent dans le provider. Rien n’est écarté en silence.
Les noms Terraform sont dérivés de ceux de vos ressources : passés en minuscules, débarrassés des caractères spéciaux, préfixés d’un _ s’ils commencent par un chiffre. Deux ressources de même type et de même nom, deux applications « demo » par exemple, ne peuvent pas cohabiter dans une configuration : la seconde reçoit un suffixe tiré de son identifiant (demo-11ced66e), et la sortie d’erreur le signale (« Name collision on nodejs.demo: suffixing with 11ced66e »). Ces noms ne sont que des étiquettes dans votre code : renommez-les à votre convenance dans imports.tf avant l’étape 3, Terraform générera la configuration sous le nouveau nom.
Pour une adoption progressive, vous pouvez limiter la génération à une seule application, ou aux ressources portant un tag :
clever terraform generate --app mon-api > imports.tf
clever terraform generate --tag env:staging > imports.tf
Étape 3 : générer la configuration HCL avec terraform plan
Depuis sa version 1.5, Terraform sait rédiger lui-même la configuration des ressources déclarées dans des blocs import :
terraform plan -generate-config-out=generated.tf
Terraform lit chaque ressource sur la plateforme, en écrit le bloc HCL dans generated.tf (le fichier ne doit pas exister au préalable) et affiche le plan correspondant, sans rien écrire dans le state. Le fichier contient les vraies valeurs : nom, région, nombre d’instances, flavors, plan de l’add-on, dépendances.
Ce point mérite une précision. Sur une application, min_instance_count, max_instance_count, smallest_flavor et biggest_flavor sont obligatoires, comme plan sur un add-on, et le provider ne les déduira jamais tout seul. Ces attributs déterminent ce que la ressource coûte : nous refusons qu’une configuration reste muette à leur sujet et adopte, sans le dire, ce que la plateforme fait tourner. C’est aussi la raison de préférer les blocs import à la commande terraform import, qui écrit directement dans le state et vous laisse rédiger la configuration complète, sous peine d’un Missing required argument au plan suivant.
Secrets et variables d’environnement : le piège de l’import Terraform
C’est l’étape où il faut ralentir. Terraform ne recopie jamais dans une configuration générée une valeur que le provider déclare sensible, et les variables d’environnement d’une application en font partie. Le fichier généré contient donc, pour chaque application :
resource "clevercloud_nodejs" "mon-api" {
name = "mon-api"
region = "par"
min_instance_count = 1
max_instance_count = 2
smallest_flavor = "XS"
biggest_flavor = "M"
environment = null # sensitive
}
Appliquer ce fichier tel quel supprimerait toutes les variables d’environnement de l’application. Le provider offre une variable d’environnement pour contourner ce comportement le temps de la génération :
BYPASS_SENSITIVE_IMPORTS=true terraform plan -generate-config-out=generated.tf
Les attributs sensibles sont alors écrits comme les autres, et environment contient toutes vos variables, en clair. Et pas seulement dans le fichier : tant que la variable est définie, la sortie de terraform plan affiche elle aussi ces valeurs en clair. Lancez cette étape depuis votre poste, jamais dans un pipeline de CI dont les logs sont conservés. C’est exactement ce que nous voulions, et c’est aussi ce qui demande de la vigilance : clés d’API, mots de passe de bases de données externes, tokens de services tiers, tout ce que vous aviez confié à clever env set se retrouve dans un fichier .tf que vous vous apprêtez à versionner.
Avant le premier commit, passez ces variables en revue. Les valeurs de configuration ordinaires (NODE_ENV, un port, une URL publique) peuvent rester dans le code. Les secrets doivent en sortir, vers l’outil que votre équipe utilise déjà : HashiCorp Vault ou OpenBao, les variables protégées de votre CI, un fichier chiffré avec SOPS, ou au minimum des variables Terraform marquées sensitive et fournies au moment de l’exécution :
variable "stripe_api_key" {
type = string
sensitive = true
}
resource "clevercloud_nodejs" "mon-api" {
# ...
environment = {
NODE_ENV = "production"
STRIPE_API_KEY = var.stripe_api_key
}
}
Ce remplacement devient visible au plan suivant : Terraform annonce une mise à jour d’environnement, alors que la valeur n’a pas changé. Il enregistre seulement dans le state que cette valeur est désormais sensible. Le provider, lui, compare les valeurs et non leur statut : identiques, elles ne déclenchent aucun redémarrage de l’application.
Retirez BYPASS_SENSITIVE_IMPORTS dès que generated.tf est écrit : la configuration reste valide sans elle, et le provider affiche l’avertissement « Sensitive attributes are not redacted » à chaque commande tant que la variable est définie. Quant au state Terraform, il contient toujours ces valeurs, quel que soit votre choix : son stockage mérite la même attention que les secrets eux-mêmes.
Étape 4 : vérifier le plan, puis appliquer
Une fois generated.tf relu et nettoyé, un terraform plan sans la variable de contournement doit annoncer autant d’imports que de ressources, et aucune autre modification que les environment que vous venez de déclarer secret :
terraform plan
# Plan: 2 to import, 0 to add, 1 to change, 0 to destroy.
# ~ environment = (sensitive value) valeur inchangée, statut sensible enregistré
terraform apply
Tout autre changement signale qu’un attribut de la configuration diverge de ce que la plateforme fait tourner : lisez-le avant de confirmer.
L’apply inscrit alors les ressources dans le state et, pour les secrets passés en variables, y note leur statut sensible ; il ne touche à rien sur la plateforme. Un dernier terraform plan doit répondre « No changes » : votre infrastructure est désormais décrite dans du code, et les blocs import peuvent être supprimés.
Le déploiement du code reste votre décision
Un import ne remplit jamais le bloc deployment d’une application. Le provider ne peut pas deviner si vous souhaitez que Terraform gère aussi vos déploiements, et l’ajout de ce bloc déclenche un déploiement au premier apply.
Trois options s’offrent à vous. Ne rien ajouter : Terraform gère la configuration, vous continuez à déployer avec git push ou clever deploy, et un changement de variable redémarre l’application comme aujourd’hui. Déclarer commit = "github_hook" si l’application est liée à un dépôt GitHub : les déploiements restent entre les mains de GitHub, le provider ne pousse jamais de code. Ou confier le déploiement à Terraform avec repository et, si vous le souhaitez, un commit précis. Dans ce dernier cas, un token d’API (clever tokens create) ne suffit pas, car il ne peut pas pousser sur Git : le provider a besoin des identifiants OAuth du profil Clever Tools.
Après l’import
generated.tf est un point de départ, pas une configuration finie. Quelques gestes le rendent durable : découper le fichier par service ou par environnement, remplacer les identifiants en dur du champ dependencies par des références (clevercloud_postgresql.ma-base.id), extraire en variables ce qui diffère entre staging et production. Et puisque la commande est en bêta, vos retours sont les bienvenus sur le dépôt GitHub de Clever Tools : c’est ainsi qu’elle sortira du mode expérimental.
Reste la question du state, que vous confiez aujourd’hui à un bucket S3 ou à un backend tiers. Nous n’en dirons pas plus pour l’instant, sinon qu’il pourrait bientôt ne plus avoir à quitter Clever Cloud.
FAQ
L’import modifie-t-il mes applications ?
Non. Les blocs import n’écrivent que dans le state. La seule mise à jour attendue porte sur environment lorsque vous avez remplacé un secret par une variable sensitive : la valeur est identique et le provider ne redémarre pas l’application. Le seul risque réel est d’appliquer une configuration où environment vaut null : c’est tout l’objet de la section sur les secrets.
Puis-je n’importer qu’une partie de mon organisation ?
Oui, avec --app pour une application ou --tag pour les ressources portant un tag. Rien n’oblige à tout migrer en une fois.
Pourquoi certaines ressources manquent-elles dans imports.tf ?
Soit elles sont gérées par un add-on (l’application d’un Keycloak, le Kibana d’un Elasticsearch) et Terraform ne doit pas les prendre en charge, soit leur runtime n’a pas encore de ressource dans le provider. Dans les deux cas, la commande l’indique sur la sortie d’erreur.
Cela fonctionne-t-il avec OpenTofu, le fork de Terraform ?
Oui. OpenTofu implémente les blocs import et l’option -generate-config-out depuis sa version 1.6, et notre provider est disponible sur son registry.
Faut-il conserver les blocs import ?
Non. Une fois les ressources dans le state, ils ne servent plus et peuvent être supprimés.
Clever Tools 5.3 est open source sur GitHub, disponible sur npm et dans nos dépôts de paquets. Le provider Clever Cloud est publié sur le Terraform Registry, son code est sur GitHub, et la documentation complète est disponible sur www.clever.cloud/developers.