Pour faire appel à un provider spécifique, on doit l'appeler dans notre fichier .tf
:
provider "kubernetes" {
version = "~> 1.25"
}
resource "resource_type" "resource_name" {
arg = "value"
}
On peut par exemple prendre un exemple (non fonctionnel) pour le provider GCP :
resource "google_compute_instance" "default" {
name = "test"
machine_type = "e2-medium"
zone = "us-central1-a"
}
# Element qu'on fourni qu'une fois et non modifiable
data "google_iam_policy" "admin" {
provider = google-beta
binding {
role = "roles/compute.admin"
members = [
"user:jane@example.com",
]
}
}
count = itération
resource "resource_type" "resource_name" {
count = nb
arg = "value"
}
For each
variable "instances" {
type = "map" # Type map qui nous permet de la parcourir avec un for_each
default = {
key1 = "123"
key2 = "456"
key3 = "789"
}
}
resource "axs_instance" "server" {
for_each = var.instances # On parcourt la variable "instances"
ami = each.value # On peut parcourir via le mot clé "each", "value" aura la valeur "123" "456"...
instance_type = "t2.micro"
tags = {
Name = each.key # On veut que le tag = clé du map, soit "key1", "key2"...
}
}
terraform.tfstate
.state
en remote sur un système compatible (s3, bucket GCP, postgres...) qui permet de locker le fichier quand quelqu'un le modifie.remote-exec
: exécution sur machine distantelocal-exec
: exécution sur la machine terraformType de variables :
variable "str" {
type = string
default = "127.0.0.1 gitlab.test"
}
resource "null_resource" "node1" {
provisioner "local-exec" {
command = "echo '${var.str}' > hosts.txt"
}
}
Exécution du fichier :
❯ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/null...
- Installing hashicorp/null v3.2.1...
- Installed hashicorp/null v3.2.1 (signed by HashiCorp)
❯ tf apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# null_resource.node1 will be created
+ resource "null_resource" "node1" {
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
null_resource.node1: Creating...
null_resource.node1: Provisioning with 'local-exec'...
null_resource.node1 (local-exec): Executing: ["/bin/sh" "-c" "echo '127.0.0.1 gitlab.test' > hosts.txt"]
null_resource.node1: Creation complete after 0s [id=1592973278992609483]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
❯ ls
hosts.txt main.tf terraform.tfstate
❯ cat hosts.txt
127.0.0.1 gitlab.test
variable "hosts" {
default = {
"127.0.0.1" = "localhost gitlab.local"
"192.169.1.168" = "gitlab.test"
"192.169.1.170" = "prometheus.test"
}
}
resource "null_resource" "hosts" {
for_each = var.hosts
provisioner "local-exec" {
command = "echo '${each.key} ${each.value}' >> hosts.txt"
}
}
Le résultat du fichier hosts.txt
sera le suivant :
❯ tf apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# null_resource.hosts["127.0.0.1"] will be created
+ resource "null_resource" "hosts" {
+ id = (known after apply)
}
# null_resource.hosts["192.169.1.168"] will be created
+ resource "null_resource" "hosts" {
+ id = (known after apply)
}
# null_resource.hosts["192.169.1.170"] will be created
+ resource "null_resource" "hosts" {
+ id = (known after apply)
}
Plan: 3 to add, 0 to change, 0 to destroy.
...
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
❯ cat hosts.txt
192.169.1.170 prometheus.test
127.0.0.1 localhost gitlab.local
192.169.1.168 gitlab.test
Le point négatif en utilisant command
c'est que terraform ne peut pas voir la différence dans le fichier hosts.txt
. Si on supprime une ligne du fichier --> Terraform ne le verra pas.
Le deuxième problème dans le script ci-dessus, c'est que si on ajoute une valeur dans 127.0.0.1
par exemple, terraform ne verra pas le changement ! Pour renoncer à cela, il faut utiliser un triggers
:
variable "hosts" {
default = {
"127.0.0.1" = "localhost gitlab.local"
"192.169.1.168" = "gitlab.test"
"192.169.1.170" = "prometheus.test"
}
}
resource "null_resource" "hosts" {
for_each = var.hosts
triggers = {
foo = each.value
}
provisioner "local-exec" {
command = "echo '${each.key} ${each.value}' >> hosts.txt"
}
}
En appliquant ce fichier, si je rajoute une valeur, le changement sera détecté ! Cependant cela ne supprimera pas la ligne dans notre cas.
variable "hosts" {
default = ["127.0.0.1 test.com", "192.168.1.1 wiki.com"]
}
resource "null_resource" "hosts" {
count = length(var.hosts) # Récupère le nombre d'éléments dans la liste, sorte de while
triggers = {
foo = element(var.hosts, count.index) # Récupère la valeur de l'élément numéro X dans la variable "hosts"
}
provisioner "local-exec" {
command = "echo '${element(var.hosts, count.index)}' >> hosts.txt" # Récupère l'élément numéro X dans la variable "hosts"
}
}
La précédence des variables dans Terraform est l'ordre dans lequel Terraform utilise les différentes sources de variables pour définir les valeurs de variable lors de l'exécution de la planification ou de l'application d'un plan.
Les sources de variables dans l'ordre de précédence sont :
TF_VAR_<nomvar>
terraform.tfvars
terraform.tfvars.json
*.auto.tfvars
ou *.auto.tfvars.json
-var
ou -var-file
Les variables via CLI seront donc plus importantes que les variables d'environnement.
Exemple :
❯ cat main.tf
variable "myvar" {}
output "mavariable" {
value = var.myvar
}
❯ export TF_VAR_myvar="testwiki"
❯ tf apply
...
Outputs:
mavariable = "testwiki"
Si on met un terraform.tfvars
, la variable d'env ne sera plus prise en compte :
❯ cat terraform.tfvars
myvar="wikiiiiii"
❯ tf apply
...
Outputs:
mavariable = "wikiiiiii"
Exemple piégeux :
❯ tf apply -var 'myvar="test"' -var-file varfile.tfvars
Ici ca sera -var-file
qui sera pris en compte car terraform prend en compte les paramètres de gauche à droite.
Exemple simple d'un remote exec avec le transfert d'un fichier de configuration nginx.conf
:
nginx.conf
dans /tmp/default
de la machine distante`nginx.conf
au bon endroit et redémarre le service nginxvariable "ssh_host" {}
variable "ssh_user" {}
variable "ssh_key" {}
resource "null_resource" "ssh_target" {
connection {
type = "ssh"
user = var.ssh_user
host = var.ssh_host
private_key = file(var.ssh_key)
}
provisioner "remote-exec" {
inline = [
"sudo apt update -qq >/dev/null",
"sudo apt install -qq -y nginx >/dev/null"
]
}
provisioner "file" { # Nécessite une "connection"
source = "nginx.conf"
destination = "/tmp/default" # N'a pas les droits ailleurs
}
provisioner "remote-exec" {
inline = [
"sudo cp -a /tmp/default /etc/nginx/sites-available/default",
"sudo systemctl restart nginx"
]
}
provisioner "local-exec" {
command = "curl ${var.ssh_host}:80"
}
}
output "host" {
value = var.ssh_host
}
output "user" {
value = var.ssh_user
}
output "key" {
value = var.ssh_key
}