Terraform — Expressions et fonctions

De wiki.nexiat.fr
Aller à la navigation Aller à la recherche
Fiche express
Type Système de types HCL, expressions, fonctions intégrées
Types de base primitifs, collections, structurés
Conditionnel condition ? val_vrai : val_faux
Splat liste[*].attribut
Voir aussi Terraform — Concepts fondamentaux · Terraform — Modules et variables · Terraform — Boucles et méta-arguments

Le HCL n'est pas qu'un format de déclaration de blocs : c'est un langage d'expressions à part entière, typé, avec ses opérateurs et plus d'une centaine de fonctions intégrées. Cette page détaille le système de types, les constructions d'expressions (interpolation, conditions, splat) et passe en revue les principales familles de fonctions. Elle complète Terraform — Modules et variables, qui aborde les types côté déclaration de variables.

Système de types

HCL distingue trois familles de types.

Types primitifs

  • string — chaîne de caractères, ex. "rke2-node-01"
  • number — entier ou flottant, ex. 3, 4.2
  • booltrue / false

Types de collection

Regroupent des éléments d'un même type.

  • list(type) — séquence ordonnée, indexable, doublons autorisés
  • set(type) — ensemble non ordonné, sans doublon
  • map(type) — dictionnaire clé (string) → valeur
variable "zones" {
  type    = list(string)
  default = ["a", "b", "c"]
}

variable "tags" {
  type    = map(string)
  default = { environnement = "prod", geree_par = "terraform" }
}

Types structurés

Regroupent des éléments de types potentiellement différents, à structure fixe.

  • object({ ... }) — attributs nommés et typés, façon « struct »
  • tuple([...]) — séquence positionnelle de types hétérogènes
variable "vm_profile" {
  type = object({
    cpu    = number
    memory = number
    disks  = list(number)
  })
  default = {
    cpu    = 4
    memory = 8192
    disks  = [40, 100]
  }
}

Valeur spéciale null

null représente l'absence de valeur. Affecter null à un argument optionnel équivaut généralement à ne pas le renseigner (le provider applique sa valeur par défaut).

Interpolation de chaînes

Toute expression peut être injectée dans une chaîne via ${...} :

name = "rke2-${var.environnement}-${count.index}"

Depuis Terraform 0.12, si la chaîne entière est une seule interpolation (rien avant/après), le résultat garde son type d'origine plutôt que d'être converti en string — utile pour passer un nombre ou une liste sans les « stringifier » involontairement.

Les heredoc (<<-EOT ... EOT) supportent aussi l'interpolation, pratique pour des blocs de configuration multi-lignes (cloud-init, YAML embarqué) :

user_data = <<-EOT
  #cloud-config
  hostname: rke2-${count.index}
EOT

Opérateurs

  • Arithmétiques : + - * / %
  • Comparaison : == != < <= > >=
  • Logiques : && || !
  • Concaténation de listes/chaînes : selon le type, via fonctions dédiées (concat, interpolation) plutôt qu'un opérateur + universel

Expression conditionnelle

Syntaxe ternaire classique :

instance_type = var.environnement == "prod" ? "m5.xlarge" : "t3.medium"

# Combinée à count pour une ressource optionnelle
resource "aws_eip" "this" {
  count = var.assign_public_ip ? 1 : 0
}

Très utilisée pour piloter count (ressource présente ou non) ou choisir une valeur selon l'environnement, sans dupliquer de bloc.

Expressions splat

L'opérateur [*] extrait un attribut de tous les éléments d'une liste/tuple en une seule expression, évitant une boucle explicite :

# Équivalent condensé de [for vm in aws_instance.node : vm.id]
resource "aws_instance" "node" {
  count = 3
  # ...
}

output "ids" {
  value = aws_instance.node[*].id
}

Le splat ne fonctionne que sur des listes/tuples (typiquement issues d'une ressource avec count). Pour une ressource en for_each (map), on utilise plutôt une expression for — voir Terraform — Boucles et méta-arguments.

Expressions for

Transforment une collection en une autre (liste ou map), avec filtrage optionnel :

# Liste -> liste, avec filtre
locals {
  prod_zones = [for z in var.zones : z if z != "sandbox"]
}

# Liste -> map
locals {
  zone_index = { for i, z in var.zones : z => i }
}

Tour d'horizon des fonctions intégrées

Terraform ne permet pas de définir ses propres fonctions : on compose parmi les fonctions intégrées (aucune fonction utilisateur, contrairement à d'autres langages). Quelques familles utiles au quotidien :

Numériques

max(4, 8, 2)        # 8
min(4, 8, 2)         # 2
ceil(4.1)            # 5
floor(4.9)            # 4

Chaînes

lower("RKE2-Node")            # "rke2-node"
format("rke2-%02d", 3)        # "rke2-03"
join("-", ["prod", "toulouse"])   # "prod-toulouse"
split(",", "a,b,c")           # ["a", "b", "c"]
trimspace("  x  ")            # "x"

Collections

concat(["a"], ["b", "c"])          # ["a", "b", "c"]
merge({a = 1}, {b = 2})            # {a = 1, b = 2}
lookup(var.tags, "env", "n/a")     # valeur ou défaut si clé absente
contains(var.zones, "a")           # true
distinct(["a", "a", "b"])          # ["a", "b"]
length(var.zones)                  # nombre d'éléments

Encodage et hachage

jsonencode({ a = 1 })        # sérialisation JSON
yamldecode(file("conf.yaml"))
base64encode("secret")
sha256("valeur")

Pratique pour générer un bloc user_data JSON, ou calculer une empreinte utilisée comme déclencheur de recréation (ex. hash d'un fichier de configuration en argument d'un triggers de provisioner).

Date et heure

timestamp()                         # horodatage courant (ISO 8601)
formatdate("YYYY-MM-DD", timestamp())

À manier avec précaution : timestamp() change à chaque plan, ce qui peut provoquer un diff permanent si la valeur est directement affectée à un attribut de ressource (le documenter explicitement, ex. via ignore_changes — voir Terraform — Boucles et méta-arguments).

Filesystem

file("${path.module}/scripts/init.sh")     # contenu brut d'un fichier
templatefile("${path.module}/cloud-init.tpl", { hostname = "node-01" })
fileexists("${path.module}/optional.conf")

templatefile est la fonction la plus utilisée en pratique : elle interpole des variables dans un fichier gabarit externe, évitant les heredoc géants dans le .tf.

Voir aussi