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.