Formation Terraform

Infrastructure as Code - Provisionnement et gestion d'infrastructure cloud

🚀 Introduction à Terraform

Qu'est-ce que Terraform ?

Terraform est un outil open-source développé par HashiCorp qui permet de définir, déployer et gérer l'infrastructure cloud à travers du code (Infrastructure as Code - IaC).

đŸ—ïž Infrastructure as Code

Définir l'infrastructure avec du code déclaratif plutÎt que par des processus manuels.

🔄 Multi-Provider

Support de multiples fournisseurs cloud (AWS, Azure, GCP, etc.) avec une syntaxe unifiée.

📊 État de l'Infrastructure

Suivi automatique de l'état actuel de votre infrastructure.

🎯 DĂ©claratif

Décrivez ce que vous voulez, Terraform détermine comment l'obtenir.

Avantages de Terraform

💡 Conseil : Terraform est idĂ©al pour gĂ©rer l'infrastructure de production de maniĂšre cohĂ©rente et prĂ©visible. Commencez par des ressources simples avant de passer Ă  des architectures complexes.

⚙ Installation et Configuration

Installation de Terraform

Linux / macOS

# Télécharger Terraform wget https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_linux_amd64.zip # Décompresser l'archive unzip terraform_1.6.0_linux_amd64.zip # Déplacer vers /usr/local/bin sudo mv terraform /usr/local/bin/ # Installation via Homebrew (macOS) brew install terraform # Installation via APT (Ubuntu/Debian) sudo apt-get install terraform

Windows

# Installation via Chocolatey choco install terraform # Alternative : téléchargement manuel # Aller sur https://www.terraform.io/downloads.html

Vérification de l'installation

# Vérifier la version installée terraform version # Afficher l'aide terraform -help

Configuration des providers

# Définir l'Access Key AWS export AWS_ACCESS_KEY_ID="votre-access-key" # Définir la Secret Key AWS export AWS_SECRET_ACCESS_KEY="votre-secret-key" # Définir la région par défaut AWS export AWS_DEFAULT_REGION="eu-west-1" # Se connecter à Azure az login # Définir les credentials Google Cloud export GOOGLE_APPLICATION_CREDENTIALS="chemin/vers/service-account.json"
⚠ SĂ©curitĂ© : Ne jamais hardcoder les credentials dans les fichiers Terraform. Utilisez les variables d'environnement ou des services de gestion de secrets.

🎯 Concepts Fondamentaux

📄 Configuration

Fichiers .tf qui décrivent l'infrastructure désirée en langage HCL (HashiCorp Configuration Language).

🔌 Provider

Plugin qui permet Ă  Terraform d'interagir avec des APIs (AWS, Azure, GCP, etc.).

đŸ—ïž Resource

ÉlĂ©ment d'infrastructure (VM, rĂ©seau, base de donnĂ©es) gĂ©rĂ© par Terraform.

📊 State

Fichier qui mappe la configuration Terraform aux ressources réelles.

📋 Plan

Description des actions que Terraform va effectuer pour atteindre l'état désiré.

📩 Module

Collection réutilisable de ressources Terraform.

Workflow Terraform

# Initialiser le répertoire de travail terraform init # Créer un plan d'exécution terraform plan # Appliquer les changements terraform apply # Détruire l'infrastructure terraform destroy

📝 Syntaxe HCL (HashiCorp Configuration Language)

Structure de base

# Commentaire explicatif resource "aws_instance" "example" { ami = "ami-0c55b159cbfafe1d0" instance_type = "t2.micro" tags = { Name = "ExampleInstance" } }

Types de blocs

1. Provider

provider "aws" { region = "eu-west-1" } provider "azurerm" { features {} }

2. Resource

resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Name = "main-vpc" } }

3. Data Source

data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] # Canonical filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] } }

4. Variables

variable "region" { description = "AWS region" type = string default = "eu-west-1" } variable "instance_count" { description = "Number of instances" type = number default = 2 }

5. Outputs

output "instance_ip" { description = "Public IP of the instance" value = aws_instance.example.public_ip }

Types de données

string

"Hello World"

number

42 ou 3.14

bool

true ou false

list

["a", "b", "c"]

map

{key1 = "value1", key2 = "value2"}

object

Structure complexe avec types typés

🔌 Providers et Resources

Configuration des Providers

terraform { required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } } }

