Syntaxe HCL
Objectifs du chapitre
- Comprendre la syntaxe HCL
- Maîtriser les types de données
- Utiliser les expressions et fonctions
- Écrire du code HCL propre
1 - Introduction à HCL
Qu'est-ce que HCL ?
HCL (HashiCorp Configuration Language) est un langage déclaratif conçu pour être :
- Lisible par les humains
- Éditable facilement
- Compatible JSON
Structure de base
# Commentaire sur une ligne
/* Commentaire
sur plusieurs
lignes */
# Bloc avec arguments
type "label" "name" {
argument1 = "valeur"
argument2 = 123
# Bloc imbriqué
nested_block {
nested_arg = true
}
}
2 - Types de blocs
Resource
# Crée une ressource dans le cloud
resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
}
}
Variable
# Déclare une variable d'entrée
variable "instance_type" {
description = "Type d'instance EC2"
type = string
default = "t2.micro"
}
Output
# Exporte une valeur
output "instance_ip" {
description = "IP publique de l'instance"
value = aws_instance.web.public_ip
}
Provider
# Configure un provider
provider "aws" {
region = "eu-west-1"
}
Locals
# Définit des valeurs locales
locals {
environment = "production"
common_tags = {
Environment = local.environment
Project = "MyProject"
}
}
3 - Types de données
Types primitifs
# String
name = "hello"
message = "Hello, ${var.name}!"
# Number
count = 42
price = 19.99
# Bool
enabled = true
disabled = false
Types complexes
# List (ordered collection)
availability_zones = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
# Map (key-value pairs)
tags = {
Name = "WebServer"
Environment = "Production"
}
# Set (unique values, no order)
unique_ports = toset([80, 443, 8080])
# Object (structured type)
server_config = {
name = "web-01"
cpu = 4
memory = 8
}
# Tuple (fixed-length list with specific types)
mixed = ["hello", 42, true]
Déclaration de types
variable "string_var" {
type = string
}
variable "number_var" {
type = number
}
variable "bool_var" {
type = bool
}
variable "list_var" {
type = list(string)
}
variable "map_var" {
type = map(string)
}
variable "object_var" {
type = object({
name = string
age = number
enabled = bool
})
}
variable "any_var" {
type = any
}
4 - Références et expressions
Référencer des ressources
# Référence simple
resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type = "t2.micro"
subnet_id = aws_subnet.main.id # Référence
}
resource "aws_subnet" "main" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
Types de références
# Ressource
aws_instance.web.id
aws_instance.web.public_ip
# Variable
var.instance_type
var.environment
# Local
local.common_tags
local.environment
# Data source
data.aws_ami.ubuntu.id
# Module output
module.vpc.vpc_id
# Provider
provider.aws
Interpolation
# Interpolation de variables
name = "server-${var.environment}-${count.index}"
# Interpolation conditionnelle
instance_type = var.environment == "production" ? "t2.large" : "t2.micro"
# Interpolation avec fonctions
bucket_name = lower("MyBucket-${random_id.bucket.hex}")
5 - Opérateurs
Arithmétiques
# Addition, soustraction, multiplication, division
total = 10 + 5 # 15
diff = 10 - 5 # 5
prod = 10 * 5 # 50
quot = 10 / 5 # 2
mod = 10 % 3 # 1 (modulo)
neg = -10 # -10
Comparaison
# Égalité et inégalité
equal = var.env == "prod"
not_equal = var.env != "dev"
# Comparaison numérique
greater = 10 > 5
less = 5 < 10
gte = 10 >= 10
lte = 5 <= 10
Logiques
# AND, OR, NOT
both = var.enabled && var.ready
either = var.primary || var.secondary
negate = !var.disabled
6 - Expressions conditionnelles
Ternaire
# condition ? true_value : false_value
instance_type = var.environment == "production" ? "t2.large" : "t2.micro"
# Imbriqué
size = var.env == "prod" ? "large" : (var.env == "staging" ? "medium" : "small")
Avec count
resource "aws_instance" "web" {
count = var.create_instance ? 1 : 0
ami = "ami-12345678"
instance_type = "t2.micro"
}
Avec for_each
resource "aws_instance" "web" {
for_each = var.create_instances ? var.instances : {}
ami = each.value.ami
instance_type = each.value.type
}
7 - Boucles et itérations
for expression
# Transformer une liste
upper_names = [for name in var.names : upper(name)]
# ["alice", "bob"] -> ["ALICE", "BOB"]
# Filtrer une liste
adults = [for p in var.people : p.name if p.age >= 18]
# Créer une map depuis une liste
name_map = {for s in var.servers : s.id => s.name}
# [{id="1", name="web"}] -> {"1" = "web"}
# Transformer une map
upper_tags = {for k, v in var.tags : k => upper(v)}
count
resource "aws_instance" "web" {
count = 3
ami = "ami-12345678"
instance_type = "t2.micro"
tags = {
Name = "web-${count.index}" # web-0, web-1, web-2
}
}
# Accéder aux instances
output "instance_ids" {
value = aws_instance.web[*].id
}
for_each
# Avec un set
resource "aws_iam_user" "users" {
for_each = toset(["alice", "bob", "charlie"])
name = each.value
}
# Avec une map
resource "aws_instance" "servers" {
for_each = {
web = { type = "t2.micro", ami = "ami-web" }
api = { type = "t2.small", ami = "ami-api" }
db = { type = "t2.medium", ami = "ami-db" }
}
ami = each.value.ami
instance_type = each.value.type
tags = {
Name = each.key
}
}
8 - Fonctions intégrées
Fonctions de chaînes
# Manipulation de strings
lower("HELLO") # "hello"
upper("hello") # "HELLO"
title("hello world") # "Hello World"
trim(" hello ") # "hello"
trimprefix("helloworld", "hello") # "world"
trimsuffix("helloworld", "world") # "hello"
replace("hello", "l", "x") # "hexxo"
split(",", "a,b,c") # ["a", "b", "c"]
join("-", ["a", "b", "c"]) # "a-b-c"
format("Hello, %s!", "World") # "Hello, World!"
substr("hello", 0, 3) # "hel"
Fonctions numériques
abs(-5) # 5
ceil(4.2) # 5
floor(4.8) # 4
max(1, 5, 3) # 5
min(1, 5, 3) # 1
pow(2, 3) # 8
signum(-5) # -1
Fonctions de collection
# Listes
length([1, 2, 3]) # 3
element(["a", "b", "c"], 1) # "b"
index(["a", "b", "c"], "b") # 1
contains(["a", "b"], "a") # true
concat([1, 2], [3, 4]) # [1, 2, 3, 4]
flatten([[1, 2], [3, 4]]) # [1, 2, 3, 4]
distinct([1, 1, 2, 2, 3]) # [1, 2, 3]
reverse([1, 2, 3]) # [3, 2, 1]
sort(["c", "a", "b"]) # ["a", "b", "c"]
slice([1, 2, 3, 4], 1, 3) # [2, 3]
range(1, 5) # [1, 2, 3, 4]
# Maps
keys({a = 1, b = 2}) # ["a", "b"]
values({a = 1, b = 2}) # [1, 2]
lookup({a = 1}, "a", 0) # 1
lookup({a = 1}, "b", 0) # 0
merge({a = 1}, {b = 2}) # {a = 1, b = 2}
Fonctions de fichiers
file("script.sh") # Contenu du fichier
fileexists("config.yaml") # true/false
filebase64("image.png") # Base64 du fichier
templatefile("template.tpl", {
name = "World"
}) # Fichier rendu
Fonctions de type
tostring(123) # "123"
tonumber("123") # 123
tobool("true") # true
tolist(toset(["a", "b"])) # ["a", "b"]
tomap({a = 1}) # {a = 1}
toset([1, 1, 2]) # toset([1, 2])
try(var.value, "default") # var.value ou "default"
can(var.value.nested) # true si accessible
9 - Dynamic blocks
Syntaxe
resource "aws_security_group" "example" {
name = "example"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
Variable associée
variable "ingress_rules" {
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}
10 - Bonnes pratiques HCL
Formatage
# Formater automatiquement
terraform fmt
# Vérifier le formatage
terraform fmt -check
Nommage
# ✅ Bon - snake_case
resource "aws_instance" "web_server" {}
variable "instance_count" {}
# ❌ Mauvais
resource "aws_instance" "WebServer" {}
variable "instanceCount" {}
Organisation
# ✅ Un fichier par type
# variables.tf - toutes les variables
# outputs.tf - tous les outputs
# main.tf - ressources principales
# ✅ Grouper les ressources liées
# networking.tf - VPC, subnets, routes
# compute.tf - EC2, ASG
# database.tf - RDS, ElastiCache
Résumé
Points clés
- HCL est déclaratif et lisible
- Utilisez les types appropriés pour la validation
- Les expressions for permettent des transformations puissantes
- count et for_each pour créer plusieurs ressources
- Formatez toujours avec
terraform fmt
Exercices pratiques
- Créez des variables de différents types
- Utilisez une expression for pour transformer une liste
- Créez des ressources avec for_each
- Explorez les fonctions de chaînes