Teraform

Concept

  • Construire
  • Modifier
  • Versionner

Explosion avec le cloud, API centree
Nombreux providers de dispo (cloud, software, reseau, database etc...)
Action sur l'infra via fichier de conf (HCL) == InfraAsCode
Stateful vs Ansible qui lui est stateless
Gros interet par rapport a Ansible car si container nginx installe puis on decide d'installer apache alors il deploie apache et surtout supprimer nginx (interet du stateful)
Utilisation : IAC, maintien d'infra, CI/CD, automatisation d'infra
Gros interet par rapport

State : * Stockage de l'etat de l'infra et sa config
* state = terraform.tfstate * tfstate (reel) >> plan (voulu) >> changements/creation

Different etapes : * refresh * plan * apply * destroy

Fichiers utilises = .tf
Developpe en GO
Resource == une brique d'infra (instance, containers, switch etc...)
Utilisation de l'API des providers !!

Expressions

Conditional Expressions documents the <CONDITION> ? <TRUE VAL> : <FALSE VAL> expression, which chooses between two values based on a bool condition.

Ex : Ici on définit un password pk si la variable certificates_pk_encryption est définie alors on vérifie si la variable vs_server_url.type est égal à Thycotic si oui on recupère le password Thycotic sinon celui de cyberark (que deux types différents dans la definition des variables). Si certificates_pk_encryption n'est pas défini alors on set à null

pk_password = var.certificates_pk_encryption ? (var.vs_server_url.type == "thycotic" ? data.tss_secret.pk_password[0].value : data.conjur_secret.pk_password[0].value) : ""

For Expressions documents expressions like [for s in var.list : upper(s)], which can transform a complex type value into another complex type value.

Symbol

The symbol =>

Ici on voit à quoi sert le symbole "=>", il permet d'associer la clé avec la valeur désignée. Ici la clé rules

locals {
  groups = {
    example0 = {
      description = "sg description 0"
      rules = [{
        description = "rule description 0",
      },{
        description = "rule description 1",
      }]
    },
    example1 = {
      description = "sg description 1"
      rules = [{
        description = "rule description 0",
      },{
        description = "rule description 1",
      }]
    }
  }
  rules = { for k,v in local.groups : k => v.rules }
}
output "rules" {
  value = local.rules
}

[OUTPUT]

rules = {
  "example0" = [
    {
      "description" = "rule description 0"
    },
    {
      "description" = "rule description 1"
    },
  ] etc...

Resource

Element qui peut etre CRUD (CreateRemoveUpdateDelete) via le provider
Un objet d'une ressource est unique (un nom) dans un meme module

Format :

resource "resource_type" "resource_nom"{
  arg = "valeur"
}

Exemple :

resource "aws_instance" "web"{
  ami = "som-ami-id"
  instance_type = "t2.micro"
}

Data Source

C'est une resource non modifiable

data "aws_ami" "ubuntu" {
  most_recent = true
  filter {
    name   = "name"
    values = ["myami-*"]
  }
}

Meta arguments

resource "ressource_type" "ressource_nom" {
  count = nb #Pour faire de l'iteration sur la resource
  arg = "valeur"
}

ForEach

Pour iterer

variable "instances" {
  type = "map"
  default = {
    clef1 = "123"
    clef2 = "456"
    clef3 = "789"
  }
}
resource "aws_instance" "server" {
  for_each = var.instances 
  ami = each.value
  instance_type = "t2.micro"
  tags = {
    Name = each.key
  }
}

Variables

3 type de variables :

  • string
  • list
  • map
  • number
  • bool

Deux manieres de declarer une variable :

output "mavariable" {
  value = var.str
}
output "mavariable" {
  value = "${var.str}"
}

Exemple :

String

variable "str" {
  type = string
  default ="127.0.0.1 gitlab.test"
}

resource "null_resource" "node1" {
 provisioner "local-exec" {
  command = "echo '${var.str}' > hosts.txt"
 }
}

Map

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"
 }
}

On peut utiliser un trigger aussi qui lorsque sa valeur change déclanche le provisioner

resource "aws_instance" "cluster" {
  count = 3
}
resource "null_resource" "cluster" {
  triggers = {
    cluster_instance_ids = join(",", aws_instance.cluster.*.id)
  }
  connection {
    host = element(aws_instance.cluster.*.public_ip, 0)
  }
  provisioner "remote-exec" {
    inline = [
      "bootstrap-cluster.sh ${join(" ", aws_instance.cluster.*.private_ip)}",
    ]
  }
}

List

variable "hosts" {
  default     = ["127.0.0.1 localhost","192.168.1.133 gitlab.test"]
}
resource "null_resource" "hosts" {
 count = "${length(var.hosts)}"
 provisioner "local-exec" {
  command = "echo '${element(var.hosts, count.index)}' >> hosts.txt"
 }
}

pro

  • definitionplusieurs niveaux : environnement > fichier specifie

  • ordre des variables

  • 1 - environnement
  • 2 - fichier : terraform.tfvars
  • 3 - fichier json : terraform.tfvars.json
  • 4 - fichier *.auto.tfvars ou *.auto.tfvars.json
  • 5 - CLI : -var ou - var-file

Modules

Equivalent d'un role Ansible
Structure d'un module :
https://www.terraform.io/language/modules/develop/structure

  • utilisation d'un module
module "monmodule" {
  source = "./rep_module"
}
  • principe d'heritage du provider

    • par defaut celui du fichier dans lequel il est appele
    • prossibilite de reciser le provider
  • possiblite d'instancier plusieurs fois un meme module

module "instance1" {
  source = "./rep_module"
}
module "instance2" {
  source = "./rep_module"
}

Installation d'un module

terraform get
terraform init
  • peut permettre de gerer la gestion de dependance (pour les prerequis)
terraform apply -target=module.docker
terraform apply -target=module.postgres

Modules - Docker

On peut creer des containers via le module docker

Exemple de creation du container nginx:

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "2.16.0"
    }
  }
}

provider "docker" {
  host = "tcp://127.0.0.1:2375"
}

resource "docker_image" "nginx" {
  name = "nginx:latest"
}
resource "docker_container" "nginx" {
  image = docker_image.nginx.latest
  name  = "enginecks"
  ports {
    internal = 80
    external = 80
  }
}

Ici ce qui le plus important c'est d'avoir acces a la socket docker distante pour utiliser son API (securiser avec TLS en prod)

Module - K8s

DATA SOURCES : sources d'informations (rend de l'info et ne cree rien)

  • kubernetes_all_namespaces : liste de tous les NS
  • kubernetes_config_map : acces aux configmap
  • kubernetes_ingress : liste des ingress
  • kubernetes_namespace : information sur un namespace
  • kubernetes_secret : liste des secrets (configmaps like)
  • kubernetes_service_account : compte de service utilise dans les pods
  • kubernetes_service : liste des informations sur un service
  • kubernetes_storage_class : informations relatives aux classes de stockages (association PV ET PVC)

RESOURCES : creation d'objets

  • kubernetes_pod
  • kubernetes_deployment
  • kubernetes_stateful_set (persisntance)
  • kubernetes_namespace
  • kubernetes_replication_controller
  • kubernetes_service
  • kubernetes_config_map
  • kubernetes_secret

Destroy

Destroy sur kubernetes a des effets tres precis. C'est l'inverse de apply

On peut choisir grace a target de supprimer une ressource ou un module