Aller au contenu principal

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

  1. Créez des variables de différents types
  2. Utilisez une expression for pour transformer une liste
  3. Créez des ressources avec for_each
  4. Explorez les fonctions de chaînes

← Installation | Providers →