Skip to content

Les Blocs, le cœur du langage HCL

Le langage HCL a été construit pour qu'il soit facilement lisible pour un humain.
Les constructions du langage peuvent également être exprimées en json pour faciliter la génération et l'analyse par programmation.

Le syntaxe native du langage est construit autour de deux notions : - les arguments attribuent une valeur à un nom myargument = myvalue - les blocks contiennent d'autres informations resource "aws_instance" "example" { ami = "abc123" network_interface { # ... } } Chaque bloc peut contenu un à plusieurs arguments et/ou sous-blocs.

Il existe une convention de style. https://www.terraform.io/language/syntax/style

Les Blocs variables

Les variables peuvent être d'entrée, de sortie ou locale.

Le bloc variable d'entrée

Il possède les arguments suivant qui sont tous optionnels: - type - default - description - validation (bloc avec deux arguments : condition, error_message) - sensitive (true/false) données cryptée dans les sorties et states - nullable (true/false)

example de variables

variable "myvar" {
  type = string
  default = ""
  description = "c'est ma variable"
}

variable "mymap" {
  type = map(object({
    mykey = string
  }))
}

variable "mylist" {
  type    = list(string)
  default = []
}

Utilisation : var.

Le bloc variable de sortie

Les blocs de sortie sont plus simples avec les arguments suivant: - value (obligatoire) - description - sensitive - depends_on (liste des dépendances) - precondition (bloc avec les arguments: condition, error_message)

exemple:

output "instance_ip_addr" {
  value = aws_instance.server.private_ip
}

Les sorties s'affichent dans la cli (terraform plan, apply et output)

Le bloc variable locals

Permet d'attribuer des valeurs utilisables dans les blocs ressource ou data C'est un bloc avec une liste d'argument ou de bloc avec une liste d'arguments

locals {
  service_name = "forum"
  owner        = "Community Team"
} 