Providers populaires

🟡 AWS

Amazon Web Services - Le plus utilisé

đŸ”” Azure

Microsoft Azure - DeuxiĂšme plus populaire

🔮 GCP

Google Cloud Platform

🟣 Kubernetes

Gestion des ressources Kubernetes

🟱 Docker

Gestion des conteneurs Docker

⚫ Null

Exécution de scripts et provisioners

Exemples de Resources

AWS EC2 Instance

resource "aws_instance" "web_server" { ami = data.aws_ami.ubuntu.id instance_type = "t3.micro" vpc_security_group_ids = [aws_security_group.web.id] subnet_id = aws_subnet.public.id user_data = <<-EOF #!/bin/bash apt-get update apt-get install -y nginx systemctl start nginx systemctl enable nginx EOF tags = { Name = "WebServer" Environment = "prod" } }

Azure Virtual Machine

resource "azurerm_virtual_machine" "main" { name = "acctvm" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name network_interface_ids = [azurerm_network_interface.main.id] vm_size = "Standard_DS1_v2" storage_image_reference { publisher = "Canonical" offer = "UbuntuServer" sku = "18.04-LTS" version = "latest" } storage_os_disk { name = "myosdisk1" caching = "ReadWrite" create_option = "FromImage" managed_disk_type = "Standard_LRS" } }
📚 Documentation : Chaque provider a sa propre documentation avec tous les resources disponibles. Consultez registry.terraform.io pour les dĂ©tails.

🔧 Variables et Outputs

Déclaration de Variables

variable "environment" { description = "Environment name" type = string default = "dev" validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be dev, staging, or prod." } } variable "instance_config" { description = "Instance configuration" type = object({ instance_type = string disk_size = number public_ip = bool }) default = { instance_type = "t3.micro" disk_size = 20 public_ip = false } }

Utilisation des Variables

resource "aws_instance" "example" { ami = var.ami_id instance_type = var.instance_config.instance_type root_block_device { volume_size = var.instance_config.disk_size } tags = { Name = "${var.environment}-instance" } }

Fichiers de Variables

terraform.tfvars

environment = "prod" region = "eu-west-1" instance_config = { instance_type = "t3.small" disk_size = 50 public_ip = true }

Variables d'environnement

# Définir la variable d'environnement pour l'environment export TF_VAR_environment="prod" # Définir la variable d'environnement pour la région export TF_VAR_region="eu-west-1"

Outputs

output "instance_public_ip" { description = "Public IP address of the instance" value = aws_instance.example.public_ip sensitive = false } output "vpc_id" { description = "VPC ID" value = aws_vpc.main.id } output "database_connection" { description = "Database connection string" value = aws_db_instance.main.endpoint sensitive = true }

Local Values

locals { common_tags = { Environment = var.environment Project = "MyProject" ManagedBy = "Terraform" } instance_name = "${var.environment}-${var.application_name}" } resource "aws_instance" "example" { ami = var.ami_id instance_type = var.instance_type tags = merge(local.common_tags, { Name = local.instance_name }) }

📩 Modules Terraform

Qu'est-ce qu'un Module ?

Un module est un conteneur pour plusieurs ressources utilisées ensemble. Chaque configuration Terraform comprend au moins un module, appelé module racine.

Structure d'un Module

modules/ └── vpc/ ├── main.tf # Resources principales ├── variables.tf # DĂ©clarations de variables ├── outputs.tf # DĂ©clarations d'outputs └── README.md # Documentation

Exemple de Module VPC

modules/vpc/main.tf

resource "aws_vpc" "main" { cidr_block = var.cidr_block enable_dns_hostnames = var.enable_dns_hostnames enable_dns_support = var.enable_dns_support tags = merge(var.tags, { Name = var.name }) } resource "aws_subnet" "public" { count = length(var.public_subnets) vpc_id = aws_vpc.main.id cidr_block = var.public_subnets[count.index] availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true tags = merge(var.tags, { Name = "${var.name}-public-${count.index + 1}" Type = "public" }) } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = merge(var.tags, { Name = "${var.name}-igw" }) }

modules/vpc/variables.tf

variable "name" { description = "Name prefix for resources" type = string } variable "cidr_block" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16" } variable "public_subnets" { description = "List of public subnet CIDR blocks" type = list(string) default = ["10.0.1.0/24", "10.0.2.0/24"] } variable "availability_zones" { description = "List of availability zones" type = list(string) } variable "enable_dns_hostnames" { description = "Enable DNS hostnames in VPC" type = bool default = true } variable "tags" { description = "Tags to apply to resources" type = map(string) default = {} }

modules/vpc/outputs.tf

output "vpc_id" { description = "ID of the VPC" value = aws_vpc.main.id } output "public_subnet_ids" { description = "IDs of the public subnets" value = aws_subnet.public[*].id } output "internet_gateway_id" { description = "ID of the Internet Gateway" value = aws_internet_gateway.main.id }

Utilisation d'un Module

module "vpc" { source = "./modules/vpc" name = "production" cidr_block = "10.0.0.0/16" public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] availability_zones = ["eu-west-1a", "eu-west-1b", "eu-west-1c"] tags = { Environment = "production" Project = "MyApp" } } # Utilisation des outputs du module resource "aws_instance" "web" { ami = "ami-0c55b159cbfafe1d0" instance_type = "t3.micro" subnet_id = module.vpc.public_subnet_ids[0] }

