Terraform Provider OpenStack¶
Introduction¶
Le provider Terraform OpenStack permet de provisionner des ressources cloud de manière déclarative et reproductible. C'est l'outil standard pour l'Infrastructure as Code (IaC) sur OpenStack.
Prérequis¶
- OpenStack fonctionnel avec APIs accessibles
- Phase 3 - Kolla-Ansible
- Terraform installé (v1.5+)
- Credentials OpenStack (clouds.yaml ou variables d'environnement)
Points à apprendre¶
Architecture IaC avec Terraform¶
graph TB
subgraph External
DevOps["DevOps/Platform Engineer<br/>Gère l'infrastructure"]
CI["CI/CD Pipeline<br/>GitLab CI, GitHub Actions"]
end
subgraph "IaC Stack"
TF["Terraform<br/>HCL<br/>Déclaration ressources"]
State["State Backend<br/>S3/Swift<br/>État de l'infrastructure"]
Git["Git Repository<br/>GitLab/GitHub<br/>Versioning configs"]
end
subgraph "OpenStack Cloud"
API["OpenStack APIs<br/>Nova, Neutron, Cinder..."]
Resources["Resources<br/>VMs, Networks, Volumes"]
end
DevOps -->|Push code<br/>git| Git
Git -->|Trigger<br/>webhook| CI
CI -->|Execute<br/>terraform apply| TF
TF -->|Read/Write| State
TF -->|API calls<br/>HTTPS| API
API -->|Provision| Resources
Installation¶
# Installation Terraform
wget https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_amd64.zip
unzip terraform_1.6.6_linux_amd64.zip
sudo mv terraform /usr/local/bin/
# Vérifier
terraform version
Configuration du provider¶
# versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "~> 1.54"
}
}
# Backend pour le state (recommandé en équipe)
backend "swift" {
container = "terraform-state"
archive_container = "terraform-state-archive"
state_name = "production.tfstate"
}
}
# provider.tf
provider "openstack" {
# Authentification via clouds.yaml (recommandé)
cloud = "openstack"
# Ou via variables d'environnement OS_*
# auth_url = var.auth_url
# user_name = var.user_name
# password = var.password
# tenant_name = var.tenant_name
# region = var.region
}
Configuration clouds.yaml¶
# ~/.config/openstack/clouds.yaml
clouds:
openstack:
auth:
auth_url: https://10.0.0.10:5000/v3
username: admin
password: ${OS_PASSWORD}
project_name: admin
project_domain_name: Default
user_domain_name: Default
region_name: RegionOne
interface: internal
verify: false # Si certificat self-signed
Variables et outputs¶
# variables.tf
variable "environment" {
description = "Environnement (dev, staging, prod)"
type = string
default = "dev"
}
variable "network_cidr" {
description = "CIDR du réseau privé"
type = string
default = "192.168.100.0/24"
}
variable "instance_count" {
description = "Nombre d'instances à créer"
type = number
default = 1
}
variable "flavor_name" {
description = "Flavor pour les instances"
type = string
default = "m1.small"
}
variable "image_name" {
description = "Image de base"
type = string
default = "ubuntu-22.04"
}
# outputs.tf
output "instance_ips" {
description = "Adresses IP des instances"
value = openstack_compute_instance_v2.app[*].access_ip_v4
}
output "floating_ips" {
description = "Floating IPs assignées"
value = openstack_networking_floatingip_v2.app[*].address
}
output "network_id" {
description = "ID du réseau créé"
value = openstack_networking_network_v2.app.id
}
Ressources de base¶
# main.tf
# Réseau privé
resource "openstack_networking_network_v2" "app" {
name = "${var.environment}-app-network"
admin_state_up = true
}
# Sous-réseau
resource "openstack_networking_subnet_v2" "app" {
name = "${var.environment}-app-subnet"
network_id = openstack_networking_network_v2.app.id
cidr = var.network_cidr
ip_version = 4
dns_nameservers = ["8.8.8.8", "8.8.4.4"]
allocation_pool {
start = cidrhost(var.network_cidr, 10)
end = cidrhost(var.network_cidr, 200)
}
}
# Routeur
resource "openstack_networking_router_v2" "app" {
name = "${var.environment}-router"
external_network_id = data.openstack_networking_network_v2.external.id
}
resource "openstack_networking_router_interface_v2" "app" {
router_id = openstack_networking_router_v2.app.id
subnet_id = openstack_networking_subnet_v2.app.id
}
# Security group
resource "openstack_networking_secgroup_v2" "app" {
name = "${var.environment}-app-sg"
description = "Security group for application"
}
resource "openstack_networking_secgroup_rule_v2" "ssh" {
security_group_id = openstack_networking_secgroup_v2.app.id
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "0.0.0.0/0"
}
resource "openstack_networking_secgroup_rule_v2" "http" {
security_group_id = openstack_networking_secgroup_v2.app.id
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "0.0.0.0/0"
}
# Instance
resource "openstack_compute_instance_v2" "app" {
count = var.instance_count
name = "${var.environment}-app-${count.index + 1}"
flavor_name = var.flavor_name
image_name = var.image_name
key_pair = openstack_compute_keypair_v2.app.name
security_groups = [openstack_networking_secgroup_v2.app.name]
network {
uuid = openstack_networking_network_v2.app.id
}
metadata = {
environment = var.environment
managed_by = "terraform"
}
user_data = file("${path.module}/cloud-init.yaml")
}
# Keypair
resource "openstack_compute_keypair_v2" "app" {
name = "${var.environment}-keypair"
public_key = file("~/.ssh/id_rsa.pub")
}
# Floating IP
resource "openstack_networking_floatingip_v2" "app" {
count = var.instance_count
pool = "external"
}
resource "openstack_compute_floatingip_associate_v2" "app" {
count = var.instance_count
floating_ip = openstack_networking_floatingip_v2.app[count.index].address
instance_id = openstack_compute_instance_v2.app[count.index].id
}
# Data sources
data "openstack_networking_network_v2" "external" {
name = "external"
}
Cloud-init¶
# cloud-init.yaml
#cloud-config
package_update: true
package_upgrade: true
packages:
- nginx
- docker.io
- python3-pip
users:
- name: deploy
groups: docker, sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ${file("~/.ssh/id_rsa.pub")}
runcmd:
- systemctl enable nginx
- systemctl start nginx
- systemctl enable docker
- systemctl start docker
final_message: "Instance ready after $UPTIME seconds"
Workflow Terraform¶
flowchart TD
Start([Start]) --> Init["terraform init<br/><i>Télécharge providers<br/>Configure backend</i>"]
Init --> Validate["terraform validate<br/><i>Vérifie syntaxe HCL</i>"]
Validate --> Plan["terraform plan<br/><i>Calcule les changements<br/>Sans modifier</i>"]
Plan --> PlanOK{Plan OK?}
PlanOK -->|yes| Apply["terraform apply<br/><i>Applique les changements<br/>Demande confirmation</i>"]
PlanOK -->|no| Fix[Corriger configuration]
Apply --> ApplyOK{Apply réussi?}
ApplyOK -->|yes| Created[Resources created]
ApplyOK -->|no| Rollback[Rollback partiel<br/>Vérifier state]
Created --> Show["terraform show<br/><i>Affiche l'état actuel</i>"]
Rollback --> Show
Fix --> Show
Show --> Stop([Stop])
Commandes essentielles¶
# Initialiser le projet
terraform init
# Valider la syntaxe
terraform validate
# Formater le code
terraform fmt -recursive
# Planifier les changements
terraform plan -out=tfplan
# Appliquer les changements
terraform apply tfplan
# Afficher l'état
terraform show
terraform state list
# Importer une ressource existante
terraform import openstack_compute_instance_v2.existing <instance-id>
# Détruire l'infrastructure
terraform destroy
# Workspace (environnements)
terraform workspace new production
terraform workspace select production
Exemples pratiques¶
Structure de projet recommandée¶
terraform-openstack/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ └── production/
├── modules/
│ ├── network/
│ ├── compute/
│ └── security/
├── .gitignore
├── .terraform-version
└── README.md
Fichier .gitignore¶
# .gitignore
.terraform/
*.tfstate
*.tfstate.*
*.tfvars
!example.tfvars
.terraform.lock.hcl
crash.log
*.pem
*.key
Test rapide¶
# Créer une infrastructure minimale
cat > main.tf << 'EOF'
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "~> 1.54"
}
}
}
provider "openstack" {}
resource "openstack_compute_instance_v2" "test" {
name = "terraform-test"
flavor_name = "m1.small"
image_name = "cirros"
network {
name = "provider-net"
}
}
EOF
# Exécuter
source ~/openrc
terraform init
terraform apply -auto-approve
# Vérifier
openstack server list | grep terraform-test
# Nettoyer
terraform destroy -auto-approve
Ressources¶
Checkpoint¶
- Terraform installé et configuré
- Provider OpenStack fonctionnel
- Création de ressources de base (VM, réseau)
- Variables et outputs utilisés
- State backend configuré (Swift ou S3)
- Structure de projet organisée