Introduction
Rappel : dans l’article précédent, nous avons vu comment créer un template VSphere pour Ubuntu 20.04.
Maintenant, que nous avons le template et le fichier de "post-configuration", nous aimerions automatiser son déploiement. Or, il y a un outil pour ça : Terraform. Si l’outil a initialement été conçu pour provisionner des machines dans le cloud, il supporte VSphere.
Je renouvelle l’avertissement de mon post précédent : l’administration système (et Terraform en particulier) n’étant pas ma spécialité, il est possible que le script qui suit contienne des mauvaises pratiques. N’hésitez pas à me le signaler : je corrigerai en conséquence. |
Pour utiliser le script qui suit, il est obligatoire d’avoir assigné une adresse IP fixe au template de la machine virtuelle. C’est cette IP fixe qui permet dans la dernière partie du script de s’y connecter pour injecter le script de configuration et l’exécuter. |
Structure
Le code Terraform peut être assez redondant. Nous allons donc mutualiser ce qui peut l’être dans un module.
Voici la structure de notre projet :
- main.tf
- files
- ubuntu20.04_config.sh
- modules
- ubuntu20.04
- main.tf
Le module
# Constantes (1)
locals {
vm_config_file = "ubuntu20.04_config.sh"
# Ces constantes ont été définies lors de l'installation d'Ubuntu, pour créer le template.
vm_template_login = "admin"
vm_template_password = "my_secret_password"
vm_template_ip = "192.168.0.10"
}
# Infrastructure VSphere.
provider "vsphere" { (2)
user = "vsphere_user"
password = "vsphere_password"
vsphere_server = "vsphere_server"
}
data "vsphere_datacenter" "dc" {
name = "datacenter"
}
data "vsphere_datastore" "ds" {
name = var.vm_datastore
datacenter_id = data.vsphere_datacenter.dc.id
}
data "vsphere_compute_cluster" "cluster" {
name = "cluster"
datacenter_id = data.vsphere_datacenter.dc.id
}
data "vsphere_network" "network" {
name = "vlan1"
datacenter_id = data.vsphere_datacenter.dc.id
}
data "vsphere_virtual_machine" "ubuntu2004_template" {
name = "ubuntu20.04_template"
datacenter_id = data.vsphere_datacenter.dc.id
}
# Variables to configure VM. (3)
variable "vm_hostname" {
description = "VM hostname."
type = string
}
variable "vm_ip" {
description = "VM IP."
type = string
}
variable "vm_num_cpus" {
description = "VM CPUs."
type = number
default = 1
}
variable "vm_memory" {
description = "VM memory (MB)."
type = number
default = 2048
}
variable "vm_datastore" {
description = "VM datastore."
type = string
default = "datastore1"
}
# Déclaration des disques (4)
variable "vm_disks" {
description = "VM memory (MB)."
type = map(any)
default = {
disk0 = {
unit_number = 0
size = 8 # Taille du template par défaut (en GB).
}
}
}
# Déclaration de la VM
resource "vsphere_virtual_machine" "ubuntu_server_2004" {
name = var.vm_hostname
resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id
datastore_id = data.vsphere_datastore.ds.id
guest_id = data.vsphere_virtual_machine.ubuntu2004_template.guest_id
scsi_type = data.vsphere_virtual_machine.ubuntu2004_template.scsi_type
num_cpus = var.vm_num_cpus
memory = var.vm_memory
network_interface {
network_id = data.vsphere_network.network.id
}
# Configuration des disques (4)
dynamic "disk" {
for_each = var.vm_disks
content {
label = disk.key
size = disk.value.size
unit_number = try(disk.value.unit_number, 0)
}
}
clone {
template_uuid = data.vsphere_virtual_machine.ubuntu2004_template.id
}
# Injection et exécution du script de post-configuration
provisioner "file" {
source = "files/${local.vm_config_file}"
destination = "/tmp/${local.vm_config_file}"
connection {
type = "ssh"
host = local.vm_template_ip
user = var.vm_template_login
password = var.vm_template_password
}
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/${local.vm_config_file}",
"echo \"${var.vm_template_password}\" | sudo -S /tmp/${local.vm_config_file} ${var.vm_hostname} ${var.vm_ip}",
"rm -f /tmp/${local.vm_config_file}",
"echo \"${var.vm_template_password}\" | sudo -S netplan apply"
]
connection {
type = "ssh"
host = local.vm_template_ip
user = var.vm_template_login
password = var.vm_template_password
}
}
}
1 | Les locals permettent d’éviter les répétitions de constantes (ou de précalculer des valeurs en combinant des variables). |
2 | Pour simplifier le script, j’ai mis les identifiants en dur. Il est possible de les remplacer par des variables, pour éviter que les identifiants se retrouvent en gestion de configuration et/ou si on souhaite adresser de multiples instances VSphere. |
3 | Les variables permettent d’injecter les éléments de configuration qui varient d’une machine à l’autre. |
4 | L’utilisation d’une map et d’une boucle permet d’affecter plusieurs disques à notre machine virtuelle en cas de besoin. |
Ce module est assez simpliste. Il est possible d’externaliser plus de choses sous la forme de constantes/variables, pour prendre en compte une architecture VMWare plus complexes (multiples datacenters, clusters, réseaux, etc.) Il est également possible d’affecter plusieurs réseaux à une même machine, en utilisant la même technique que pour l’affectation des disques. |
La déclaration des machines
Le module étant créé, il ne reste plus qu’à déclarer les machines dans le fichier principal main.tf
.
# Première VM, utilisant les valeurs par défaut du template.
module "vm1" {
source = "./modules/ubuntu20.04"
vm_hostname = "vm1"
vm_ip = "192.168.0.11"
}
# Utilisation des variables pour configurer les différents éléments.
module "vm2" {
source = "./modules/ubuntu20.04"
vm_hostname = "vm2"
vm_ip = "192.168.0.12"
vm_num_cpus = 2
vm_memory = 4096
vm_datastore = "datastore2"
vm_disks = {
"disk0" = { size = 30 }
"disk1" = {
size = 40
unit_number = 1
}
"disk2" = {
size = 50
unit_number = 2
}
}
}
Le déploiement
Il n’y a plus qu’à exécuter et à laisser Terraform travailler :
terraform init
terraform plan
terraform apply
Conlusion
Terraform, c’est un super-outil pour automatiser le déploiement de l’infrastructure.
C’est aussi un outil "dangereux" : il ne sait pas gérer toutes les modifications d’infrastructure "en place", et en cas de modification d’une machine, il va parfois procéder en la supprimant/recréant. Or, tout le monde n’est pas passé à l’approche infrastructure immutable, où les machines n’hébergent que du code et où les données sont attachées depuis l’extérieur… Si on n’y prête pas attention, on peut donc avoir vite fait de supprimer une machine hébergeant des données de production. |