Featured image of post Terraform Associate #4 — Terraform Configuration

Terraform Associate #4 — Terraform Configuration

Quatrième module de ma préparation à la certification Terraform Associate : resources, data sources, types et conditions, gestion des données sensibles.

Suite de ma série sur la certification Terraform Associate, après le module sur le workflow. Le quatrième objectif de l’exam guide, “Read and write configuration”, est le plus dense. Celui-ci couvre les resources, les data sources, le système de types et les expressions conditionnelles, ainsi que la gestion des sensitive data.

Resources

Le bloc resource est l’unité de base de la configuration : c’est lui qui déclare une ressource gérée par Terraform (créée, modifiée et détruite selon le state).

resource "aws_instance" "web" {
  ami           = "ami-0abcd1234"
  instance_type = "t3.micro"
}
  • Les types de ressources disponibles dépendent des providers installés.

  • aws_instance est le type de ressource, web est le nom local : ensemble ils forment l’adresse aws_instance.web, utilisée pour y référer ailleurs dans la configuration ou en CLI (terraform state show aws_instance.web).

  • Les méta-arguments s’appliquent à n’importe quel type de resource, indépendamment du provider.

  • Ils déterminent comment Terraform crée et gère la ressource, par exemple :

    • count : crée N instances de la ressource, indexées (aws_instance.web[0]).
    • for_each : crée une instance par élément d’une map ou d’un set, indexées par clé (aws_instance.web["a"]) — plus robuste que count quand la liste peut changer, car ça évite de décaler les index existants.
    • depends_on : force une dépendance explicite quand Terraform ne peut pas la déduire des références entre arguments.
    • lifecycle : bloc imbriqué pour affiner le comportement (create_before_destroy, prevent_destroy, ignore_changes).

Data sources

Terraform peut aller chercher des données auprès d’un large éventail de data sources : API, autres workspaces Terraform, outputs de fonctions… Ce fetch dynamique rend la configuration plus flexible, mais ne crée ni ne modifie aucune ressource.

data "aws_ami" "web" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.web.id
  instance_type = "t3.micro"
}
  • Terraform tente d’interroger les data sources dès la phase de plan, mais peut différer la lecture à la phase d’apply selon la configuration.
  • Ajouter un argument depends_on à un bloc data force Terraform à différer la lecture jusqu’à l’apply.
  • On peut ajouter des blocs precondition et postcondition.
  • On peut ajouter les méta-arguments count et for_each aux blocs data pour créer plusieurs instances de la data source.
  • La syntaxe data.<TYPE>.<LABEL>.<ATTRIBUTE> permet de référencer les attributs d’une data resource ailleurs dans la configuration.

Types

Terraform a son propre système de types, utilisé notamment pour contraindre les variables (variable "x" { type = ... }) et pour typer les valeurs manipulées dans la configuration.

  • Primitifs : string, number, bool.
  • Collections (éléments d’un seul type) : list(type), set(type), map(type).
  • Structurels (éléments de types différents, structure fixe) : object({ ... }), tuple([...]).
variable "instance_type" {
  type    = string
  default = "t3.micro"
}

variable "tags" {
  type    = map(string)
  default = {}
}

variable "network" {
  type = object({
    cidr_block = string
    az_count   = number
  })
}

Terraform effectue des conversions implicites quand c’est possible (une list sans type déclaré est inférée, un number peut être converti en string dans une interpolation), mais déclarer le type explicitement sur les variables reste la meilleure protection contre une valeur mal formée passée en entrée d’un module.

Références

On peut référencer les ressources dans notre configuration de plusieurs façons.

Références de ressources

Une adresse de ressource :

[chemin de module][spec de ressource]

Chemin de module

module.nom_module[index de module]

Chemin de module complexe

module.foo[0].module.bar[“a”]

Chemin de spec de ressource

type_de_ressource.nom_ressource[index d’instance]

Valeurs pas encore connues

Quand Terraform planifie des changements pour appliquer la configuration, certaines valeurs ne peuvent pas être renseignées immédiatement car elles sont décidées dynamiquement par le système distant.

Ces valeurs inconnues apparaissent dans la sortie de terraform plan sous la forme (known after apply).

Variables & outputs

Les modules Terraform communiquent entre eux via des inputs et des outputs. Les variables permettent de paramétrer un module pour que d’autres utilisateurs puissent fournir des valeurs personnalisées au runtime.

Outputs

Ils exposent des données d’un module, permettant d’exporter des informations sur l’infrastructure. La valeur d’un bloc output s’apparente à une valeur de retour dans d’autres langages de programmation.

output "instance_public_ip" {
  description = "IP publique de l'instance web"
  value       = aws_instance.web.public_ip
}

Locals

Les locals permettent de définir et réutiliser des expressions au sein d’un module.

locals {
  name_prefix = "${var.project}-${var.environment}"
  common_tags = {
    project = var.project
    env     = var.environment
  }
}

resource "aws_instance" "web" {
  ami           = "ami-0abcd1234"
  instance_type = "t3.micro"
  tags          = local.common_tags
}

Types complexes

Un type complexe regroupe plusieurs valeurs en une seule.

Collection

  • list(...) : une séquence de valeurs identifiées par des entiers consécutifs à partir de zéro.
  • map(...) : une collection de valeurs où chacune est identifiée par un label de type string.
  • set(...) : une collection de valeurs uniques, sans identifiant secondaire ni ordre.
variable "azs" {
  type    = list(string)
  default = ["eu-west-1a", "eu-west-1b"]
}

