Infrastructure as Code
Master Expert Technologie de l'information EPITECH 2020.
Co-fondateur et CTO d'une startup dans l'Edtech 2019 - fin 2022. (+3 ans)
Formation PSPO-1 Agile Scrum 2022.
Co-fondateur et CTO d'une startup dans la Deeptech fin 2022 - aujourd'hui.
Valentin MONTAGNE

1
1. Découverte de Terraform
3
3. Configurer avec Ansible
2
2. Déployer avec Terraform sur un Cloud Provider
Déroulement du cours
Chaque séance débutera par la présentation d'un concept et de l'intérêt d'utilisation de celui-ci.
1
Théorie
Après la théorie, nous verrons alors la pratique en réalisant des exercices sur un repository gitlab.
2
Pratique
Nous verrons ensemble la correction des travaux pratiques. N'hésitez pas à poser vos questions.
3
Correction
Déroulement des journées
Connaissez-vous l'IaC / Terraform ?
Rendu TP et exercices
Pour faciliter le rendu final des TPs et exercices, vous allez créer un dépôt sur Github avec le nom IaC en publique et le cloner sur votre machine.
Si vous préférez le garder en privé, vous devez m'ajouter avec les droits pour voir votre dépôt avec le nom d'utilisateur : ValentinMontagne
Découverte de Terraform
1.
Qu'est-ce que l'Infrastructure as Code ?
Définir toute son infrastructure via des fichiers de configuration.
Automatiser la création, l'édition et la suppression des ressources de l'infrastructure dans le cloud.
Suivre les différentes versions de l'infrastructure en fonction de la solution.



Qu'est-ce que Terraform ?
Automatise le déploiement sur tous les grands Clouds.
Est capable de gérer des grands cluster avec Kubernetes.
S'intègre facilement dans les pipelines CI / CD.



Pourquoi utiliser Terraform ?
Est devenu une référence comme outil d'IaC.
Des fonctionnalités internes pour gérer des situations complexes.
Plusieurs outils CI / CD ont déjà des intégrations.



Configuration as Code vs IaC
CaC et IaC sont deux moyens de gérer les ressources de l'infrastructure, mais ils se concentrent sur des choses différentes :
Le CaC gère la configuration, les logiciels et les paramètres au sein des serveurs, comme les paramètres des utilisateurs et les configurations des applications. Ansible et Puppet sont des exemples d'outils CaC.
Conclusion : alors que l'IaC met en place l'environnement, le CaC s'assure que le logiciel au sein de cet environnement fonctionne correctement.
Alternative à Terraform - OpenTofu
Terraform a été mis en open-source en 2014 sous la Mozilla Public License (v2.0). Puis, le 10 août 2023, avec peu ou pas de préavis, HashiCorp a changé la licence pour Terraform de la MPL à la Business Source License (v1.1), une licence non open source.
OpenTofu est un fork de la version Open source de Terraform et est géré par la fondation Linux. C'est donc une bonne alternative à Terraform aujourd'hui.
La migration à OpenTofu est extrêmement simple car il n'y a pas de différence de fonctionnement avec Terraform.
Découverte de HCL
HCL, ou HashiCorp Configuration Language, est un langage lisible par l'homme pour les outils DevOps. Il est utilisé pour coder la gestion de l'infrastructure et l'orchestration des services de manière claire et gérable.
HCL est conçu pour trouver un équilibre entre un langage de configuration générique comme JSON ou YAML et un langage de script de haut niveau.
Plusieurs produits HashiCorp, dont Terraform, utilisent HCL comme langage de configuration principal. Sa syntaxe et sa structure claires permettent de créer des modules de ressources et des configurations.
La syntaxe de HCL
La syntaxe de base du langage de configuration HCL comprend la définition de blocs, d'attributs et d'expressions.
Les blocs sont des unités fondamentales telles que la ressource, le module et le provider, identifiées par des mots-clés et placées entre accolades.
Les attributs sont des paires clé-valeur à l'intérieur des blocs, où les clés sont des chaînes et les valeurs peuvent être des chaînes, des nombres ou d'autres types de données.
Les expressions permettent d'intégrer des variables, des fonctions et des références à d'autres ressources, ce qui permet des configurations dynamiques.
Exemple de la syntaxe de HCL
resource "aws_instance" "ec2" { # <-- Bloc ressource : type "aws_instance", nom "ec2"
ami = data.aws_ami.amazon_linux_2.id # <-- Expression : référence à une autre ressource
instance_type = var.instance_type # <-- Expression : variable d'entrée
key_name = "staging" # <-- Attribut string
user_data = <<-EOF # <-- Bloc d'attribut multi-lignes (heredoc)
#!/bin/bash
yum update -y
yum install -y awscli jq docker
service docker start
export GITLAB_PASSWORD=$(echo $(aws secretsmanager get-secret-value --secret-id GITLAB_CONTAINER_REGISTRY --query SecretString --output text --region eu-west-3) | jq -r '.password')
echo $(aws secretsmanager get-secret-value --secret-id workspace/staging --query SecretString --output text --region eu-west-3) | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" > .env
docker login registry.gitlab.com -u vm-singularity -p $GITLAB_PASSWORD
docker run --env-file .env -d -p 80:80 registry.gitlab.com/marvelab/workspace:${var.hash}
EOF
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name # <-- Référence à une autre ressource
vpc_security_group_ids = [aws_security_group.ec2.id] # <-- Liste avec référence
# Bloc d'attributs imbriqué pour les tags
tags = {
Name = "${var.namespace}_EC2_${var.environment}" # <-- Interpolation de variables
}
user_data_replace_on_change = true # <-- Attribut booléen
}
Providers
Qu'est-ce qu'un Provider ?
Les Providers sont des plugins qui permettent d'interagir avec diverses API externes. Ils gèrent le cycle de vie des ressources en définissant les types de ressources et les sources de données.
Chaque Provider nécessite une configuration, qui comprend généralement des détails d'authentification et des URLs.
Les Providers sont spécifiés dans le bloc provider, et plusieurs Providers peuvent être utilisés dans un seul projet Terraform pour gérer les ressources sur différentes plateformes.
Terraform Registry
Permet de découvrir, de partager et d'utiliser les modules et Providers Terraform.
Configurer un provider
La configuration est réalisée dans le bloc provider de vos fichiers de configuration Terraform. Ce bloc comprend des paramètres tels que les identifiants d'authentification, la région et d'autres paramètres spécifiques au provider.
Les providers doivent être initialisés à l'aide de terraform init
pour télécharger et installer les plugins nécessaires.
Des configurations multiples peuvent être gérées en créant des alias de provider, ce qui permet de gérer les ressources dans différents environnements ou comptes au sein d'un même provider.
Version d'un provider
La spécification des versions des Providers dans Terraform garantit un comportement cohérent et prévisible dans différents environnements. La version doit être définie dans le bloc required_providers.
Cette approche permet d'éviter les changements inattendus ou les problèmes de compatibilité dus aux mises à jour des providers, améliorant ainsi la stabilité et la fiabilité de la gestion de l'infrastructure.
Exemple de configuration
# Bloc principal de configuration de Terraform
terraform {
# Déclaration des providers nécessaires pour ce projet
required_providers {
aws = {
# Le provider AWS est développé par HashiCorp (source officielle)
source = "hashicorp/aws"
# Spécifie la version du provider AWS compatible
version = "~> 4.16"
}
}
# Version minimale de Terraform requise pour exécuter ce projet
required_version = ">= 1.2.0"
}
# Configuration du provider AWS
# Ce bloc précise comment Terraform va interagir avec AWS
provider "aws" {
# Région AWS cible : ici, eu-west-3 correspond à la région "Paris"
region = "eu-west-3"
# Optionnel : vous pouvez ajouter un profil si vous utilisez ~/.aws/credentials
# profile = "mon-profil"
# Terraform utilisera automatiquement vos identifiants AWS si :
# - vous les avez définis via les variables d’environnement AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY
# - ou vous avez un fichier ~/.aws/credentials correctement configuré
}
Resources
Qu'est-ce qu'une Resource ?
Représente des composants de l'infra comme les VMs, les buckets, les bases de données ou les VPS.
Chaque déclaration génère l'élément côté Provider après execution de Terraform.
Chaque resource est configurable en fonction du Provider.



