Terraform^2 = Terragrunt
Mise à jour :
Si vous avez déjà utilisé Terraform, vous savez à quel point il peut être puissant… mais aussi difficile à maintenir dans des projets complexes. C’est là que Terragrunt entre en jeu !
Qu’est-ce que Terragrunt ?
Terragrunt est une surcouche pour Terraform ou OpenTofu qui permet de simplifier et structurer le code. Il aide à éviter la duplication, à gérer les modules plus efficacement et à standardiser les bonnes pratiques d’infrastructure-as-code.
Pourquoi utiliser Terragrunt avec Terraform/Tofu ?
Imaginez que vous gérez plusieurs environnements (dev
, staging
, prod
) avec
Terraform. Copier-coller du code dans chaque dossier devient vite ingérable.
Terragrunt permet d’organiser tout cela intelligemment en évitant les
répétitions inutiles.
Voici quelques avantages clés :
- Factorisation du code : Plus besoin de copier-coller vos fichiers
.tf
pour chaque environnement. - Gestion des dépendances : Il exécute automatiquement les modules dans le bon ordre.
- Backends centralisés : Configuration unique pour tous vos états Terraform
(
S3
,GCS
…). - Modularité : Vous pouvez réutiliser des modules de façon plus efficace.
Notions à connaître
Avant de plonger dans la pratique, il est essentiel de comprendre les concepts clés de Terragrunt.
Les fichiers terragrunt.hcl
: le cœur de Terragrunt**
Le fichier terragrunt.hcl
est l’élément central de Terragrunt. Il remplace
les fichiers .tf
pour structurer la configuration d’un module ou d’un
environnement.
Voici un exemple de fichier terragrunt.hcl
simple :
terraform { source = "git::git@github.com:mon-org/mon-module.git//vpc"}
inputs = { region = "eu-west-2" cidr_block = "10.0.0.0/16"}
Explication :
terraform.source
: Définit où chercher le module Terraform à utiliser. Ici, un module est récupéré depuis un dépôt Git.inputs
: Liste des variables à passer au module Terraform.
Gestion centralisée des backends
Un des aspects les plus pénibles de Terraform est la configuration des backends (où stocker l’état des ressources). Avec Terragrunt, on peut centraliser cette configuration pour l’appliquer à tous les modules automatiquement. Pour OUTSCALE, il faut ajouter pas mal de paramètres skip pour que cela fonctionne correctement.
Dans infra/root.hcl
:
remote_state { backend = "s3" config = { bucket = "terragrunt-example-tf-state-${local.region}" key = "${path_relative_to_include()}/tf.tfstate" region = "${local.region}" endpoint = "https://oos.${local.region}.outscale.com" skip_region_validation = true skip_credentials_validation = true skip_metadata_api_check = true skip_s3_checksum = true # le paramètre à ajouter skip_bucket_ssencryption = true skip_bucket_root_access = true # use only if the AWS account root user should not have access to the remote state bucket for some reason skip_bucket_versioning = true skip_bucket_public_access_blocking = true skip_bucket_enforced_tls = true skip_requesting_account_id = true } generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" }}
Tous les sous-modules héritent automatiquement de cette configuration.
Organisation du code et DRY (Don’t Repeat Yourself)
L’un des gros avantages de Terragrunt est d’éviter la duplication du code. Il encourage à utiliser une structure de projet propre et modulaire, basée sur des principes DRY.
Une structure type avec Terragrunt peut ressembler à ceci :
├── infrastructure ├── _envcommon │ ├── eip.hcl │ └── network.hcl ├── non-prod │ ├── account.hcl │ └── eu-west-2 │ ├── dev │ │ ├── eip-nat-a │ │ │ └── terragrunt.hcl │ │ ├── eip-nat-b │ │ │ └── terragrunt.hcl │ │ ├── env.hcl │ │ ├── net-a │ │ │ └── terragrunt.hcl │ │ └── net-b │ │ └── terragrunt.hcl │ └── region.hcl ├── prod ├── modules │ ├── eip │ │ ├── main.tf │ │ └── variables.tf │ └── network │ ├── main.tf │ └── variables.tf └── root.hcl
Comment ça marche ? :
- Un fichier
root.hcl
global (dansinfra/
) contient les règles communes. - Chaque environnement (
dev
,prod
, etc.) hérite de ces configurations et peut ajouter ses propres spécificités.
Gestion des variables et inputs
Avec Terraform, on utilise des fichiers terraform.tfvars
. Avec
Terragrunt, les variables sont définies directement dans terragrunt.hcl
via inputs
.
Exemple :
inputs = { instance_type = "tinav6.c1r1p3" tags = { key = "Name" value = "haproxy-1" }}
Plus besoin de fichiers .tfvars
, tout est géré proprement depuis
Terragrunt.
Héritage et composition des configurations
Terragrunt permet d’hériter des configurations pour éviter la duplication.
On utilise la directive include
pour récupérer des paramètres définis dans un
fichier parent.
Exemple dans dev/vpc/terragrunt.hcl
:
include "root" { path = find_in_parent_folders("root.hcl")}
inputs = { environment = "dev"}
Explication :
find_in_parent_folders()
va rechercher un fichierterragrunt.hcl
dans les dossiers parents et l’inclure automatiquement.- Seules les variables spécifiques à
dev
sont définies ici.
Cela permet une gestion modulaire et évolutive des environnements sans
copier-coller des fichiers .tf
!
Gestion des dépendances entre modules
Par défaut, Terraform ne gère pas nativement les dépendances entre
projet. Terragrunt, lui, propose la directive dependencies
pour s’assurer
que certains projets soient créés avant d’autres.
Exemple : si l’application a besoin du réseau (vpc
) avant d’être déployée :
dependencies { paths = ["../vpc"]}
Installation et configuration de Terragrunt
Maintenant que nous avons compris les concepts essentiels de Terragrunt, passons à la pratique ! Dans ce chapitre, nous allons voir comment installer et configurer Terragrunt sur votre machine.
Pré-requis
Avant d’installer Terragrunt, assurez-vous d’avoir Terraform ou OpenTofu installé sur votre machine. Pour vérifier :
terraform version
ou
tofu version
Installer Terragrunt sur Linux/macOS
Sur Linux ou macOS, le moyen le plus simple d’installer Terragrunt est via
brew
ou
asdf
:
brew install terragrunt# ouasdf plugin add terragruntasdf install terragrunt latestasdf global terragrunt latest
Une fois installé, vérifiez la version de Terragrunt :
terragrunt --versionterragrunt version v0.72.4
Installer Terragrunt sur Windows
Sur Windows, vous pouvez utiliser Scoop ou Chocolatey :
scoop install terragrunt
ou
choco install terragrunt
Autres méthodes d’installation
Si vous ne souhaitez pas utiliser un gestionnaire de paquets, vous pouvez télécharger l’exécutable depuis les Releases de Terragrunt ↗
Créer votre premier projet Terragrunt
Une fois Terragrunt installé, il est temps de se lancer dans un projet
d’infrastructure multi-environnements. Pour cet exemple, nous allons créer un
module VPC pour Outscale que nous
allons déployer dans un environnement dev
puis de prod.
Création de la structure du projet
Voici comment créer un projet avec Terragrunt en quelques commandes :
mkdir -p infrastructure/{_envcommon,non-prod/eu-west-2/dev,modules/eip,modules/network} \ infrastructure/non-prod/eu-west-2/dev/{eip-nat-a,eip-nat-b,net-a,net-b}
Pour le moment, on ne crée que la structure de l’environnement dev
, une fois
bien en place, on pourra ajouter les autres environnements.
On peut créer ensuite les fichiers de configuration Terragrunt et les modules Terraform :
touch infrastructure/{root.hcl,.envrc} \ infrastructure/_envcommon/{eip.hcl,network.hcl} \ infrastructure/non-prod/{account.hcl,eu-west-2/region.hcl} \ infrastructure/non-prod/eu-west-2/dev/{env.hcl} \ infrastructure/non-prod/eu-west-2/dev/eip-nat-a/terragrunt.hcl \ infrastructure/non-prod/eu-west-2/dev/eip-nat-b/terragrunt.hcl \ infrastructure/non-prod/eu-west-2/dev/net-a/terragrunt.hcl \ infrastructure/non-prod/eu-west-2/dev/net-b/terragrunt.hcl \ infrastructure/modules/eip/{main.tf,variables.tf} \ infrastructure/modules/network/{main.tf,variables.tf}
Création du fichier .envrc contenant les secrets
Dans le fichier .envrc
qui est à la racine du projet ajouter les lignes
suivantes :
export OUTSCALE_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxx"export OUTSCALE_SECRET_KEY_ID="xxxxxxxxxx"
Remplacer les xxxxxxxx
par les vraies valeurs des clés d’accès. Pour charger
ces variables d’environnement, il suffit de lancer la commande source .envrc
ou utiliser
direnv ↗.
Création du module Terraform eip
Pour notre premier module nous allons faire simple, un module pour créer une
adresse IP élastique. Voici le contenu des fichiers main.tf
et variables.tf
resource "outscale_public_ip" "publicip" { tags { key = "Name" value = var.name }}
et :
variable "environment" { type = string description = "Environment (dev, staging, prod)"}
variable "region" { type = string description = "Region"}
variable "subregion" { type = string description = "AZ"}
variable "name" { type = string description = "Name of the EIP"}
Vous pouvez voir que c’est un module très simple.
Configuration du projet Terragrunt
Maintenant que la structure est en place, il est temps de configurer
Terragrunt. Pour cela, on va commencer par le fichier root.hcl
qui va
contenir la configuration globale du projet.
# ---------------------------------------------------------------------------------------------------------------------# TERRAGRUNT CONFIGURATION# Terragrunt is a thin wrapper for Terraform/OpenTofu that provides extra tools for working with multiple modules,# remote state, and locking: https://github.com/gruntwork-io/terragrunt# ---------------------------------------------------------------------------------------------------------------------
locals { # Automatically load account-level variables account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl")) # Automatically load region-level variables region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl")) # Automatically load environment-level variables environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) # Extract the variables we need for easy access access_key_id = local.account_vars.locals.access_key_id secret_key_id = local.account_vars.locals.secret_key_id region = local.region_vars.locals.region}
# Generate an AWS provider blockgenerate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOFterraform { required_version = ">= 1.0" required_providers { outscale = { source = "outscale/outscale" version = "0.12.0" } }}
provider "outscale" { access_key_id = "${get_env("OUTSCALE_ACCESS_KEY_ID", "")}" secret_key_id = "${get_env("OUTSCALE_SECRET_KEY_ID", "")}" region = "${get_env("TERRAGRUNT_REGION", "eu-west-2")}" endpoints { api = "api.${get_env("TERRAGRUNT_REGION", "eu-west-2")}.outscale.com" }}EOF}
# Configure Terragrunt to automatically store tfstate files in an S3 bucketremote_state { backend = "s3" config = { bucket = "$terragrunt-example-tf-state-${local.region}" key = "${path_relative_to_include()}/tf.tfstate" region = "${local.region}" endpoint = "https://oos.${local.region}.outscale.com" skip_region_validation = true skip_credentials_validation = true skip_metadata_api_check = true skip_s3_checksum = true # le paramètre à ajouter skip_bucket_ssencryption = true skip_bucket_root_access = true # use only if the AWS account root user should not have access to the remote state bucket for some reason skip_bucket_versioning = true skip_bucket_public_access_blocking = true skip_bucket_enforced_tls = true skip_requesting_account_id = true } generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" }}
# ---------------------------------------------------------------------------------------------------------------------# GLOBAL PARAMETERS# These variables apply to all configurations in this subfolder. These are automatically merged into the child# `terragrunt.hcl` config via the include block.# ---------------------------------------------------------------------------------------------------------------------
# Configure root level variables that all resources can inherit. This is especially helpful with multi-account configs# where terraform_remote_state data sources are placed directly into the modules.inputs = merge( local.account_vars.locals, local.region_vars.locals, local.environment_vars.locals,)
Ce fichier Terragrunt est une configuration centrale qui définit les variables globales, le fournisseur cloud (Outscale), et la gestion du backend Terraform pour stocker l’état des infrastructures dans un bucket S3.
- Chargement Automatique des Variables d’Environnement :
locals { account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl")) region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl")) environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) access_key_id = local.account_vars.locals.access_key_id secret_key_id = local.account_vars.locals.secret_key_id region = local.region_vars.locals.region}
Explication : Ce bloc charge automatiquement les variables de configuration depuis trois fichiers :
account.hcl
: Contient les identifiants d’accès (Access Key & Secret Key).region.hcl
: Définit la région cloud utilisée (ex:eu-west-2
).env.hcl
: Définit des variables spécifiques à l’environnement (ex:dev
,prod
).
- Génération Dynamique du Fournisseur Cloud (Outscale)
generate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <<EOFterraform { required_version = ">= 1.0" required_providers { outscale = { source = "outscale/outscale" version = "0.12.0" } }}
provider "outscale" { access_key_id = "${get_env("OUTSCALE_ACCESS_KEY_ID", "")}" secret_key_id = "${get_env("OUTSCALE_SECRET_KEY_ID", "")}" region = "${get_env("OUTSCALE_REGION", "eu-west-2")}" endpoints { api = "api.${get_env("OUTSCALE_REGION", "eu-west-2")}.outscale.com" }}EOF}
Explication : Ce bloc génère automatiquement un fichier provider.tf
qui définit le fournisseur cloud Outscale. 🔹 Il utilise des variables
d’environnement (OUTSCALE_ACCESS_KEY_ID
, OUTSCALE_SECRET_KEY_ID
,
OUTSCALE_REGION
) pour ne pas exposer les credentials en clair.
- Configuration du Backend Terraform avec S3
remote_state { backend = "s3" config = { bucket = "terragrunt-example-tf-state-${local.region}" key = "${path_relative_to_include()}/tf.tfstate" region = "${local.region}" endpoint = "https://oos.${local.region}.outscale.com" skip_region_validation = true skip_credentials_validation = true skip_metadata_api_check = true skip_s3_checksum = true skip_bucket_ssencryption = true skip_bucket_root_access = true skip_bucket_versioning = true skip_bucket_public_access_blocking = true skip_bucket_enforced_tls = true skip_requesting_account_id = true } generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" }}
Explication :
- Configure le stockage distant de l’état Terraform (
tfstate
) dans un bucket S3. - Utilise un nom de bucket dynamique, basé sur la région
(
TG_BUCKET_PREFIX
). - Désactive plusieurs vérifications de sécurité, pour s’adapter à Outscale qui fonctionne différemment d’AWS.
- Génère automatiquement un fichier
backend.tf
pour éviter de le créer manuellement.
- Fusion des Variables Globales
inputs = merge( local.account_vars.locals, local.region_vars.locals, local.environment_vars.locals,)
Explication :
- Ce bloc fusionne toutes les variables (
account.hcl
,region.hcl
,env.hcl
) en un seul bloc d’entrées (inputs
). - Cela permet aux modules Terraform d’accéder facilement à toutes les variables, sans devoir les répéter dans chaque fichier.
Configuration de l’environnement dev
Pour l’environnement dev
, on va créer un fichier env.hcl
qui contient les
variables spécifiques à cet environnement :
# Set common variables for the environment. This is automatically pulled in the root terragrunt.hcl configuration to# feed forward to the child modules.locals { environment = "dev"}
Configuration de la région eu-west-2
Pour la région eu-west-2
, on va créer un fichier region.hcl
qui contient les
variables spécifiques à cette région :
# Set common variables for the region. This is automatically pulled in in the root terragrunt.hcl configuration to# configure the remote state bucket and pass forward to the child modules as inputs.locals { region = "eu-west-2"}
Configuration du compte
Pour le compte, on va créer un fichier account.hcl
qui contient les variables
d’accès à Outscale :
# Set account-wide variables. These are automatically pulled in to configure the remote state bucket in the root# terragrunt.hcl configuration.locals { access_key_id = "${get_env("OUTSCALE_ACCESS_KEY_ID", "")}" secret_key_id = "${get_env("OUTSCALE_SECRET_KEY_ID", "")}"}
Configuration du module EIP
Configuration commune
# ---------------------------------------------------------------------------------------------------------------------# COMMON TERRAGRUNT CONFIGURATION# This is the common component configuration for mysql. The common variables for each environment to# deploy mysql are defined here. This configuration will be merged into the environment configuration# via an include block.# ---------------------------------------------------------------------------------------------------------------------
locals { # Automatically load environment-level variables environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
# Extract out common variables for reuse env = local.environment_vars.locals.environment
# Expose the base source URL so different versions of the module can be deployed in different environments. This will # be used to construct the source URL in the child terragrunt configurations. base_source = "${path_relative_from_include()}/../modules/eip"}
# ---------------------------------------------------------------------------------------------------------------------# MODULE PARAMETERS# These are the variables we have to pass in to use the module. This defines the parameters that are common across all# environments.# ---------------------------------------------------------------------------------------------------------------------inputs = {
}
Configuration spécifique à une instance
Pour notre première instance du module eip, on va créer un fichier
terragrunt.hcl
dans le répertoir eip-nat-a
qui contient les variables qui
contient les variables spécifiques à cette instance :
# ---------------------------------------------------------------------------------------------------------------------# TERRAGRUNT CONFIGURATION# This is the configuration for Terragrunt, a thin wrapper for Terraform and OpenTofu that helps keep your code DRY and# maintainable: https://github.com/gruntwork-io/terragrunt# ---------------------------------------------------------------------------------------------------------------------
# Include the root `terragrunt.hcl` configuration. The root configuration contains settings that are common across all# components and environments, such as how to configure remote state.include "root" { path = find_in_parent_folders("root.hcl")}
# Include the envcommon configuration for the component. The envcommon configuration contains settings that are common# for the component across all environments.include "envcommon" { path = "${dirname(find_in_parent_folders("root.hcl"))}/_envcommon/eip.hcl" # We want to reference the variables from the included config in this configuration, so we expose it. expose = true}
# Configure the version of the module to use in this environment. This allows you to promote new versions one# environment at a time (e.g., qa -> stage -> prod).terraform { source = "${include.envcommon.locals.base_source}"}
inputs = { name = "nat-a"}
Explication :
include "root"
: Inclut la configuration globale du projet.include "envcommon"
: Inclut la configuration commune du module EIP.terraform.source
: Définit la source du module Terraform à utiliser.inputs
: Définit les variables spécifiques à ce module (ici, le nom de l’EIP).
Déploiement de l’infrastructure
Maintenant que la configuration est en place, il est temps de déployer notre
infrastructure avec Terragrunt. J’ai créé d’autres instances des modules
eip
et network
pour les autres AZs.
Il faut dans un premier temps initialiser le projet :
Pour cela, on va se placer dans le dossier infrastructure
et lancer la
commande :
terragrunt run-all init16:22:30.744 INFO The stack at . will be processed in the following order for command init:Group 1- Module ./non-prod/eu-west-2/dev/eip-nat-a- Module ./non-prod/eu-west-2/dev/eip-nat-b- Module ./non-prod/eu-west-2/dev/net-a- Module ./non-prod/eu-west-2/dev/net-b
Remote state S3 bucket terragrunt-example-tf-state-eu-west-2 does not exist or you don't have permissions to access it. Would you like Terragrunt to create it? (y/n) y16:22:34.137 STDOUT [non-prod/eu-west-2/dev/eip-nat-b] terraform: Initializing the backend...16:22:34.137 STDOUT [non-prod/eu-west-2/dev/net-b] terraform: Initializing the backend...16:22:34.137 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: Initializing the backend...16:22:34.140 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: Initializing the backend...
...
16:22:34.720 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: should now work.16:22:34.721 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: If you ever set or change modules or backend configuration for Terraform,16:22:34.721 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: rerun this command to reinitialize your working directory. If you forget, other16:22:34.721 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: commands will detect it and remind you to do so if necessary.
Ensuite, on peut lancer le déploiement :
terragrunt run-all plan16:37:30.987 INFO The stack at . will be processed in the following order for command plan:Group 1- Module ./non-prod/eu-west-2/dev/eip-nat-a- Module ./non-prod/eu-west-2/dev/eip-nat-b- Module ./non-prod/eu-west-2/dev/net-a- Module ./non-prod/eu-west-2/dev/net-b
16:37:32.026 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: Terraform used the selected providers to generate the following execution16:37:32.026 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: plan. Resource actions are indicated with the following symbols:16:37:32.026 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + create16:37:32.026 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: Terraform will perform the following actions:16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: # outscale_public_ip.publicip will be created16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + resource "outscale_public_ip" "publicip" {16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + id = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + link_public_ip_id = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + nic_account_id = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + nic_id = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + private_ip = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + public_ip = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + public_ip_id = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + request_id = (known after apply)16:37:32.027 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + vm_id = (known after apply)16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + tags {16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + key = "Name"16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: + value = "nat-a"16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: }16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: }16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: Plan: 1 to add, 0 to change, 0 to destroy.16:37:32.028 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform:
...
16:37:32.244 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: Note: You didn't use the -out option to save this plan, so Terraform can't16:37:32.244 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: guarantee to take exactly these actions if you run "terraform apply" now.
Enfin, on peut appliquer les changements :
terragrunt run-all apply16:38:52.689 INFO The stack at . will be processed in the following order for command apply:Group 1- Module ./non-prod/eu-west-2/dev/eip-nat-a- Module ./non-prod/eu-west-2/dev/eip-nat-b- Module ./non-prod/eu-west-2/dev/net-a- Module ./non-prod/eu-west-2/dev/net-b
Are you sure you want to run 'terragrunt apply' in each folder of the stack described above? (y/n) y16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: Terraform used the selected providers to generate the following execution16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: plan. Resource actions are indicated with the following symbols:16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + create16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: Terraform will perform the following actions:16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: # outscale_net.net will be created16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + resource "outscale_net" "net" {16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + dhcp_options_set_id = (known after apply)16:38:56.198 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + id = (known after apply)16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + ip_range = "10.0.0.0/16"16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + net_id = (known after apply)16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + request_id = (known after apply)16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + state = (known after apply)16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: + tenancy = (known after apply)16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: }16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: Plan: 1 to add, 0 to change, 0 to destroy.16:38:56.199 STDOUT [non-prod/eu-west-2/dev/net-a] terraform:16:38:56.396 STDOUT [non-prod/eu-west-2/dev/eip-nat-a] terraform: Terraform used the selected providers to generate the following execution
...
16:38:58.316 STDOUT [non-prod/eu-west-2/dev/net-b] terraform: Apply complete! Resources: 1 added, 0 changed, 0 destroyed.16:38:58.316 STDOUT [non-prod/eu-west-2/dev/net-b] terraform:
On peut vérifier que les ressources ont bien été créées :
terragrunt run-all show16:40:20.327 INFO The stack at . will be processed in the following order for command show:Group 1- Module ./non-prod/eu-west-2/dev/eip-nat-a- Module ./non-prod/eu-west-2/dev/eip-nat-b- Module ./non-prod/eu-west-2/dev/net-a- Module ./non-prod/eu-west-2/dev/net-b
16:40:21.638 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: # outscale_net.net:16:40:21.638 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: resource "outscale_net" "net" {16:40:21.638 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: dhcp_options_set_id = "dopt-2b29cb4a"16:40:21.639 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: id = "vpc-5a55e2bb"16:40:21.639 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: ip_range = "10.0.0.0/16"16:40:21.639 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: net_id = "vpc-5a55e2bb"16:40:21.639 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: state = "available"16:40:21.639 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: tenancy = "default"16:40:21.639 STDOUT [non-prod/eu-west-2/dev/net-a] terraform: }
...
Détruire une instance de module
Pour détruire une instance de module, on peut utiliser la commande terragrunt destroy
en se plaçant dans le répertoire du module à détruire. Par exemple,
pour détruire l’instance nat-a
du module eip-nat
, on peut faire :
cd non-prod/eu-west-2/dev/eip-nat-aterragrunt destroy16:42:01.545 STDOUT terraform: outscale_public_ip.publicip: Refreshing state... [id=eipalloc-daaaffab]16:42:02.011 STDOUT terraform: Terraform used the selected providers to generate the following execution16:42:02.011 STDOUT terraform: plan. Resource actions are indicated with the following symbols:16:42:02.011 STDOUT terraform: - destroy16:42:02.011 STDOUT terraform: Terraform will perform the following actions:16:42:02.011 STDOUT terraform: # outscale_public_ip.publicip will be destroyed16:42:02.011 STDOUT terraform: - resource "outscale_public_ip" "publicip" {16:42:02.012 STDOUT terraform: - id = "eipalloc-daaaffab" -> null16:42:02.012 STDOUT terraform: - public_ip = "171.33.84.205" -> null16:42:02.012 STDOUT terraform: - public_ip_id = "eipalloc-daaaffab" -> null16:42:02.012 STDOUT terraform: # (5 unchanged attributes hidden)16:42:02.012 STDOUT terraform: - tags {16:42:02.012 STDOUT terraform: - key = "Name" -> null16:42:02.012 STDOUT terraform: - value = "nat-a" -> null16:42:02.012 STDOUT terraform: }16:42:02.012 STDOUT terraform: }16:42:02.012 STDOUT terraform: Plan: 0 to add, 0 to change, 1 to destroy.16:42:02.012 STDOUT terraform:16:42:02.012 STDOUT terraform: Do you really want to destroy all resources?16:42:02.012 STDOUT terraform: Terraform will destroy all your managed infrastructure, as shown above.16:42:02.012 STDOUT terraform: There is no undo. Only 'yes' will be accepted to confirm.16:42:02.012 STDOUT terraform: Enter a value:yes16:42:05.447 STDOUT terraform: outscale_public_ip.publicip: Destroying... [id=eipalloc-daaaffab]16:42:05.807 STDOUT terraform: outscale_public_ip.publicip: Destruction complete after 1s16:42:06.089 STDOUT terraform:16:42:06.089 STDOUT terraform: Destroy complete! Resources: 1 destroyed.16:42:06.089 STDOUT terraform:
On voit ici tout le potentiel de terragrunt pour gérer des infrastructures complexes contenants de nombreux modules. On peut facilement détruire une instance de module sans impacter les autres.
Débogage et résolution des problèmes avec Terragrunt
Même si Terragrunt facilite la gestion de Terraform, on n’est pas à l’abri d’erreurs !
Terragrunt génèrent des logs utiles en cas d’erreur. Pour afficher les logs de terraform :
TF_LOG=DEBUG terragrunt plan
Parfois, Terragrunt ne charge pas le bon fichier de configuration. Pour voir
quel terragrunt.hcl
est utilisé :
terragrunt plan --terragrunt-log-level debug
Avant d’exécuter un plan
ou apply
, on peut demander à Terragrunt de
générer les fichiers Terraform et vérifier ce qu’il produit :
terragrunt run-all plan --terragrunt-debug
Conclusion
Après cette première plongée dans Terragrunt, je réalise à quel point cet outil peut simplifier la gestion des infrastructures avec Terraform. Pour l’instant, je suis encore loin de le maîtriser complètement, mais cette exploration m’a déjà permis de comprendre ses principaux avantages : organisation modulaire, gestion des dépendances et mutualisation des configurations.
Mon objectif est maintenant de l’utiliser sur mon lab personnel pour construire mes modules Terraform. En développant mes propres configurations avec Terragrunt, je pourrai plus facilement monter des labs complets et réutilisables.
Ce guide est donc une première version basée sur mes découvertes. Avec la pratique, j’affinerai mes connaissances et, qui sait, peut-être que dans quelques mois, je pourrai partager un retour d’expérience plus avancé !