variable "tags" {
  type    = map(string)
  default = { env = "prod", team = "infra" }
}

variable "allowed_ports" {
  type    = set(number)
  default = [80, 443]
}

Types structurels

  • object(...) : un ensemble d’attributs nommés ayant chacun leur propre type (ex : object({ name=string, age=number })).
  • tuple(...) : une séquence d’éléments identifiés par des entiers consécutifs à partir de zéro, chaque élément ayant son propre type (ex : tuple([string, number, bool])).
variable "network" {
  type = object({
    cidr_block = string
    az_count   = number
  })
  default = {
    cidr_block = "10.0.0.0/16"
    az_count   = 2
  }
}

Any

  • any est rarement le bon type à utiliser. Toujours spécifier le type exact, sauf en cas de données réellement dynamiques.

Validation de la configuration

terraform validate

La validation permet de vérifier que la configuration Terraform fonctionne comme prévu.

Moyens de validation

Terraform propose plusieurs moyens de valider la configuration :

  • Les validations de variables d’input vérifient les paramètres de la configuration au moment du plan.
  • Les preconditions s’assurent qu’une resource, data source ou output individuel respecte les exigences avant que Terraform ne tente de la créer.
  • Les postconditions vérifient que Terraform a produit les resources et data sources avec les propriétés attendues.
  • Les checks permettent de valider que l’infrastructure se comporte comme prévu sans bloquer les opérations Terraform selon leur résultat.
resource "aws_instance" "web" {
  ami           = "ami-0abcd1234"
  instance_type = "t3.micro"

  lifecycle {
    precondition {
      condition     = data.aws_ami.web.architecture == "x86_64"
      error_message = "L'AMI doit être en x86_64."
    }

    postcondition {
      condition     = self.public_ip != ""
      error_message = "L'instance doit avoir une IP publique."
    }
  }
}

check "site_up" {
  data "http" "homepage" {
    url = "https://${aws_instance.web.public_ip}"
  }

  assert {
    condition     = data.http.homepage.status_code == 200
    error_message = "Le site ne répond pas en HTTP 200."
  }
}

Checks

Le bloc check sert à valider l’infrastructure en dehors du cycle de vie habituel des ressources. Il s’exécute en dernière étape d’un plan ou d’un apply, une fois que Terraform a planifié ou provisionné l’infrastructure. Quand l’assertion d’un bloc check échoue, Terraform émet un warning et poursuit l’opération en cours.

Il permet de :

  • Valider des resources, data sources, variables ou outputs de la configuration.
  • Valider le comportement de l’infrastructure dans son ensemble.
  • Vérifier la configuration de l’infrastructure sans bloquer les opérations.
  • Effectuer une validation continue dans HCP Terraform.

Workflow de validation

Terraform valide les différents aspects de la configuration le plus tôt possible. En général, l’ordre d’exécution est le suivant :

Gérer les données sensibles

Terraform peut avoir besoin d’accéder à des données sensibles (credentials cloud, tokens API, autres secrets) pour provisionner l’infrastructure. Terraform stocke le state dans un fichier en clair, qui inclut toute valeur secrète définie dans la configuration.

Argument sensitive

L’argument sensitive sur un bloc variable ou output permet de masquer ces valeurs dans les logs du CLI Terraform et dans l’UI HCP Terraform.

Argument ephemeral

Les valeurs ephemeral sont disponibles au runtime d’une opération, mais Terraform les omet des fichiers de state et de plan.

Quand les utiliser ?

sensitiveephemeral
Où l’utiliserBloc variable ou outputBloc variable, output d’un module enfant, bloc ephemeral, argument write-only, provider, provisioner/connection
Effet sur le state/planReste dans le state, mais masqué dans les logs CLI et l’UI HCP TerraformJamais persisté dans le state ni le plan
Effet sur l’affichageValeur remplacée par (sensitive value) dans le plan/applyValeur disponible uniquement au runtime de l’opération
Portée d’usageUtilisable partout où la variable/output est référencéRestreint aux contextes ephemeral listés ci-dessus
Cas d’usage typiqueMot de passe affiché dans un outputToken temporaire passé à un provider ou une resource

Arguments write-only

Les arguments write-only permettent de passer de façon sécurisée des valeurs temporaires aux managed resources de Terraform pendant une opération, sans les persister dans le state ou les fichiers de plan.

resource "aws_db_instance" "db" {
  identifier             = "app-db"
  password_wo            = var.db_password
  password_wo_version    = 1
}

variable "db_password" {
  type      = string
  ephemeral = true
}

Bonnes pratiques de sécurité

Si des valeurs sensibles sont stockées dans un fichier de state, il est recommandé de mettre en place des mesures de sécurité supplémentaires :

  • Stocker le state à distance.
  • Chiffrer le state au repos.
  • Utiliser des contrôles d’accès pour limiter qui peut accéder au state.
  • Utiliser des logs d’audit pour tracer les accès au state dans le temps.

Ce que je retiens

  • count vs for_each : for_each évite le décalage d’index quand la liste change, à privilégier dès que les éléments sont identifiables par une clé stable.
  • Preconditions, postconditions et checks ne servent pas la même chose : les deux premières bloquent le plan/apply sur une resource précise, les checks valident l’infrastructure globalement sans bloquer.
  • sensitive masque l’affichage, ephemeral évite la persistance : seul ephemeral garantit qu’une valeur n’atterrit jamais dans le state ou le plan.

Prochaine étape de la série : les modules.

Généré avec Hugo
Thème Stack conçu par Jimmy