Dans mon article précédent, nous avons découvert Terraform avec Docker en local. Aujourd'hui, nous passons à l'étape suivante : déployer une vraie infrastructure cloud sur Scaleway.
Pourquoi Scaleway ?
Scaleway est un cloud provider français qui offre plusieurs avantages :
- Prix compétitifs : Une instance DEV1-S (2 vCPU, 2GB RAM) coûte environ 10€/mois
- Datacenter en France : Latence faible et conformité RGPD
- API simple : Provider Terraform bien maintenu
Ce qu'on va construire
À la fin de cet article, tu auras déployé :
- Un projet Scaleway pour isoler tes ressources
- Une clé SSH générée automatiquement par Terraform
- Un Security Group avec règles firewall
- Une instance DEV1-S avec Ubuntu 22.04
Prérequis
- Terraform installé (voir mon article précédent)
- Un compte Scaleway (console.scaleway.com)
- Tes credentials API Scaleway (Access Key + Secret Key)
Pour créer tes credentials, rends-toi dans la console Scaleway : IAM > API Keys > Generate API Key.
Architecture des fichiers
1infra/
2├── main.tf # Ressources principales
3├── variables.tf # Définition des variables
4├── outputs.tf # Valeurs de sortie
5├── versions.tf # Configuration Terraform et providers
6└── terraform.tfvars # Valeurs des variables (non committé)Configuration du provider
versions.tf
Commençons par configurer Terraform et le provider Scaleway :
1terraform {
2 required_version = ">= 1.0"
3
4 required_providers {
5 scaleway = {
6 source = "scaleway/scaleway"
7 version = "~> 2.0"
8 }
9 tls = {
10 source = "hashicorp/tls"
11 version = "~> 4.0"
12 }
13 local = {
14 source = "hashicorp/local"
15 version = "~> 2.0"
16 }
17 http = {
18 source = "hashicorp/http"
19 version = "~> 3.0"
20 }
21 }
22}Nous utilisons quatre providers :
scaleway/scalewaypour les ressources cloudhashicorp/tlspour générer la clé SSHhashicorp/localpour sauvegarder les clés SSH localementhashicorp/httppour récupérer notre IP publique
variables.tf
Définissons les variables nécessaires :
1# Credentials Scaleway
2variable "scw_access_key" {
3 description = "Scaleway Access Key"
4 type = string
5 sensitive = true
6}
7
8variable "scw_secret_key" {
9 description = "Scaleway Secret Key"
10 type = string
11 sensitive = true
12}
13
14variable "scw_organization_id" {
15 description = "Scaleway Organization ID"
16 type = string
17}
18
19# Configuration
20variable "project_name" {
21 description = "Nom du projet Scaleway"
22 type = string
23 default = "mon-projet"
24}
25
26variable "region" {
27 description = "Scaleway region"
28 type = string
29 default = "fr-par"
30}
31
32variable "zone" {
33 description = "Scaleway zone"
34 type = string
35 default = "fr-par-1"
36}
37
38variable "instance_type" {
39 description = "Type d'instance (DEV1-S, DEV1-M, GP1-XS...)"
40 type = string
41 default = "DEV1-S"
42}L'attribut sensitive = true empêche Terraform d'afficher ces valeurs dans les logs.
Différence region/zone : Chez Scaleway, une région (fr-par) contient plusieurs zones (fr-par-1, fr-par-2, fr-par-3). Les ressources sont créées dans une zone spécifique.
terraform.tfvars
Crée ce fichier pour stocker tes valeurs (ne le committe jamais !) :
1scw_access_key = "SCWXXXXXXXXXXXXXXXXX"
2scw_secret_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3scw_organization_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
4project_name = "mon-super-projet"Ajoute terraform.tfvars et *.auto.tfvars à ton .gitignore pour éviter de committer tes secrets.
Créer le projet et configurer le provider
main.tf
1# Configuration du provider
2provider "scaleway" {
3 access_key = var.scw_access_key
4 secret_key = var.scw_secret_key
5 organization_id = var.scw_organization_id
6 region = var.region
7 zone = var.zone
8}
9
10# Créer un projet pour isoler les ressources
11resource "scaleway_account_project" "main" {
12 name = var.project_name
13 description = "Projet géré par Terraform"
14}Un projet Scaleway permet d'isoler tes ressources et de gérer les permissions. C'est une bonne pratique d'avoir un projet par environnement (dev, staging, prod).
Générer une clé SSH automatiquement
Plutôt que de créer une clé SSH manuellement, laissons Terraform s'en charger :
1# Générer une paire de clés ED25519
2resource "tls_private_key" "ssh" {
3 algorithm = "ED25519"
4}
5
6# Enregistrer la clé publique dans Scaleway
7resource "scaleway_iam_ssh_key" "main" {
8 name = "${var.project_name}-terraform"
9 public_key = trimspace(tls_private_key.ssh.public_key_openssh)
10 project_id = scaleway_account_project.main.id
11}
12
13# Sauvegarder la clé privée localement
14resource "local_file" "ssh_private_key" {
15 content = tls_private_key.ssh.private_key_openssh
16 filename = "${path.module}/${var.project_name}_ed25519"
17 file_permission = "0600"
18}
19
20# Sauvegarder la clé publique localement
21resource "local_file" "ssh_public_key" {
22 content = tls_private_key.ssh.public_key_openssh
23 filename = "${path.module}/${var.project_name}_ed25519.pub"
24}Quelques points importants :
- ED25519 est plus sécurisé et plus rapide que RSA
trimspace()supprime les espaces/retours à la ligne parasitesfile_permission = "0600"est obligatoire pour SSH (lecture/écriture uniquement par le propriétaire)path.moduleréférence le dossier contenant les fichiers Terraform
Configurer le Security Group
Un Security Group agit comme un firewall pour ton instance. Nous allons :
- Bloquer tout le trafic entrant par défaut
- Autoriser SSH uniquement depuis notre IP
- Ouvrir les ports HTTP (80) et HTTPS (443)
1# Récupérer notre IP publique actuelle
2data "http" "my_ip" {
3 url = "https://ipv4.icanhazip.com"
4}
5
6locals {
7 my_ip = "${chomp(data.http.my_ip.response_body)}/32"
8}
9
10# Créer le Security Group
11resource "scaleway_instance_security_group" "main" {
12 project_id = scaleway_account_project.main.id
13 name = "${var.project_name}-sg"
14 description = "Security group pour ${var.project_name}"
15 inbound_default_policy = "drop" # Bloquer tout par défaut
16 outbound_default_policy = "accept" # Autoriser les sorties
17
18 # SSH - restreint à notre IP uniquement
19 inbound_rule {
20 action = "accept"
21 port = 22
22 protocol = "TCP"
23 ip_range = local.my_ip
24 }
25
26 # HTTP - ouvert à tous
27 inbound_rule {
28 action = "accept"
29 port = 80
30 protocol = "TCP"
31 }
32
33 # HTTPS - ouvert à tous
34 inbound_rule {
35 action = "accept"
36 port = 443
37 protocol = "TCP"
38 }
39}Astuce : Le data source http appelle une API qui retourne notre IP publique. La fonction chomp() supprime le retour à la ligne. Le /32 indique un masque CIDR pour une seule IP.
Cette configuration suit le principe du deny by default : on bloque tout, puis on ouvre explicitement ce dont on a besoin.
Déployer l'instance
Réserver une IP publique
1resource "scaleway_instance_ip" "main" {
2 project_id = scaleway_account_project.main.id
3}Réserver une IP séparément permet de la conserver même si on détruit et recrée l'instance. Pratique pour éviter de changer tes enregistrements DNS.
Créer l'instance
1resource "scaleway_instance_server" "main" {
2 project_id = scaleway_account_project.main.id
3 name = "${var.project_name}-server"
4 type = var.instance_type
5 image = "ubuntu_jammy"
6
7 # Attacher l'IP et le Security Group
8 ip_id = scaleway_instance_ip.main.id
9 security_group_id = scaleway_instance_security_group.main.id
10
11 tags = ["terraform", "production"]
12}Types d'instances disponibles :
| Type | vCPU | RAM | Prix/mois |
|---|---|---|---|
| DEV1-S | 2 | 2 GB | ~10€ |
| DEV1-M | 3 | 4 GB | ~20€ |
| DEV1-L | 4 | 8 GB | ~40€ |
| GP1-XS | 4 | 16 GB | ~70€ |
Images disponibles : ubuntu_jammy (22.04), ubuntu_focal (20.04), debian_bookworm, debian_bullseye, etc.
Provisionner avec Cloud-init
Cloud-init permet de configurer l'instance au premier démarrage. C'est idéal pour installer des packages, créer des fichiers, ou exécuter des scripts.
Version simple (inline)
1resource "scaleway_instance_server" "main" {
2 # ... configuration précédente ...
3
4 user_data = {
5 cloud-init = <<-EOF
6 #cloud-config
7 package_update: true
8 package_upgrade: true
9
10 packages:
11 - apt-transport-https
12 - ca-certificates
13 - curl
14 - git
15 - htop
16 - docker.io
17
18 runcmd:
19 - systemctl enable docker
20 - systemctl start docker
21 EOF
22 }
23}Note : Le user_data doit être défini avant la création de l'instance, car cloud-init s'exécute au premier démarrage.
Version avec fichier template
Pour des configurations plus complexes, utilise un fichier séparé :
1locals {
2 cloud_init_content = templatefile("${path.module}/templates/cloud-init.yml", {
3 project_name = var.project_name
4 })
5}
6
7resource "scaleway_instance_server" "main" {
8 # ... configuration précédente ...
9
10 user_data = {
11 cloud-init = local.cloud_init_content
12 }
13
14 # Recréer l'instance si cloud-init change
15 lifecycle {
16 replace_triggered_by = [terraform_data.cloud_init_hash]
17 }
18}
19
20# Hash du cloud-init pour détecter les changements
21resource "terraform_data" "cloud_init_hash" {
22 input = md5(local.cloud_init_content)
23}Et dans templates/cloud-init.yml :
1#cloud-config
2package_update: true
3package_upgrade: true
4
5packages:
6 - apt-transport-https
7 - ca-certificates
8 - curl
9 - git
10 - htop
11 - docker.io
12
13runcmd:
14 - systemctl enable docker
15 - systemctl start docker
16 - echo "Provisioning complete for ${project_name}"La fonction templatefile() permet d'injecter des variables dans le fichier YAML.
Outputs
Définis des outputs pour récupérer facilement les informations importantes :
1output "project_id" {
2 description = "ID du projet Scaleway"
3 value = scaleway_account_project.main.id
4}
5
6output "instance_public_ip" {
7 description = "IP publique de l'instance"
8 value = scaleway_instance_ip.main.address
9}
10
11output "ssh_command" {
12 description = "Commande SSH pour se connecter"
13 value = "ssh -i ${local_file.ssh_private_key.filename} ubuntu@${scaleway_instance_ip.main.address}"
14}Après terraform apply, tu peux afficher ces valeurs avec :
1terraform outputDéployer l'infrastructure
Initialiser Terraform
1$ terraform init
2
3Initializing the backend...
4Initializing provider plugins...
5- Finding scaleway/scaleway versions matching "~> 2.0"...
6- Installing scaleway/scaleway v2.x.x...
7
8Terraform has been successfully initialized!Prévisualiser les changements
1$ terraform plan
2
3Plan: 8 to add, 0 to change, 0 to destroy.Vérifie toujours le plan avant d'appliquer. C'est ton filet de sécurité.
Appliquer
1$ terraform apply
2
3Do you want to perform these actions?
4 Enter a value: yes
5
6Apply complete! Resources: 8 added, 0 changed, 0 destroyed.
7
8Outputs:
9
10instance_public_ip = "51.15.xxx.xxx"
11ssh_command = "ssh -i ./mon-projet_ed25519 ubuntu@51.15.xxx.xxx"Se connecter à l'instance
1$ ssh -i ./mon-projet_ed25519 ubuntu@51.15.xxx.xxx
2
3Welcome to Ubuntu 22.04.x LTS
4ubuntu@mon-projet-server:~$Gérer le state avec Terraform Cloud
Pour travailler en équipe ou depuis une CI/CD, stocke le state dans Terraform Cloud (gratuit jusqu'à 500 ressources gérées) :
1terraform {
2 cloud {
3 organization = "mon-organisation"
4
5 workspaces {
6 name = "mon-projet-prod"
7 }
8 }
9
10 # ... required_providers ...
11}Avantages :
- State partagé : Toute l'équipe voit le même état
- Locking : Empêche les modifications simultanées
- Historique : Conserve les versions précédentes du state
- Sécurité : Le state n'est pas stocké en local
Connecte-toi avec :
1$ terraform login
2$ terraform initBonnes pratiques
1. Structure des fichiers
Pour un projet plus conséquent, organise tes fichiers :
1infra/
2├── main.tf # Provider et ressources principales
3├── network.tf # Security groups, VPC
4├── compute.tf # Instances
5├── variables.tf
6├── outputs.tf
7├── versions.tf
8└── templates/
9 └── cloud-init.yml2. Nommage cohérent
Préfixe tes ressources avec le nom du projet :
1resource "scaleway_instance_server" "main" {
2 name = "${var.project_name}-server"
3}3. Utilise des tags
Les tags facilitent le filtrage et la gestion des coûts :
1tags = ["terraform", "production", var.project_name]4. Ne jamais modifier le state manuellement
Si tu dois importer une ressource existante :
1terraform import scaleway_instance_server.main fr-par-1/instance-id5. Détruire proprement
Pour tout supprimer :
1$ terraform destroyPour supprimer une ressource spécifique :
1$ terraform destroy -target=scaleway_instance_server.mainEstimation des coûts
Pour l'infrastructure décrite dans cet article :
| Ressource | Coût mensuel |
|---|---|
| Instance DEV1-S | ~10€ |
| IP publique (attachée) | Gratuit |
| Total | ~10€/mois |
Note : Une IP publique attachée à une instance est gratuite. Tu ne paies (~3€/mois) que si tu réserves une IP sans l'attacher à une instance.
TLDR
1# 1. Créer les fichiers Terraform (main.tf, variables.tf, etc.)
2
3# 2. Créer terraform.tfvars avec tes credentials
4scw_access_key = "SCWXXXXXXXXX"
5scw_secret_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
6scw_organization_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
7
8# 3. Initialiser
9terraform init
10
11# 4. Prévisualiser
12terraform plan
13
14# 5. Déployer
15terraform apply
16
17# 6. Se connecter
18ssh -i ./mon-projet_ed25519 ubuntu@<IP>
19
20# 7. Détruire (quand tu n'en as plus besoin)
21terraform destroyPour aller plus loin
- Documentation du provider Scaleway
- Documentation Scaleway
- Terraform Cloud
- Mon article sur Terraform et Docker
Dans un prochain article, nous verrons comment déployer une application complète avec Docker Compose, Traefik, et Let's Encrypt sur cette infrastructure.