Modules depuis le Registry

module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.0" name = "my-vpc" cidr = "10.0.0.0/16" azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] enable_nat_gateway = true enable_vpn_gateway = true tags = { Terraform = "true" Environment = "dev" } }

📊 Gestion de l'État (State)

Qu'est-ce que le State ?

Le fichier state (terraform.tfstate) mappe vos configurations Terraform aux ressources réelles. Il sert de "source de vérité" pour votre infrastructure.

📍 Mapping

Lie la configuration aux ressources réelles

📈 Performance

Cache les métadonnées pour éviter les appels API

🔒 Verrouillage

Prévient les modifications concurrentes

🔄 Synchronisation

Assure la cohérence en équipe

Backend Configuration

Local Backend (par défaut)

terraform { backend "local" { path = "terraform.tfstate" } }

S3 Backend (Recommandé)

terraform { backend "s3" { bucket = "my-terraform-state-bucket" key = "infrastructure/terraform.tfstate" region = "eu-west-1" encrypt = true dynamodb_table = "terraform-locks" } }

Azure Backend

terraform { backend "azurerm" { resource_group_name = "tfstate" storage_account_name = "tfstate0123456789" container_name = "tfstate" key = "infrastructure.tfstate" } }

Commandes State

# Lister toutes les ressources dans le state terraform state list # Afficher les détails d'une ressource spécifique terraform state show aws_instance.example # Télécharger le state depuis le backend terraform state pull # Envoyer le state vers le backend terraform state push # Déplacer une ressource dans le state terraform state mv aws_instance.old aws_instance.new # Supprimer une ressource du state terraform state rm aws_instance.example

Import de Ressources

# Importer une ressource existante dans le state terraform import aws_instance.example i-0123456789abcdef0 # Configuration minimale pour l'import (dans un fichier .tf) resource "aws_instance" "example" { ami = "ami-12345678" instance_type = "t2.micro" }
⚠ Attention : Ne jamais Ă©diter manuellement le fichier state. Utilisez toujours les commandes Terraform pour le modifier.

⚡ Commandes Terraform

Commandes de Base

# Initialiser un répertoire Terraform terraform init # Créer un plan d'exécution terraform plan # Appliquer les modifications terraform apply # Détruire l'infrastructure terraform destroy # Valider la syntaxe des fichiers terraform validate # Formater les fichiers Terraform terraform fmt # Afficher l'état ou le plan terraform show # Afficher les valeurs de sortie terraform output # Mettre à jour l'état avec la réalité terraform refresh # Générer un graphique des dépendances terraform graph

Options Utiles

# Plan avec fichier de variables spécifique terraform plan -var-file="prod.tfvars" # Apply avec confirmation automatique terraform apply -auto-approve # Apply uniquement certaines ressources terraform apply -target=aws_instance.web # Plan avec sortie dans un fichier terraform plan -out=tfplan # Apply depuis un plan sauvegardé terraform apply tfplan # Destroy avec confirmation automatique terraform destroy -auto-approve # Validation sans couleurs terraform validate -no-color # Format récursif de tous les fichiers terraform fmt -recursive

Workspace Management

# Lister les workspaces terraform workspace list # Créer un nouveau workspace terraform workspace new production # Changer de workspace terraform workspace select production # Supprimer un workspace terraform workspace delete staging # Afficher le workspace actuel terraform workspace show