locals { 
  common_tags = {
    Service = local.service_name
    Owner   = local.owner
}

Utilisation: local.

Le bloc provider

source: https://www.terraform.io/docs/providers/index.html

Un provider terraform est un fournisseur de ressource que l'on peut automatiquement récupérer par le registry de terraform (https://registry.terraform.io/).

Ces providers sont des binaires en go (comme Terraform). Certains sont directement intégrés (ex: local-exec, file, remote-exec...)

provider "kubernetes" {
  version = "~> 1.10"
}

# Define required providers and their version 
terraform {
required_version = ">= 1.2.2"
  required_providers {
    openstack = {
      source = "terraform-provider-openstack/openstack"
      version = ">= 1.47.0"
    }
  }
}

# Configure the OpenStack Provider
# For openstack, if you have your openrc.sh defined and sourced, this step is not necessary
provider "openstack" {
  user_name   = "admin"
  tenant_name = "admin"
  password    = "pwd"
  auth_url    = "http://myauthurl:5000/v2.0"
  region      = "RegionOne"
}

Les Blocs Resource

Structure d'une ressource

Une ressource est un élément que terraform va créer (CRUD). Terraform va instancier un objet unique pour un même module.

resource "ressource_type" "ressource_nom" {
  arg = "valeur"
}

Exemple : création d'un fichier contenant une liste de nom

resource "local_file" "list" {
    content  = join("/n", ["nom1", "nom2", "nom3"])
    filename = "./list_nom"
}

Une resource peut contenir des meta arguments (depends_on, count, for_each, provider, et lifecycle) qui peuvent servir pour mieux contrôler la ressource.

Ex: Création de deux fichiers avec noms indexés

resource "local_file" "list" {
    count = 2
    content  = join("/n", ["nom1", "nom2", "nom3"])
    filename = "./list_nom_{{list.index}}"
}

Les Meta-argument

  • depends_on : spécifie une dépendance à une ou plusieurs autres ressources
  • count : permet de faire des itérations avec un compteur (count.index)
  • for_each : permet de faire des itérations avec une map (each.key, each.value)
  • provider : précise le provider à utiliser si plusieurs
  • lifecycle : Gère le cycle de vie de la ressource suivant certains critères (argument: create_before_destroy, prevent_destroy, ignore_changes et replace_triggered_by)

Les provisioner

Ce sont des structures spéciales à l'intérieur de bloc de ressource pour effectuer des actions spécifiques.

  • connection : Bloc contenant les informations pour une connexion à distance (peut parfois être dans un autre bloc file ou remote-exec)
  • file : permet de copier un fichier ou un dossier
  • local-exec : permet d'exécuter un script shell sur la machine local
  • remote-exec : permet d'exécuter un script shell sur une machine distance

Provisioner sans ressource

Parfois, nous avons besoin d'executer les copies ou des scripts sans forcément utiliser une ressource d'un provider.
C'est pourquoi Terraform fournit une ressource null_resource qui permet ce cas de figure.

exemple:

resource "aws_instance" "cluster" {
  count = 3
  # ...
}

resource "null_resource" "cluster" {
  # Changes to any instance of the cluster requires re-provisioning
  triggers = {
    cluster_instance_ids = "${join(",", aws_instance.cluster.*.id)}"
  }

  # Bootstrap script can run on any instance of the cluster
  # So we just choose the first in this case
  connection {
    host = "${element(aws_instance.cluster.*.public_ip, 0)}"
  }

  provisioner "remote-exec" {
    # Bootstrap script called with private_ip of each node in the cluster
    inline = [
      "bootstrap-cluster.sh ${join(" ", aws_instance.cluster.*.private_ip)}",
    ]
  }
}

Les Blocs data

Un bloc data est une sorte de bloc resource déjà existant en lecture seule.

La structure est la similaire au bloc resource. Terraform ne crée rien et ne fait que lire les informations contenues dedans.

Meta-argument

Les blocs Data possèdent les mêmes meta-arguments que les blocs ressource: depends_on , count, for_each, provider, lifecycle (postcondition: condition, error_message)

Exemple

Avec le provider d'openstack, on récupère les informations de la zone dns nommée 'pagoda.os.univ-lyon1.fr' qui seront stocker dans l'objet "zone_1". Puis on affiche différents attributs sous forme de liste.

data "openstack_dns_zone_v2" "zone_1" {
  name = "pagoda.os.univ-lyon1.fr."
}

output "list_dns_attribut"{
  value = [
    openstack_dns_zone_v2.zone_1.id,
    openstack_dns_zone_v2.zone_1.name,
    openstack_dns_zone_v2.zone_1.attributes,
    openstack_dns_zone_v2.zone_1.pool_id
  ]
}

Les Blocs Module

Les modules sont des blocs qui regroupent un ensemble de ressources ou de modules.

Le fichier principal lui-même est un module: le module root.

Par ce principe, il est possible de découper l'ensemble des ressources en module.

Les modules ont cette structure:

module "mymodule" {
  source  = ".modules/mymodule"
  version = "0.0.1"
  mymodyle = 3 
}

Les méta-arguments

  • source : dossier où est placé le module enfant ou le provider
  • version : version du module à instancier
  • depends_on : spécifie une dépendance à une ou plusieurs autres ressources
  • count : permet de faire des itérations avec un compteur (count.index)
  • for_each : permet de faire des itérations avec une map (each.key, each.value)
  • providers : précise le ou les providers à utiliser si plusieurs
  • lifecycle : Gère le cycle de vie du module
. Parent Child1
main root/main.tf root/child.main.fr
ou
root/modules/child1/main.tf
variable var avec une valeur >--- ---> var déclarée
output output déclaré <--- ---< output donne le retour

Pour qu'une variable d'un parent soit transmise à un enfant, il faut que l'enfant ait déclarer cette variable. Idem, un output parent se réfère toujours à un output enfant.

Le principe est le même que pour un appel de fonction.

Si les variables ou les outputs ne sont pas correctement déclarés, Terraform affiche une erreur dans le plan.

Attention: Un module parent n'est pas obligé de fournir toutes les valeurs des variables déclarées dans le module enfant. Si la déclaration des variables du module enfant comprennent des valeurs par défaut. Si lla valeur par défaut n'existe pas, l'instanciation de la variable est obligatoire et l'envoie de la valeur du parent à l'enfant.