Comportement d'une Resource
Lorsque Terraform crée un nouvel objet d'infrastructure représenté par un bloc de ressources, l'identifiant de cet objet réel est enregistré dans le State de Terraform, ce qui permet de le mettre à jour et de le détruire en réponse à des changements futurs.
Pour les blocs de ressources qui ont déjà un objet d'infrastructure associé dans le State, Terraform compare la configuration réelle de l'objet avec les arguments donnés dans la configuration et, si nécessaire, met à jour l'objet pour qu'il corresponde à la configuration.
Comportement d'une Resource
L'application d'une configuration Terraform va :
- Créer des ressources qui existent dans la configuration mais qui ne sont pas associées à un objet d'infrastructure réel dans l'état.
- Détruire les ressources qui existent dans l'état mais qui n'existent plus dans la configuration.
- Mettre à jour les ressources en place dont les arguments ont changé.
- Détruire et recréer les ressources dont les arguments ont changé mais qui ne peuvent pas être mises à jour sur place en raison des limitations de l'API distante.
Accès à une Resource et Data sources
Pour accéder à une ressource dans un fichier de configuration, on utilise une Expression qui a la syntaxe suivante : <RESOURCE TYPE>.<NAME>.<ATTRIBUTE>
On peut accéder à une ressource pour ses attributs en lecture seule obtenues à partir de l'API distante ; il s'agit souvent d'éléments qui ne peuvent être connus avant la création de la ressource, comme l'id de la ressource.
De nombreux providers incluent également des Data sources, qui sont un type spécial de ressources utilisées uniquement pour rechercher des informations.
Exemple d'accès et de Data source
## Get most recent AMI for an ECS-optimized Amazon Linux 2 instance
data "aws_ami" "amazon_linux_2" {
most_recent = true
filter {
name = "virtualization-type"
values = ["hvm"]
}
filter {
name = "owner-alias"
values = ["amazon"]
}
filter {
name = "name"
values = ["amzn2-ami-ecs-hvm-*-x86_64-ebs"]
}
owners = ["amazon"]
}
resource "aws_instance" "ec2" {
ami = data.aws_ami.amazon_linux_2.id # <-- Expression d'accès
instance_type = var.instance_type
...
Meta arguments
Les méta-arguments dans les ressources Terraform fournissent un contrôle supplémentaire sur la façon dont les ressources sont gérées et interagissent dans la configuration :
- count
- for_each
- depends_on
- provider
- lifecycle
Count
Permet de spécifier le nombre d'instances d'une ressource particulière à créer. Terraform génère dynamiquement plusieurs instances de la ressource, indexées de 0 à count-1.
Cette fonction est utile pour gérer des infrastructures qui nécessitent plusieurs ressources identiques ou similaires, telles que la création de plusieurs machines virtuelles ou de plusieurs buckets. Vous pouvez créer des ressources de manière conditionnelle en définissant la valeur en fonction de variables ou d'expressions.
Chaque instance de la ressource peut être référencée de manière unique à l'aide de la valeur count.index
.
Depends_on
Déclare explicitement les dépendances entre les ressources, en s'assurant qu'une ou plusieurs ressources sont créées ou détruites seulement après que les ressources dépendantes spécifiées ont été appliquées avec succès.
Ceci est crucial pour gérer les dépendances de ressources qui ne sont pas automatiquement détectées par l'analyse implicite des dépendances de Terraform.
For_each
Permet de créer plusieurs instances d'une ressource en fonction d'un ensemble ou d'une map. Contrairement à count, qui utilise un simple entier, for_each
permet une création de ressources plus granulaire et dynamique, puisque chaque instance est associée à une paire clé-valeur spécifique d'un objet ou d'une map.
Ce méta-argument est particulièrement utile pour créer des ressources avec des configurations uniques dérivées d'une map obtenu par un Data source par exemple.
Provider
Spécifie la configuration du provider à utiliser pour une ressource, en remplaçant la sélection du provider par défaut basée sur le nom du type de ressource.
Ceci est utile dans les scénarios où plusieurs configurations du même provider sont nécessaires, comme la gestion des ressources dans différentes régions ou environnements. En définissant l'argument provider, vous pouvez vous assurer que la ressource utilise la configuration spécifiée du fournisseur, identifiée par son alias.
Lifecycle
Personnalise le comportement des ressources lors de leur création, de leur mise à jour et de leur suppression. Il comprend des paramètres tels que create_before_destroy, qui garantit qu'une nouvelle ressource est créée avant que l'ancienne ne soit détruite, ce qui évite les temps d'arrêt.
prevent_destroy protège les ressources contre les suppressions accidentelles, et ignore_changes spécifie les attributs à ignorer lors des mises à jour, ce qui permet d'apporter des modifications externes sans déclencher de changements dans Terraform.
Variables
Qu'est-ce qu'une Variable ?
Terraform utilise des variables pour rendre les configurations plus flexibles et réutilisables. Les variables peuvent être déclarées dans des fichiers .tf et se voir attribuer des valeurs par différentes méthodes, notamment des valeurs par défaut, des flags de ligne de commande, des variables d'environnement ou des fichiers .tfvars distincts. Elles prennent en charge plusieurs types de données tels que les chaînes de caractères, les nombres, les bools, les listes et les maps. Les variables peuvent être référencées dans toute la configuration à l'aide du préfixe var.<myVariable>
.
Ce système permet à l'infrastructure en tant que code d'être plus dynamique et de s'adapter à différents environnements ou cas d'utilisation.
Input Variable
Les variables d'entrée Terraform sont des paramètres pour les modules, déclarés à l'aide de blocs de variables. Elles prennent en charge plusieurs types de données, des valeurs par défaut et des descriptions. Les utilisateurs fournissent des valeurs lorsqu'ils invoquent des modules ou exécutent Terraform.
Elles peuvent être marquées comme sensibles pour des raisons de sécurité et sont généralement définies dans un fichier variables.tf.
Exemple : Définir lors l'exécution que les instances EC2 seront de type T3.micro.
Contraintes de type
Les contraintes de type de variable Terraform spécifient les types de données autorisés pour les variables d'entrée. Elles incluent les types primitifs (string, number, bool), les types complexes (list, set, map, object), et any pour les types non spécifiés.
Les contraintes peuvent imposer des structures spécifiques, des types imbriqués ou des plages de valeurs. Elles sont définies dans l'argument de type du bloc de variables, ce qui permet de détecter rapidement les erreurs et de garantir une utilisation correcte des variables dans toutes les configurations.
Contraintes de type - Exemple
# Déclaration d'une variable nommée "buckets"
variable "buckets" {
# On impose une contrainte de type stricte à cette variable :
# il s'agit d'une LISTE d'OBJETS, chacun représentant un bucket à créer
type = list(object({
# Chaque objet de la liste doit obligatoirement avoir une clé "name"
# de type chaîne de caractères (string)
name = string
# Clé facultative : "enabled"
# de type booléen, qui indique si le bucket est actif ou non
# Valeur par défaut : true (si non précisée dans l’appel)
enabled = optional(bool, true)
# Clé facultative : "website", qui elle-même est un objet complexe
website = optional(object({
index_document = optional(string, "index.html")
error_document = optional(string, "error.html")
routing_rules = optional(string)
# Valeur par défaut pour l’objet website : objet vide {}
}), {})
}))
}
Local Values
Les valeurs locales peuvent être considérées comme un nom attribué à toute expression afin de pouvoir l'utiliser plusieurs fois directement par le nom dans votre module terraform. Les valeurs locales sont appelées locals et peuvent être déclarées à l'aide du bloc locals. Les valeurs locales peuvent être des constantes littérales, des attributs de ressources, des variables ou d'autres valeurs locales. Les valeurs locales sont utiles pour définir des expressions ou des valeurs que vous devez utiliser plusieurs fois dans le module, car elles permettent d'actualiser facilement la valeur en mettant simplement à jour la valeur locale. On peut accéder à une valeur locale en utilisant l'argument local comme local.<nom_de_la_valeur>
.
Local Values - Exemple
locals {
# Ids for multiple sets of EC2 instances, merged together
instance_ids = concat(aws_instance.blue.*.id, aws_instance.green.*.id)
}
locals {
# Common tags to be assigned to all resources
common_tags = {
Service = local.service_name
Owner = local.owner
}
}
# Utilisation :
resource "aws_instance" "blue" {
# ...
tags = local.common_tags
}
Variables d'environnement
Les variables d'environnement peuvent être utilisées pour modifier le comportement par défaut de terraform, par exemple augmenter la verbosité, mettre à jour le chemin du fichier journal, définir l'espace de travail, etc.
TF_VAR_name permet de donner une valeur à certaines variables comme par exemple :
export TF_VAR_region=us-west-1
export TF_VAR_ami=ami-049d8641
export TF_VAR_alist='[1,2,3]'
export TF_VAR_amap='{ foo = "bar", baz = "qux" }'
Validation Rules
Les règles de validation peuvent être utilisées pour spécifier des validations personnalisées pour une variable. L'ajout de règles de validation a pour but de rendre la variable conforme aux règles. Les règles de validation peuvent être ajoutées à l'aide d'un bloc de validation dans un block variable.
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}
Outputs
Qu'est-ce qu'un Output ?
Un output expose les valeurs sélectionnées d'une configuration ou d'un module, les rendant accessibles aux utilisateurs ou à d'autres modules.
Définies dans des blocs de sortie, généralement dans un fichier outputs.tf, ils peuvent faire référence à des attributs de ressources ou à d'autres valeurs calculées.
Les sorties sont affichées après les opérations d'application, peuvent être interrogées à l'aide de commande Terraform et sont essentielles pour transmettre des informations entre les modules ou à des systèmes externes.
Syntaxe d'un Output
# Déclaration d'une sortie (output) dans Terraform
# Les outputs permettent d'exposer certaines valeurs après l'exécution.
output "name" {
# La valeur que Terraform affichera en sortie
# Il peut s’agir d’un nom de ressource, d’un attribut, d’une variable, etc.
# Exemple concret : aws_s3_bucket.mon_bucket.bucket
value = expression
# (Facultatif) Une description textuelle de la sortie
# Utile pour la documentation et la compréhension dans les grands projets
description = "Optional description"
# (Facultatif) Marque cette sortie comme sensible
# Cela empêche Terraform d'afficher la valeur dans la console ou les logs
# Exemple d'utilisation : mots de passe, clés d’API, tokens
sensitive = bool
}
Sensitive Output
L'attribut sensitive est une fonctionnalité utilisée pour protéger les informations sensibles dans les configurations Terraform. Lorsqu'une sortie est marquée comme sensible, Terraform obscurcit sa valeur dans la sortie de la console, en l'affichant sous la forme <sensitive> au lieu de la valeur réelle.
Cette fonction est essentielle pour protéger les données sensibles telles que les mots de passe ou les clés d'API.
Le CLI Terraform
Les commandes principales
Terraform possède un CLI permettant d'exécuter des commandes.
Voici les commandes principales à connaître pour vérifier, planifier, lancer et détruire votre infrastructure :
terraform fmt
terraform validate
terraform plan
terraform apply
terraform destroy
Format
Formate automatiquement les fichiers de configuration dans un style cohérent. Elle ajuste l'indentation, aligne les arguments et trie les blocs et les arguments par ordre alphabétique. La commande réécrit les fichiers de configuration Terraform (.tf et .tfvars) dans le répertoire courant et ses sous-répertoires.
Elle est utilisée pour maintenir un style cohérent entre les projets et les équipes, améliorant la lisibilité et réduisant les conflits de fusion.
Bonne pratique : utiliser un git hook pour automatiser le lancement de la commande avant chaque commit.
Validate
Permet de vous assurer que votre code Terraform est syntaxiquement correct avant de le déployer.
Vous évitez ainsi les erreurs de configuration dues à des attributs manquants ou à des dépendances incorrectes, ce qui vous permet de gagner du temps, d'améliorer l'efficacité et de réduire les coûts.
Bonne pratique : utiliser un git hook pour automatiser le lancement de la commande avant chaque commit.
Plan
Crée un plan d'exécution, montrant les changements que Terraform va apporter à votre infrastructure.
Il compare l'état actuel avec l'état souhaité défini dans les fichiers de configuration et produit une liste détaillée des ressources à créer, modifier ou supprimer. Il est important de noter qu'il n'apporte aucun changement réel à l'infrastructure, mais qu'il aide à identifier les problèmes potentiels avant d'appliquer les changements. Le plan peut être enregistré dans un fichier en vue d'une exécution ou d'une révision ultérieure.
Bonne pratique : Lancer la commande dans un job de votre CI pour valider les modifications d'infrastructure proposées.
Apply
Mets en œuvre les changements définis dans vos fichiers de configuration Terraform. Elle crée, met à jour ou supprime les ressources d'infrastructure spécifiées afin qu'elles correspondent à l'état souhaité.
Avant d'effectuer les changements, elle affiche un plan similaire à terraform plan
et demande une confirmation, sauf si l'option -auto-approve est utilisée.
Apply met à jour le fichier d'état pour refléter l'état actuel de l'infrastructure, ce qui permet à Terraform de suivre et de gérer les ressources au fil du temps. Il gère les dépendances entre les ressources, en les créant dans le bon ordre.
Destroy
Supprime toutes les ressources gérées par une configuration Terraform. Elle crée un plan de suppression de toutes les ressources et demande une confirmation avant l'exécution. Cette commande est utile pour nettoyer des environnements temporaires ou mettre hors service des infrastructures entières.
Elle supprime les ressources dans l'ordre inverse de leurs dépendances pour garantir un démantèlement correct.
Après la destruction, Terraform met à jour le fichier state pour refléter les changements, mais il est important de gérer ou de supprimer ce fichier si le projet est complètement déclassé.
Qu'est-ce qu'un State ?
Permet de suivre l'état actuel de votre infrastructure gérée. Il est généralement stocké dans un fichier nommé terraform.tfstate, qui associe les ressources du monde réel à votre configuration.
Cet état permet à Terraform de déterminer quels changements sont nécessaires pour obtenir la configuration souhaitée.
Il contient des informations sensibles et doit être stocké de manière sécurisée, souvent dans des backends distants comme S3 ou Terraform Cloud.
Les bonnes pratiques
- Stockez les fichiers d'état à distance dans des backends cryptés et contrôlés par version, tels que S3 ou Terraform Cloud, pour permettre l'accès de l'équipe et renforcer la sécurité.
- Mettez en œuvre le verrouillage des états pour empêcher les modifications simultanées. Utiliser des espaces de travail ou des fichiers d'état distincts pour différents environnements.
- Sauvegardez régulièrement les fichiers d'état et activez le contrôle de version pour les capacités de retour en arrière.
- Évitez de stocker des données sensibles directement dans l'état ; utilisez plutôt des outils de gestion des secrets.
- Conservez les fichiers d'état séparément de votre configuration Terraform dans le contrôle de version.
Intégration à une CI / CD
Terraform est un très bon outil pour réaliser du CI / CD dans un projet. Il permet d'automatiser le déploiement de l'infrastructure à chaque nouvelle version de votre application.
Pour cela, je vous propose de consulter un exemple simple d'un déploiement d'une image Docker sur AWS via Github Actions :
Et la slide suivante pour voir le job permettant le déploiement.
Déployer l'image Docker avec Terraform sur un Cloud Provider
deploy-image:
runs-on: ubuntu-latest
needs: build-image
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: eu-west-3
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Terraform Init
run: terraform init
- name: Terraform Format
run: terraform fmt -check
- name: Terraform Plan
run: terraform plan -input=false
- name: Terraform Apply
run: terraform apply -auto-approve -input=false
# Add condition here to avoid to deploy when checking a PR.
TP - Prise en main de Terraform en local
Installation de Terraform
En premier lieu, nous allons maintenant installer Terraform sur votre machine : https://developer.hashicorp.com/terraform/install
Vérifiez que vous Terraform est bien installé avec la commande suivante : terraform -v
Ensuite, créez le dossier "TP-local" et créez à l'intérieur le "main.tf" qui sera le fichier principal de votre infrastructure :
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.0"
}
}
}
provider "local" {}
resource "local_file" "test_file" {
content = "Hello depuis Terraform !"
filename = "${path.module}/hello.txt"
}
Initialisation du projet
Pour l'instant, nous allons utiliser le provider "local" qui permet de réaliser des changements en local sur votre machine.
Dans le fichier main.tf, la resource local_file permet de générer un nouveau fichier.
Initialisez le projet avec la commande :
terraform init
Lancez les commandes suivantes :
terraform fmt
terraform validate
terraform plan
terraform apply
Vérifiez que les changements, que remarquez-vous ?
Modification du projet
Nous allons maintenant modifier notre "infrastructure", changer le contenu de votre fichier hello.txt qui a pour nom "test_file" dans votre configuration.
Relancez les différentes commandes précédentes.
Que remarquez-vous ?
Utilisation de commande
Nous allons maintenant ajouter une commande qui va s'exécuter automatiquement.
Ajoutez dans votre main.tf le code suivant :
Ajoutez les dépendances nécessaire avec la commande :
terraform init -upgrade
Exécutez à nouveau la configuration, qu'observez-vous ?
resource "null_resource" "example" {
provisioner "local-exec" {
command = "echo 'Commande exécutée !'"
}
}
Détruire l'infrastructure
Maintenant que nous avons terminé avec cette configuration, vous allez pouvoir la détruire, pour cela exécutez la commande :
terraform destroy
Qu'observez-vous ?
Oui, le fichier terraform.tfstate est toujours présent avec sa sauvegarde, nous allons les supprimer car nous n'allons plus utiliser cette configuration.
Utilisation de Docker
Nous allons maintenant créer une infrastructure en utilisant Docker pour simuler ce que l'on va pouvoir réaliser avec un Cloud Provider. Remplacez le contenu de main.tf par celui-ci :
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "3.5.0"
}
}
}
provider "docker" {}
resource "docker_image" "nginx" {
name = "nginx:latest"
keep_locally = true
}
resource "docker_container" "nginx" {
name = "nginx-terraform"
image = docker_image.nginx.image_id
ports {
internal = 80
external = 8080
}
}
Utilisation de Docker
Attention, pour MacOS et Windows, cela peut ne pas fonctionner sans plus de configuration.
Pour Windows, vous devez allez via General > Expose Daemon on tcp://localhost:2375 et ajouter dans le block provider "docker" la ligne :
host = "tcp://localhost:2375".
Pour MacOS, vous devez mettre le bon chemin :
host = "unix://${pathexpand("~/.docker/run/docker.sock")}"
Exécutez cette configuration.
Que remarquez-vous ?
Appelez-moi pour que valider ensemble.
Clean up
Bravo, vous avez terminé ce premier TP !
Je vous invite maintenant à supprimer l'infrastructure actuelle et de passer aux exercices.
Exercice 1 : Variables
Ajoutez des variables Terraform à la place des valeurs codées en dur suivantes :
-
Le nom de l’image Docker (
nginx:latest
) -
Le nom du conteneur
-
Le port externe exposé
-
Le port interne du conteneur
Vous devez :
-
Déclarer chaque variable dans le fichier
variables.tf
-
Fournir des valeurs par défaut
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 2 : Output
Ajoutez un output nommé nginx_container_id
à votre configuration Terraform.
Celui-ci doit afficher l’identifiant (id
) du conteneur nginx
créé avec la ressource docker_container.nginx
.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 3 : Créer une commande
Ajoutez une commande à votre configuration Terraform qui permet de tester automatiquement que le serveur Nginx déployé sur le port spécifié répond avec une page d’index.
La commande doit tester l’URL :
http://localhost:<external_port>
Et vérifier que la réponse contient le mot Welcome
(présent dans l’index par défaut de Nginx).
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 4 : Créer un autre container Docker
Ajoutez un deuxième conteneur Docker nommé client
, basé sur l’image appropriate/curl
, qui exécutera une commande pour appeler le serveur nginx (déjà déployé).
Pour cela, vous devez :
-
Créer un réseau Docker dédié.
-
Connecter les deux conteneurs à ce réseau.
-
Faire en sorte que le conteneur
client
utilisecurl
pour interrogerhttp://nginx:80 et sleep
ensuite. -
Vérifier que la communication fonctionne dans le conteneur
client
.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 5 : Utilisation de count
Reprenez l'exercice précédent, puis :
-
Modifiez le conteneur
client
pour qu’il soit déployé en plusieurs exemplaires (ex. 3) à l’aide decount
. -
Assurez-vous que chaque conteneur :
-
ait un nom unique (
client-0
,client-1
, etc.), -
soit connecté au même réseau Docker que
nginx
, -
exécute un
curl http://nginx
suivi d’unsleep
de 30 secondes.
-
-
Le nombre de clients doit être paramétrable via une variable.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 6 : Utilisation de for_each
Reprenez l'exercice précédent, transformez le count
par un for_each
qui doit boucler sur une liste de nom pour vos serveurs.
Chaque serveur doit posséder le bon nom :
server-<nom>
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 7 : Utilisation des types de variables
Vous êtes en train de créer un module Terraform qui déploie des machines virtuelles. Chaque machine doit être définie avec : un nom, un nombre de vCPU (min. 2, max. 64), une taille de disque (en Go, min. 20), une région (parmi : "eu-west-1"
, "us-east-1"
, "ap-southeast-1"
).
Créez une variable machines
de type liste d'objets contenant les 4 attributs cités.
Ajoutez une validation personnalisée sur : les vcpu
(entre 2 et 64),
le disk_size
(>= 20), la region
.
Appelez-moi pour que l'on puisse valider ensemble.
Déployer avec Terraform sur un Cloud Provider
2.
Configurer le Provider AWS
Pour utiliser un Provider comme AWS, il faut le configurer pour qu'il puisse se connecter à notre compte AWS.
Pour cela, la documentation du Provider indique plusieurs manières de faire :
- Paramètres dans la configuration du fournisseur
- Variables d'environnement
- Fichiers d'identification partagés
- Fichiers de configuration partagés
- Informations d'identification du conteneur
- Informations d'identification du profil d'instance et région
Je recommande d'utiliser la méthode 2 qui est pour moi plus sécurisé.
Utiliser les variables d'environnement
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = "us-east-1"
}
# Create a VPC
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
}
# terminal
% export AWS_ACCESS_KEY_ID="anaccesskey"
% export AWS_SECRET_ACCESS_KEY="asecretkey"
% terraform plan
Exemple de configuration :
Les ressources avec AWS
Le Provider AWS possède plus de 1499 ressources, c'est pour cela que nous n'allons voir que les plus simples durant cet atelier.
Débutons par la ressource de base, l'instance AWS qui est une instance EC2 :
Lien vers la documentation complète
(Le panel de gauche permet de rechercher à l'intérieur)
Un exemple est disponible à la slide suivante.
Instance EC2 - Exemple
resource "aws_instance" "ec2" {
# ID de l'AMI Amazon Linux 2, récupéré dynamiquement via un data source
ami = data.aws_ami.amazon_linux_2.id
# Type d'instance défini via une variable (ex: t2.micro, t3.medium, etc.)
instance_type = var.instance_type
# Nom de la clé SSH permettant d'accéder à l'instance
key_name = "staging"
# Script shell exécuté au démarrage de l'instance (cloud-init)
user_data = <<-EOF
#!/bin/bash
yum update -y # Mise à jour des paquets
yum install -y awscli jq docker # Installation de l’AWS CLI, jq (JSON parser) et Docker
service docker start # Démarrage du service Docker
EOF
# Profil IAM attaché à l’instance pour lui donner des permissions AWS (ex: accès à S3)
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
# Liste des IDs de groupes de sécurité associés à l’instance
vpc_security_group_ids = [aws_security_group.ec2.id]
# Étiquettes (tags) appliquées à l’instance pour l'organisation et le suivi
tags = {
Name = "${var.namespace}_EC2_${var.environment}" # Exemple : "dev_EC2_staging"
}
# Forcer le remplacement de l'instance si le user_data change
user_data_replace_on_change = true
}
Les clés SSH avec AWS
Pour nous connecter à nos instances, nous allons avoir besoin de clés SSH. Voici comment générer et configurer une paire de clé :
ssh-keygen -t rsa -b 4096 -f ~/.ssh/my-ec2-key
Ne jamais partager la clé privée.
Exemple de configuration avec AWS :
provider "aws" {
region = "eu-west-3"
}
resource "aws_key_pair" "my_key" {
key_name = "my-ec2-key"
public_key = file("~/.ssh/my-ec2-key.pub")
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0" # à adapter à la région
instance_type = "t2.micro"
key_name = aws_key_pair.my_key.key_name
tags = {
Name = "Terraform-Instance"
}
}
TP - Déployer en local avec AWS
Installation de Localstack
Pour éviter d'utiliser directement un vrai compte AWS qui pourrait être coûteux ou laborieux quand vous devez réaliser des tests, nous allons utiliser un outil très puissant : Localstack.
Installez Localstack en suivant les instructions dans le Readme du projet.
Vérifiez l'installation de Localstack avec la commande :
localstack -v
Puis démarrez l'environnement local :
localstack start
Nous allons maintenant installer le CLI d'AWS.
Installation de AWS CLI
Pour MacOS :
brew install awscli
Pour Windows :
https://awscli.amazonaws.com/AWSCLIV2.msi
Pour Linux :
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
Vérifiez la bonne installation du CLI :
aws --version
Configurer le provider AWS
Nous allons devoir ajouter des configurations non nécessaire normalement vu que nous allons être sur un faux environnement d'AWS dans le main.tf :
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
access_key = "test"
secret_key = "test"
region = "us-east-1"
# LocalStack endpoint configuration
s3_use_path_style = true
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
s3 = "http://localhost:4566"
ec2 = "http://localhost:4566"
}
}
Créer un bucket
Nous allons créer une ressource emblématique d'AWS, un bucket S3 pour pouvoir stocker des fichiers.
Pour cela, nous allons créer le fichier s3.tf :
# Create an S3 bucket
resource "aws_s3_bucket" "demo_bucket" {
bucket = "my-bucket"
}
# Enable versioning for the bucket
resource "aws_s3_bucket_versioning" "demo_bucket_versioning" {
bucket = aws_s3_bucket.demo_bucket.id
versioning_configuration {
status = "Enabled"
}
}
# Upload a file to the bucket
resource "aws_s3_object" "demo_object" {
bucket = aws_s3_bucket.demo_bucket.id
key = "hello-world.txt"
source = "./test-file.txt"
etag = filemd5("./test-file.txt")
}
Créer un bucket - 2
Dans la slide précédente, on peut voir que l'on upload un fichier sur notre Bucket, nous allons donc créer le fichier test-file.txt :
Lancez l'initialisation du projet Terraform, planifiez et appliquez les changements. Vérifiez vos changements avec les commandes suivantes pour Linux et MacOS :
AWS_ACCESS_KEY_ID="test" AWS_SECRET_ACCESS_KEY="test" AWS_DEFAULT_REGION="us-east-1" aws --endpoint-url=http://localhost:4566 s3 ls
AWS_ACCESS_KEY_ID="test" AWS_SECRET_ACCESS_KEY="test" AWS_DEFAULT_REGION="us-east-1" aws --endpoint-url=http://localhost:4566 s3 ls s3://my-bucket
Hello, World! This is a test file for my Terraform and LocalStack demo.
Créer un bucket - 3
Pour Windows :
& { $env:AWS_ACCESS_KEY_ID = "test"; $env:AWS_SECRET_ACCESS_KEY = "test"; $env:AWS_DEFAULT_REGION = "us-east-1"; aws --endpoint-url=http://localhost:4566 s3 ls }
& { $env:AWS_ACCESS_KEY_ID = "test"; $env:AWS_SECRET_ACCESS_KEY = "test"; $env:AWS_DEFAULT_REGION = "us-east-1"; aws --endpoint-url=http://localhost:4566 s3 ls s3://my-bucket }
Créer une instance EC2
Maintenant que notre bucket est fonctionnel, nous allons créer une instance EC2 qui lancera un Nginx, créez le fichier ec2.tf :
# Generate SSH key
resource "tls_private_key" "key" {
algorithm = "RSA"
rsa_bits = 4096
}
# Create key pair
resource "aws_key_pair" "deployer" {
key_name = "deployer-key"
public_key = tls_private_key.key.public_key_openssh
}
# Store private key locally
resource "local_file" "private_key" {
content = tls_private_key.key.private_key_pem
filename = "${path.module}/deployer-key.pem"
file_permission = "0600"
}
# Create EC2 instance with Nginx
resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type = "t2.micro"
security_groups = [aws_security_group.web.name]
key_name = aws_key_pair.deployer.key_name
user_data = <<-EOF
#!/bin/bash
# Install and configure Nginx
yum update -y
amazon-linux-extras install -y nginx1
systemctl start nginx
systemctl enable nginx
# Create a simple webpage
echo "<h1>Hello from Terraform and LocalStack!</h1>" > /usr/share/nginx/html/index.html
EOF
tags = {
Name = "nginx-server"
}
}
Créer un groupe de sécurité
Avec AWS, la connexion à une instance doit être géré par des ressources réseau comme un groupe de sécurité, créez sg.tf :
# Create a security group for the EC2 instance
resource "aws_security_group" "web" {
name = "nginx-sg"
description = "Allow web and SSH traffic"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTP traffic"
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow SSH traffic"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = {
Name = "nginx-sg"
}
}
Créer les Outputs
Pour vérifier que notre instance EC2 fonctionne correctement, nous allons récupérer des informations, malheureusement localstack ne va pas jusqu'à simuler une vraie VM capable d'exécuter nginx, nous allons juste vérifier si elle est bien lancé.
Créez outputs.tf :
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
output "instance_public_ip" {
description = "Public IP of the EC2 instance"
value = aws_instance.web.public_ip
}
output "ssh_command" {
description = "SSH command to connect to the instance"
value = "ssh -i deployer-key.pem ec2-user@${aws_instance.web.public_ip}"
}
Modification et vérification
Lancez l'application de la nouvelle configuration et vérifiez que l'instance EC2 est bien présente :
AWS_ACCESS_KEY_ID="test" AWS_SECRET_ACCESS_KEY="test" AWS_DEFAULT_REGION="us-east-1" aws --endpoint-url=http://localhost:4566 ec2 describe-instances --instance-ids <Instance ID>
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 1 - Variables
Ajoutez des variables Terraform à la place des valeurs codées en dur suivantes :
-
Le type d'instance EC2
-
Le nom de l'instance EC2
-
Le nom du bucket S3
-
Le port par défaut pour le groupe de sécurité
Vous devez :
-
Déclarer chaque variable dans le fichier
variables.tf
-
Fournir des valeurs par défaut
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 2 - Output
Ajoutez un output nommé bucket_id
à votre configuration Terraform.
Celui-ci doit afficher l’identifiant (id
) du bucket créé avec la ressource aws_s3_bucket.demo_bucket
.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 3 - Création d'une nouvelle instance EC2
Grâce aux exercices précédents, une application web statique est déployée sur une instance EC2 avec Nginx. Nous allons maintenant déployer une nouvelle instance EC2 qui contiendra une base de donnée.
Vous devez :
-
Créer une nouvelle instance EC2
-
Changer la configuration pour qu'on puisse facilement l'identifier comme le serveur de la base de donnée.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 4 - Déployer sur AWS
Maintenant que vous avez validé plusieurs configurations grâce à Localstack, le but est de mettre en place sur un vrai compte AWS la dernière configuration et de vérifier son bon fonctionnement.
Arrêtez et retirer la configuration en lien avec Localstack et les buckets et déployez directement sur le compte AWS fournit pour l'atelier. (N'hésitez pas à consulter AWS details)
Créez une paire de clé et liez les à votre instance EC2 pour vous permettre de vous connecter via SSH à l'instance.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 5 - Déployer un bucket
Ajoutez un S3 bucket à votre infrastructure.
Créez un fichier en local "test-file.txt" avec comme contenu "Hello World" et ajoutez dans la configuration l'upload du fichier sur le bucket S3.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 6 Bonus - Rendre le bucket public
Maintenant que vous avez un bucket S3, ajoutez les permissions pour permettre la lecture de tous les fichiers à l'intérieur par n'importe qui.
On doit pouvoir accéder à l'objet S3 via son URL.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 7 Bonus - Déploiement d'une application
Récupérez un projet que vous avez déjà réalisé et réalisez une image Docker de ce projet.
Déployez l'image Docker et les images nécessaires pour votre projet en utilisant Terraform sur le compte AWS fournit pour cet atelier.
Appelez-moi pour que l'on puisse valider ensemble.
Configurer avec Ansible
3.
Qu'est-ce qu'Ansible ?
Logiciel open-source pour automatiser le déploiement, la configuration et la gestion d’infrastructures.
Contrairement à d’autres outils, n’utilise pas d'agent mais SSH pour se connecter et exécuter les tâches.
Les tâches à exécuter sont décrites de manière déclarative dans des fichiers au format YAML.



