Si Terraform est le moteur de votre Infrastructure as Code, alors HCL (HashiCorp Configuration Language) en est le carburant. Ce langage déclaratif, conçu spécifiquement par HashiCorp, est à la fois suffisamment simple pour être accessible aux débutants et suffisamment puissant pour exprimer des configurations d'infrastructure complexes. Dans cet article, nous allons plonger en profondeur dans la syntaxe, les types de données, les blocs, les fonctions et les expressions avancées de HCL. À la fin de cette lecture, vous maîtriserez les fondamentaux du langage et serez capable d'écrire des configurations Terraform expressives, maintenables et élégantes.
Structure du langage HCL en un coup d'oeil

Qu'est-ce que HCL ?
HCL (HashiCorp Configuration Language) est un langage de configuration créé par HashiCorp pour ses outils (Terraform, Vault, Consul, Nomad, Packer, etc.). Il a été conçu avec trois objectifs principaux :
- Lisibilité humaine : HCL est plus lisible que JSON et plus structuré que YAML.
- Interopérabilité machine : HCL peut être converti en JSON et vice-versa, facilitant l'intégration avec des outils existants.
- Expressivité : HCL supporte les variables, les fonctions, les expressions conditionnelles et les boucles, tout en restant déclaratif.
Les fichiers HCL utilisent l'extension .tf pour les configurations Terraform et .tfvars pour les fichiers de variables.
La syntaxe de base : blocs, arguments et expressions
La syntaxe HCL repose sur trois éléments fondamentaux : les blocs, les arguments et les expressions.
Les blocs
Un bloc est un conteneur qui regroupe une configuration. Il est défini par un type, zéro ou plusieurs labels, et un corps entre accolades :
# Syntaxe générale d'un bloc
type_de_bloc "label_1" "label_2" {
# corps du bloc
argument1 = valeur1
argument2 = valeur2
bloc_imbriqué {
argument3 = valeur3
}
}Voici un exemple concret avec une ressource AWS :
# "resource" est le type de bloc
# "aws_instance" est le premier label (type de ressource)
# "web_server" est le second label (nom logique)
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
}
}Les arguments
Un argument assigne une valeur à un nom à l'intérieur d'un bloc. La syntaxe est simple : nom = valeur. Les arguments sont spécifiques à chaque type de bloc et de ressource.
resource "aws_s3_bucket" "mon_bucket" {
# Arguments de la ressource
bucket = "mon-bucket-unique-12345"
tags = {
Environment = "production"
Team = "devops"
}
}Les expressions
Les expressions représentent des valeurs, soit littérales, soit calculées. HCL supporte de nombreux types d'expressions :
# Expression littérale (string)
name = "mon-serveur"
# Expression littérale (nombre)
count = 3
# Expression littérale (booléen)
enabled = true
# Référence à une autre ressource
subnet_id = aws_subnet.main.id
# Interpolation de chaîne
name = "serveur-${var.environment}"
# Expression conditionnelle
instance_type = var.environment == "production" ? "t3.large" : "t3.micro"
# Appel de fonction
upper_name = upper(var.name)Les types de données en HCL
HCL supporte un ensemble riche de types de données, divisés en types primitifs et types complexes.
Types primitifs
Les trois types primitifs de HCL sont :
# String (chaîne de caractères)
nom = "mon-serveur"
# Number (nombre entier ou décimal)
port = 8080
cpu_load = 0.75
# Bool (booléen)
actif = true
debug = falseString multilignes (Heredoc)
Pour les chaînes de caractères sur plusieurs lignes, HCL utilise la syntaxe Heredoc :
# Heredoc standard (conserve l'indentation)
description = <<EOT
Ceci est une description
sur plusieurs lignes.
Elle conserve l'indentation exacte.
EOT
# Heredoc avec suppression de l'indentation (recommandé)
user_data = <<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y nginx
systemctl start nginx
EOFLa syntaxe <<- (avec le tiret) supprime automatiquement l'indentation commune à toutes les lignes, ce qui permet de garder un code bien indenté.
Types complexes : list (tuple)
Une list est une séquence ordonnée de valeurs du même type :
# Liste de strings
availability_zones = ["eu-west-3a", "eu-west-3b", "eu-west-3c"]
# Liste de nombres
ports = [80, 443, 8080]
# Accéder à un élément par son index
first_az = var.availability_zones[0] # "eu-west-3a"
# Longueur d'une liste
nb_zones = length(var.availability_zones) # 3Types complexes : map (object)
Un map est une collection de paires clé-valeur :
# Map de strings
tags = {
Name = "WebServer"
Environment = "production"
Team = "devops"
Project = "ecommerce"
}
# Accéder à une valeur par sa clé
env = var.tags["Environment"] # "production"
# Ou avec la notation point
env = var.tags.Environment # "production"
# Map de nombres (tarifs par région)
instance_prices = {
"eu-west-1" = 0.0116
"eu-west-3" = 0.0118
"us-east-1" = 0.0104
}Types complexes : object
Un object est un type structuré avec des attributs nommés et typés :
variable "serveur" {
type = object({
nom = string
instance_type = string
port = number
actif = bool
tags = map(string)
})
default = {
nom = "web-01"
instance_type = "t3.micro"
port = 443
actif = true
tags = {
Environment = "dev"
}
}
}Types complexes : set et tuple
# Set : comme une liste mais sans doublons et sans ordre garanti
variable "allowed_ips" {
type = set(string)
default = ["10.0.0.1", "10.0.0.2", "10.0.0.3"]
}
# Tuple : liste avec des types hétérogènes
variable "config" {
type = tuple([string, number, bool])
default = ["serveur", 8080, true]
}Les blocs fondamentaux de Terraform
Terraform utilise plusieurs types de blocs HCL, chacun ayant un rôle spécifique dans la configuration de l'infrastructure.
Le bloc terraform
Le bloc terraform configure le comportement global de Terraform lui-même :
terraform {
# Version minimale de Terraform requise
required_version = ">= 1.5.0"
# Providers requis avec contraintes de version
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.0"
}
}
# Configuration du backend pour le state
backend "s3" {
bucket = "mon-terraform-state"
key = "prod/terraform.tfstate"
region = "eu-west-3"
dynamodb_table = "terraform-locks"
encrypt = true
}
}Le bloc provider
Le bloc provider configure un fournisseur de services :
# Provider AWS avec configuration
provider "aws" {
region = "eu-west-3"
profile = "production"
default_tags {
tags = {
ManagedBy = "Terraform"
Project = "mon-projet"
}
}
}
# Vous pouvez avoir plusieurs instances du même provider
# en utilisant des alias
provider "aws" {
alias = "us_east"
region = "us-east-1"
}
# Utiliser un provider avec alias
resource "aws_s3_bucket" "cdn_bucket" {
provider = aws.us_east
bucket = "mon-bucket-cdn"
}Le bloc resource
Le bloc resource définit un composant d'infrastructure à créer et gérer :
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "vpc-principale"
}
}
resource "aws_subnet" "publique" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "eu-west-3a"
map_public_ip_on_launch = true
tags = {
Name = "subnet-publique"
}
}
# Référencer des attributs d'autres ressources
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
subnet_id = aws_subnet.publique.id # Référence !
tags = {
Name = "web-server"
}
}Le bloc data
Le bloc data permet de lire des informations depuis un provider sans créer de ressource. C'est utile pour référencer des ressources existantes qui ne sont pas gérées par votre configuration Terraform :
# Récupérer l'AMI Ubuntu la plus récente
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Utiliser la data source dans une ressource
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id # Référence au data source
instance_type = "t3.micro"
}
# Récupérer les AZs disponibles
data "aws_availability_zones" "available" {
state = "available"
}
# Récupérer le VPC par défaut
data "aws_vpc" "default" {
default = true
}Le bloc variable
Le bloc variable déclare une variable d'entrée qui paramètre votre configuration :
variable "environment" {
description = "L'environnement de déploiement (dev, staging, prod)"
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "L'environnement doit être dev, staging ou prod."
}
}
variable "instance_count" {
description = "Nombre d'instances à créer"
type = number
default = 1
}
variable "enable_monitoring" {
description = "Activer le monitoring détaillé"
type = bool
default = false
}
variable "allowed_cidrs" {
description = "Liste des CIDR autorisés"
type = list(string)
default = ["10.0.0.0/8"]
}
variable "instance_config" {
description = "Configuration des instances par environnement"
type = map(object({
instance_type = string
volume_size = number
monitoring = bool
}))
default = {
dev = {
instance_type = "t3.micro"
volume_size = 20
monitoring = false
}
prod = {
instance_type = "t3.large"
volume_size = 100
monitoring = true
}
}
}
# Variable sensible (masquée dans les logs)
variable "db_password" {
description = "Mot de passe de la base de données"
type = string
sensitive = true
}Les variables peuvent être fournies de plusieurs manières (par ordre de priorité croissante) : valeur par défaut, fichier terraform.tfvars, fichiers *.auto.tfvars, variables d'environnement (TF_VAR_nom), option -var en ligne de commande.
Le bloc output
Le bloc output exporte des valeurs après l'application de la configuration. Ces valeurs peuvent être affichées à l'écran, utilisées par d'autres configurations Terraform, ou consommées par des scripts :
output "instance_ip" {
description = "Adresse IP publique de l'instance"
value = aws_instance.web.public_ip
}
output "instance_url" {
description = "URL du serveur web"
value = "http://${aws_instance.web.public_ip}"
}
output "database_endpoint" {
description = "Endpoint de la base de données"
value = aws_db_instance.main.endpoint
sensitive = true # Masquer la valeur dans les logs
}
# Output conditionnel
output "lb_dns_name" {
description = "DNS du load balancer (si activé)"
value = var.enable_lb ? aws_lb.main[0].dns_name : "N/A"
}Le bloc locals
Le bloc locals définit des valeurs locales calculées qui peuvent être réutilisées dans la configuration. C'est l'équivalent des constantes ou des variables intermédiaires :
locals {
# Valeur simple
project_name = "ecommerce"
# Valeur calculée
name_prefix = "${var.environment}-${local.project_name}"
# Tags communs
common_tags = {
Project = local.project_name
Environment = var.environment
ManagedBy = "Terraform"
CreatedAt = timestamp()
}
# Calcul conditionnel
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
# Transformation de données
subnet_cidrs = [for i in range(3) : cidrsubnet("10.0.0.0/16", 8, i)]
# Résultat : ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"]
}
# Utilisation des locals
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = local.instance_type
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-web"
Role = "webserver"
})
}Les fonctions built-in de HCL
Terraform fournit un ensemble riche de fonctions built-in pour manipuler les données. Voici les plus utilisées, classées par catégorie.
Fonctions sur les chaînes de caractères
locals {
# upper / lower : changer la casse
majuscule = upper("terraform") # "TERRAFORM"
minuscule = lower("TERRAFORM") # "terraform"
# title : première lettre en majuscule
titre = title("hello world") # "Hello World"
# format : formatage de chaîne (comme printf)
message = format("Serveur %s sur le port %d", "web", 8080)
# "Serveur web sur le port 8080"
# join : concaténer une liste en string
zones = join(", ", ["eu-west-3a", "eu-west-3b", "eu-west-3c"])
# "eu-west-3a, eu-west-3b, eu-west-3c"
# split : découper une string en liste
parties = split(",", "a,b,c") # ["a", "b", "c"]
# replace : remplacement dans une chaîne
clean = replace("hello-world", "-", "_") # "hello_world"
# trimspace : supprimer les espaces en début et fin
propre = trimspace(" hello ") # "hello"
# substr : extraction de sous-chaîne
extrait = substr("terraform", 0, 5) # "terra"
# regex / regexall : expressions régulières
match = regex("[0-9]+", "serveur-42") # "42"
}Fonctions sur les collections
locals {
# length : nombre d'éléments
nb_items = length(["a", "b", "c"]) # 3
# lookup : chercher une valeur dans un map avec valeur par défaut
region_ami = lookup({
"eu-west-3" = "ami-123"
"us-east-1" = "ami-456"
}, "eu-west-3", "ami-default") # "ami-123"
# element : accéder à un élément avec index cyclique
az = element(["a", "b", "c"], 4) # "b" (index 4 % 3 = 1)
# contains : vérifier la présence d'un élément
has_prod = contains(["dev", "staging", "prod"], "prod") # true
# concat : fusionner des listes
all_cidrs = concat(
["10.0.0.0/16"],
["172.16.0.0/12"],
["192.168.0.0/16"]
)
# merge : fusionner des maps
all_tags = merge(
{ Environment = "prod" },
{ Team = "devops" },
{ ManagedBy = "Terraform" }
)
# { Environment = "prod", Team = "devops", ManagedBy = "Terraform" }
# flatten : aplatir des listes imbriquées
flat = flatten([["a", "b"], ["c"], ["d", "e"]])
# ["a", "b", "c", "d", "e"]
# keys / values : extraire les clés ou valeurs d'un map
tag_keys = keys({ Name = "web", Env = "prod" }) # ["Env", "Name"]
tag_values = values({ Name = "web", Env = "prod" }) # ["prod", "web"]
# distinct : supprimer les doublons
unique = distinct(["a", "b", "a", "c", "b"]) # ["a", "b", "c"]
# sort : trier une liste
sorted = sort(["banana", "apple", "cherry"]) # ["apple", "banana", "cherry"]
# zipmap : créer un map à partir de deux listes
server_map = zipmap(
["web", "api", "db"],
["10.0.1.1", "10.0.1.2", "10.0.1.3"]
)
# { web = "10.0.1.1", api = "10.0.1.2", db = "10.0.1.3" }
}Fonctions numériques
locals {
# min / max
minimum = min(5, 12, 3, 9) # 3
maximum = max(5, 12, 3, 9) # 12
# abs : valeur absolue
absolu = abs(-42) # 42
# ceil / floor : arrondir
plafond = ceil(4.3) # 5
plancher = floor(4.7) # 4
# signum : signe d'un nombre
signe = signum(-15) # -1
# parseint : convertir une chaîne en entier
hex_value = parseint("FF", 16) # 255
}Fonctions de fichiers
locals {
# file : lire le contenu d'un fichier
ssh_key = file("~/.ssh/id_rsa.pub")
# fileexists : vérifier si un fichier existe
has_config = fileexists("${path.module}/config.json")
# templatefile : lire un fichier template avec variables
user_data = templatefile("${path.module}/templates/user_data.sh", {
hostname = "web-01"
environment = var.environment
packages = ["nginx", "htop", "curl"]
})
# filebase64 : lire un fichier en base64
logo_base64 = filebase64("${path.module}/files/logo.png")
# jsondecode / jsonencode
config = jsondecode(file("${path.module}/config.json"))
json_output = jsonencode({
name = "test"
port = 8080
})
# yamldecode / yamlencode
k8s_config = yamldecode(file("${path.module}/config.yaml"))
}Fonctions réseau
locals {
# cidrsubnet : calculer un sous-réseau
subnet_1 = cidrsubnet("10.0.0.0/16", 8, 0) # "10.0.0.0/24"
subnet_2 = cidrsubnet("10.0.0.0/16", 8, 1) # "10.0.1.0/24"
subnet_3 = cidrsubnet("10.0.0.0/16", 8, 255) # "10.0.255.0/24"
# cidrhost : calculer une adresse IP dans un sous-réseau
gateway = cidrhost("10.0.1.0/24", 1) # "10.0.1.1"
# cidrnetmask : obtenir le masque de sous-réseau
masque = cidrnetmask("10.0.0.0/16") # "255.255.0.0"
}Expressions conditionnelles
HCL supporte les expressions conditionnelles ternaires, similaires à celles de nombreux langages de programmation :
# Syntaxe : condition ? valeur_si_vrai : valeur_si_faux
# Choix du type d'instance selon l'environnement
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
# Activer le monitoring uniquement en production
monitoring = var.environment == "prod" ? true : false
# Nombre de sous-réseaux selon l'environnement
# (les conditionnelles marchent aussi avec count)
count = var.create_instance ? 1 : 0
}
# Conditionnel dans les locals
locals {
# Choix de la taille du volume
volume_size = var.environment == "prod" ? 100 : var.environment == "staging" ? 50 : 20
# Valeur par défaut si variable vide
bucket_name = var.custom_bucket_name != "" ? var.custom_bucket_name : "default-bucket-${var.environment}"
# Fusionner des tags conditionnellement
tags = merge(
local.common_tags,
var.environment == "prod" ? { CriticalLevel = "high" } : {}
)
}Les boucles en HCL
HCL propose plusieurs mécanismes pour créer des ressources multiples ou transformer des données : count, for_each et les expressions for.
count : créer N ressources identiques
count est le mécanisme le plus simple pour créer plusieurs instances d'une même ressource :
# Créer 3 instances EC2
resource "aws_instance" "web" {
count = 3
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "web-${count.index}" # web-0, web-1, web-2
}
}
# Accéder aux instances créées
output "instance_ips" {
value = aws_instance.web[*].public_ip # Splat expression
}
# count conditionnel : créer ou non une ressource
resource "aws_eip" "web" {
count = var.assign_elastic_ip ? 1 : 0
instance = aws_instance.web[0].id
}
# count depuis une variable
variable "server_names" {
default = ["web", "api", "worker"]
}
resource "aws_instance" "servers" {
count = length(var.server_names)
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = var.server_names[count.index]
}
}for_each : créer des ressources à partir d'un map ou set
for_each est plus flexible que count car chaque ressource est identifiée par une clé unique plutôt que par un index numérique :
# for_each avec un map
variable "instances" {
default = {
web = {
instance_type = "t3.micro"
ami = "ami-123456"
}
api = {
instance_type = "t3.small"
ami = "ami-789012"
}
worker = {
instance_type = "t3.medium"
ami = "ami-345678"
}
}
}
resource "aws_instance" "servers" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.key # "web", "api", "worker"
}
}
# Accéder à une instance spécifique
output "web_ip" {
value = aws_instance.servers["web"].public_ip
}
# for_each avec un set de strings
resource "aws_iam_user" "users" {
for_each = toset(["alice", "bob", "charlie"])
name = each.value
}
# for_each avec un map pour les Security Group rules
variable "ingress_rules" {
default = {
http = {
port = 80
description = "HTTP"
}
https = {
port = 443
description = "HTTPS"
}
ssh = {
port = 22
description = "SSH"
}
}
}
resource "aws_security_group_rule" "ingress" {
for_each = var.ingress_rules
type = "ingress"
from_port = each.value.port
to_port = each.value.port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.main.id
description = each.value.description
}Expression for : transformer des collections
L'expression for permet de transformer des listes et des maps, similaire aux list comprehensions en Python :
locals {
# Transformer une liste : mettre en majuscules
names = ["alice", "bob", "charlie"]
upper_names = [for name in local.names : upper(name)]
# ["ALICE", "BOB", "CHARLIE"]
# Filtrer une liste
long_names = [for name in local.names : name if length(name) > 3]
# ["alice", "charlie"]
# Transformer une liste en map
name_map = { for name in local.names : name => upper(name) }
# { alice = "ALICE", bob = "BOB", charlie = "CHARLIE" }
# Itérer sur un map
servers = {
web = "10.0.1.1"
api = "10.0.1.2"
worker = "10.0.1.3"
}
server_urls = { for name, ip in local.servers : name => "http://${ip}:8080" }
# { web = "http://10.0.1.1:8080", api = "http://10.0.1.2:8080", ... }
# Transformer avec index
indexed_names = [for i, name in local.names : "${i}: ${name}"]
# ["0: alice", "1: bob", "2: charlie"]
# Grouper par valeur (avec le symbole ...)
users = {
alice = "admin"
bob = "dev"
charlie = "admin"
david = "dev"
}
users_by_role = {
for name, role in local.users : role => name...
}
# { admin = ["alice", "charlie"], dev = ["bob", "david"] }
}Les commentaires en HCL
HCL supporte trois styles de commentaires :
# Commentaire sur une ligne (style shell)
// Commentaire sur une ligne (style C) - également valide
/*
Commentaire sur
plusieurs lignes
(style C)
*/
resource "aws_instance" "web" {
ami = "ami-123456" # Commentaire en fin de ligne
instance_type = "t3.micro" // Aussi valide en fin de ligne
/*
Cette section est temporairement désactivée
monitoring = true
ebs_optimized = true
*/
}Le style # est le plus utilisé dans la communauté Terraform et est considéré comme le standard idiomatique.
Les dynamic blocks
Les dynamic blocks permettent de générer dynamiquement des blocs imbriqués à l'intérieur d'une ressource. Ils sont utiles quand le nombre de blocs imbriqués dépend d'une variable :
variable "ingress_rules" {
default = [
{
port = 80
description = "HTTP"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 443
description = "HTTPS"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 22
description = "SSH"
cidr_blocks = ["10.0.0.0/8"]
}
]
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group pour le serveur web"
# Bloc dynamic qui génère un bloc "ingress" pour chaque règle
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.port
to_port = ingress.value.port
protocol = "tcp"
cidr_blocks = ingress.value.cidr_blocks
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}Les expressions de type avancées
Splat expressions
Les splat expressions ([*]) permettent d'extraire un attribut de tous les éléments d'une liste de ressources :
resource "aws_instance" "web" {
count = 3
ami = "ami-123456"
instance_type = "t3.micro"
}
# Splat expression : récupérer toutes les IPs
output "all_ips" {
value = aws_instance.web[*].public_ip
# ["52.1.2.3", "52.4.5.6", "52.7.8.9"]
}
# Équivalent avec for
output "all_ips_for" {
value = [for instance in aws_instance.web : instance.public_ip]
}Opérateurs
locals {
# Arithmétique
somme = 5 + 3 # 8
produit = 5 * 3 # 15
division = 10 / 3 # 3.333...
modulo = 10 % 3 # 1
# Comparaison
egal = 5 == 5 # true
different = 5 != 3 # true
superieur = 5 > 3 # true
inferieur = 3 < 5 # true
# Logique
et = true && false # false
ou = true || false # true
non = !true # false
# Combinaison
is_prod_large = var.environment == "prod" && var.instance_count > 10
}Bonnes pratiques pour écrire du HCL propre
Pour conclure, voici les bonnes pratiques essentielles pour écrire du HCL maintenable et professionnel :
Formatage automatique
Utilisez toujours terraform fmt pour formater votre code de manière cohérente. Intégrez-le dans vos hooks Git ou votre CI/CD :
# Formater tous les fichiers du répertoire courant
terraform fmt
# Formater récursivement
terraform fmt -recursive
# Vérifier le formatage sans modifier (utile en CI)
terraform fmt -checkValidation
Validez toujours la syntaxe de votre configuration avant d'exécuter un plan :
# Valider la syntaxe et la cohérence
terraform validateConventions de nommage
- Utilisez le snake_case pour les noms de ressources, variables et outputs :
web_server,instance_count. - Utilisez des noms descriptifs et significatifs : préférez
aws_instance.web_serveràaws_instance.this. - Ajoutez des descriptions à toutes les variables et outputs.
- Préfixez les noms de ressources cloud avec l'environnement ou le projet.
Organisation du code
- Séparez les variables (
variables.tf), les outputs (outputs.tf) et les locals (locals.tf) dans des fichiers dédiés. - Regroupez les ressources liées dans le même fichier (par exemple, toutes les ressources réseau dans
networking.tf). - Utilisez les modules pour encapsuler la logique réutilisable.
- Ne codez jamais les valeurs en dur : utilisez des variables pour tout ce qui peut changer entre les environnements.
Conclusion
Vous disposez maintenant d'une compréhension solide du langage HCL et de ses capacités. De la syntaxe de base (blocs, arguments, expressions) aux fonctionnalités avancées (boucles for_each, dynamic blocks, fonctions built-in), HCL offre un équilibre remarquable entre simplicité et expressivité.
La clé pour maîtriser HCL est la pratique. Commencez par des configurations simples, puis explorez progressivement les fonctionnalités avancées au fur et à mesure que vos besoins se complexifient. N'hésitez pas à consulter la documentation officielle de Terraform qui détaille chaque fonction et chaque expression disponible.
Dans les prochains articles de cette série, nous aborderons des sujets plus avancés comme les modules Terraform, la gestion du state et les bonnes pratiques en équipe. En attendant, ouvrez votre éditeur et commencez à expérimenter avec les exemples de cet article !
Cet article fait partie de la série Terraform sur CodeClan. Partagez vos configurations et vos questions en commentaire, et restez connectés pour les prochains épisodes de cette série !