✅ Bonnes Pratiques

📁 Structure de Projet

Organisez vos fichiers logiquement avec des modules réutilisables

🔐 SĂ©curitĂ©

Ne jamais exposer de credentials dans le code

📊 State Management

Utilisez un backend distant pour le state

đŸ·ïž Tagging

Appliquez des tags cohérents à toutes les ressources

🔄 Versionning

Versionnez vos modules et configurations

đŸ§Ș Testing

Testez vos configurations avant la production

Structure de Projet Recommandée

terraform-infrastructure/ ├── environments/ │ ├── dev/ │ │ ├── main.tf │ │ ├── variables.tf │ │ ├── outputs.tf │ │ └── terraform.tfvars │ ├── staging/ │ └── prod/ ├── modules/ │ ├── vpc/ │ ├── ec2/ │ └── rds/ ├── global/ │ ├── s3/ │ └── iam/ └── README.md

Conventions de Nommage

# Ressources resource "aws_instance" "web_server" { # snake_case name = "web-server-prod" # kebab-case pour noms } # Variables variable "environment_name" { # snake_case description = "Environment name" } # Modules module "application_vpc" { # snake_case source = "./modules/vpc" }

Gestion des Secrets

# ❌ JAMAIS comme ça resource "aws_db_instance" "main" { password = "super_secret_password" # JAMAIS ! } # ✅ Utilisez des variables resource "aws_db_instance" "main" { password = var.db_password } # ✅ Ou des services managĂ©s resource "aws_db_instance" "main" { manage_master_user_password = true }

Validation et Tests

# Vérifier le formatage des fichiers terraform fmt -check # Valider la configuration terraform validate # Créer un plan pour vérifier les changements terraform plan # Exemple de test Terratest (Go) func TestTerraformExample(t *testing.T) { terraformOptions := &terraform.Options{ TerraformDir: "../", } # Nettoyer aprÚs les tests defer terraform.Destroy(t, terraformOptions) # Initialiser et appliquer terraform.InitAndApply(t, terraformOptions) # Ajouter ici vos tests spécifiques }
🎯 Conseil Pro : Utilisez terraform plan avant chaque apply, mĂȘme en dĂ©veloppement. Cela vous aide Ă  comprendre les changements et Ă©viter les surprises.

🎓 Exercices Pratiques

Exercice 1 : PremiĂšre Infrastructure

Objectif : Créer une instance EC2 simple avec un groupe de sécurité.

TĂąches :

  • Configurer le provider AWS
  • CrĂ©er un groupe de sĂ©curitĂ© autorisant SSH (port 22)
  • Lancer une instance t3.micro Ubuntu
  • Afficher l'IP publique en output

Exercice 2 : Infrastructure avec Variables

Objectif : Refactoriser l'exercice 1 avec des variables.

TĂąches :

  • CrĂ©er des variables pour la rĂ©gion, type d'instance, AMI
  • Utiliser un fichier terraform.tfvars
  • Ajouter des tags variables
  • ImplĂ©menter des validations sur les variables

Exercice 3 : Module Réutilisable

Objectif : Créer un module pour déployer une application web.

TĂąches :

  • CrĂ©er un module avec VPC, subnets, et instances
  • Configurer un load balancer
  • ImplĂ©menter l'auto-scaling
  • Utiliser le module dans deux environnements

Exercice 4 : Backend Distant

Objectif : Configurer un backend S3 avec verrouillage DynamoDB.

TĂąches :

  • CrĂ©er un bucket S3 pour le state
  • Configurer une table DynamoDB pour le verrouillage
  • Migrer le state local vers S3
  • Tester le verrouillage avec deux terminaux

Solutions Types

Solution Exercice 1

# Configuration du provider AWS provider "aws" { region = "eu-west-1" } # Création d'un groupe de sécurité pour le web resource "aws_security_group" "web" { name_prefix = "web-sg" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } # Récupération de l'AMI Ubuntu la plus récente data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] } } # Création de l'instance EC2 resource "aws_instance" "web" { ami = data.aws_ami.ubuntu.id instance_type = "t3.micro" vpc_security_group_ids = [aws_security_group.web.id] tags = { Name = "WebServer" } } # Output de l'adresse IP publique output "public_ip" { value = aws_instance.web.public_ip }