Pourquoi utiliser Ansible avec Terraform ?
-
Complémentarité entre provisionnement et configuration : Terraform est idéal pour créer l’infrastructure, tandis qu’Ansible excelle dans la configuration des machines (installation de paquets, déploiement d’applications).
-
Séparation des responsabilités : Utiliser Terraform pour gérer l’état de l’infrastructure et Ansible pour appliquer des configurations cela sépare "ce qui est" (l’infra) de "ce qu’elle fait" (les services qu’elle héberge).
-
Flexibilité post-provisionnement : Ansible peut être relancé facilement et indépendamment pour appliquer des mises à jour, corriger une configuration ou réagir à un changement, sans toucher à l’infrastructure sous-jacente.
Les inventaires
L’inventaire (ou inventory) est un fichier ou une source dynamique qui liste tous les hôtes (machines) que Ansible peut gérer.
Il permet aussi de regrouper ces hôtes, de leur affecter des variables, et de cibler précisément les machines sur lesquelles exécuter des tâches.
Par défaut, l’inventaire est un fichier texte situé dans /etc/ansible/hosts
ou défini via -i
dans la ligne de commande.
Format d'un inventaire
[webservers]
web1.example.com ansible_host=192.168.1.100
web2.example.com ansible_host=192.168.1.101
[dbservers]
db1.example.com ansible_host=192.168.1.200 mysql_port=3306
db2.example.com ansible_host=192.168.1.201 mysql_port=3306
# Production Environment Group using another group
[production:children]
webservers
dbservers
# Specific Server Variables
[webservers:vars]
http_port=80
https_port=443
# Group with Connection Variables
[remote_servers]
remote1.example.com ansible_host=203.0.113.10 ansible_user=admin ansible_ssh_private_key_file=/path/to/private/key
# Global SSH Connection Defaults
[all:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_become=yes
ansible_become_method=sudo
Les Playbooks
Un Playbook est un fichier YAML qui décrit une ou plusieurs séquences de tâches à exécuter sur un ou plusieurs hôtes.
Il permet d’automatiser des configurations, des déploiements, ou de l’orchestration de services de manière déclarative.
On parle de scénario, contenant des tasks, exécutées sur des hosts.
Le format d'un Playbook
---
- hosts: webservers # <-- Means the tasks will run on all servers in the webservers group
tasks:
- name: Ensure nginx is installed
apt: # <-- Use APT to install packages on Debian / Ubuntu
name: nginx
state: present
- name: Configure web server
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- Restart Nginx # <-- Call handler Restart Nginx
handlers:
- name: Restart Nginx
service: # <-- Use Debian / Ubuntu's service system
name: nginx
state: restarted
Exemple d'un Playbook
---
- hosts: webservers
become: yes # Use sudo for elevated privileges
vars:
# Centralized variables for easy management
web_package: nginx
web_port: 80
application_name: myapp
app_root_directory: "/var/www/{{ application_name }}"
# Pre-tasks run before roles and tasks
pre_tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600 # Only update if older than 1 hour
when: ansible_os_family == 'Debian'
# Roles allow for modular, reusable configuration
roles:
- role: security
tags:
- security
- role: webserver
tags:
- web
# Individual tasks for specific configurations
tasks:
- name: Ensure web server is installed
apt:
name: "{{ web_package }}"
state: present
tags:
- packages
- name: Create application directory
file:
path: "{{ app_root_directory }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
tags:
- app-setup
- name: Deploy application code
git:
repo: 'https://github.com/yourorg/yourapp.git'
dest: "{{ app_root_directory }}"
version: main # Specify branch/tag
notify:
- Restart Nginx
tags:
- deployment
- name: Configure Nginx virtual host
template:
src: templates/nginx-site.conf.j2
dest: "/etc/nginx/sites-available/{{ application_name }}"
notify:
- Validate Nginx Configuration
- Restart Nginx
tags:
- configuration
- name: Enable Nginx site
file:
src: "/etc/nginx/sites-available/{{ application_name }}"
dest: "/etc/nginx/sites-enabled/{{ application_name }}"
state: link
notify:
- Restart Nginx
tags:
- configuration
- name: Set up application environment
copy:
content: |
DB_HOST={{ database_host }}
DB_USER={{ database_user }}
SECRET_KEY={{ secret_key }}
dest: "{{ app_root_directory }}/.env"
owner: www-data
group: www-data
mode: '0600'
no_log: true # Prevents logging sensitive information
tags:
- secrets
# Handlers for service management
handlers:
- name: Validate Nginx Configuration
command: nginx -t
register: nginx_test
failed_when: nginx_test.rc != 0
changed_when: false
- name: Restart Nginx
service:
name: nginx
state: restarted
# Post-tasks run after roles and tasks
post_tasks:
- name: Verify application is responding
uri:
url: "http://localhost:{{ web_port }}"
status_code: 200
register: webapp_response
retries: 3
delay: 5
until: webapp_response.status == 200
tags:
- validation
# Example of a separate play for database servers
- hosts: dbservers
become: yes
roles:
- role: database
tags:
- database
# Conditional play for monitoring servers
- hosts: monitoring
become: yes
tasks:
- name: Install monitoring tools
apt:
name:
- prometheus
- grafana
state: present
when: monitoring_enabled | default(false)
TP - Configurer des serveurs
Installation de Ansible et multipass
Installez Ansible avec la commande suivante :
pipx install --include-deps ansible
Puis, installez Multipass, qui nous permettra de simuler des serveurs.
Vérifiez que tout est bien installé :
multipass -v
ansible -v
Créer les serveurs
Créez les serveurs nécessaires pour la suite du TPs :
Ensuite, générez la paire de clé SSH qui permettra à Ansible de se connecter à nos serveurs :
multipass launch -n web-server && multipass launch -n db-server
# Generate SSH key if you don't have one
ssh-keygen -t rsa -b 4096 -f ./ansible_test_key
# Add SSH key to instances
multipass exec web-server -- mkdir -p /home/ubuntu/.ssh
multipass exec db-server -- mkdir -p /home/ubuntu/.ssh
# Copy public key to instances
multipass exec web-server -- sh -c "echo '$(cat ./ansible_test_key.pub)' >> /home/ubuntu/.ssh/authorized_keys"
multipass exec db-server -- sh -c "echo '$(cat ./ansible_test_key.pub)' >> /home/ubuntu/.ssh/authorized_keys"
Créer l'inventaire
Créez ensuite le fichier inventory
avec les informations des serveurs :
[webservers]
web-server ansible_host=<web-server IP> # <-- multipass info web-server | grep IPv4
[dbservers]
db-server ansible_host=<db-server IP> # <-- multipass info db-server | grep IPv4
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=./ansible_test_key
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
Créer le Playbook
Créez ensuite le fichier playbook.yml
avec les tâches à réaliser pour Ansible :
---
- hosts: all
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install basic packages
apt:
name:
- htop
- vim
- curl
state: present
- hosts: webservers
become: yes
tasks:
- name: Install nginx
apt:
name: nginx
state: present
- hosts: dbservers
become: yes
tasks:
- name: Install MySQL
apt:
name:
- mysql-server
- python3-pymysql
state: present
Appliquer la configuration
Vérifiez que Nginx et Mysql ne sont pas déjà présent sur les serveurs :
Vous devriez avoir une erreur pour les deux commandes.
Maintenant, lancez Ansible pour configurer les serveurs :
Réessayez les commandes pour vérifier que Nginx et Mysql sont bien présents.
Appelez-moi pour que l'on puisse valider ensemble.
# Run the playbook
ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory playbook.yml
# Connectez-vous à web-server pour lancer la commande dans son terminal
curl localhost
# Connectez-vous à db-server pour lancer la commande dans son terminal
mysql -v
Exercice 1 - Configurer Docker
Ajoutez dans le Playbook Ansible tout ce qui est nécessaire pour ajouter au web-server le service Docker.
Retirez Nginx et lancez l'image traefik/whoami via Ansible.
Vérifiez que votre web-server affiche quelque chose sur le port 80.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 2 - Ansible et Terraform
Déployez avec Terraform, 3 instances EC2 et créez automatiquement un inventory avec toutes les informations nécessaires pour que Ansible puisse configurer ces instances.
(IP, Clés SSH, etc)
Appliquez sur les 3 instances une configuration via un Playbook qui installe et lance sur toutes les instances Nginx.
Vérifiez sur vos instances EC2 que Nginx réagit au port 80.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 3 Bonus - AWS et Docker
Déployez avec Terraform une instance EC2 "webserver" et une instance EC2 "dbserver" et créez automatiquement un inventory avec toutes les informations nécessaires pour que Ansible puisse configurer ces instances.
(IP, Clés SSH, etc)
Appliquez sur l'instance "webserver" une configuration via un Playbook qui installe et lance l'image Docker Nginx et sur l'instance "dbserver" une base de donnée MySQL.
Vérifiez sur vos instances EC2 que Nginx et MySQL soient bien présents.
Appelez-moi pour que l'on puisse valider ensemble.
Exercice 4 Bonus - Déployer et configurer une application
Déployez avec Terraform une infrastructure pour mettre en ligne une application que vous avez déjà réalisé précédemment.
Créez une image Docker de votre projet pour pouvoir ensuite utiliser Ansible pour configurer l'ajout et l'utilisation de l'image Docker et d'autres images nécessaires pour votre projet sur vos instances déployées par Terraform.
Appelez-moi pour que l'on puisse valider ensemble.
4.
N'hésitez pas à me donner votre avis.
Merci!
Infrastructure as Code
By Valentin MONTAGNE
Infrastructure as Code
- 87