Déploiement d'une VM Windows avec IIS sur Azure: Lab DY67
Dans le cadre du programme Développement d'applications sécuritaires au Cégep Limoilou, le lab DY67 avait pour objectif de provisionner une machine virtuelle Windows Server sur Azure en utilisant Terraform; un approche d'infrastructure en tant que code (Infra as Code). Voici ce que j'ai déployé, comment ça fonctionne et ce que j'en ai retenu.
Aperçu de l'infrastructure
L'ensemble de l'environnement est défini dans main.tf et se déploie avec une seule commande. Terraform crée les ressources suivantes dans l'ordre:
- Groupe de ressources Azure (AKA Resrource group)
- Réseau virtuel (VNet AKA virtual Network) et sous-réseau
- Groupe de sécurité réseau (NSG AKA Network Security Group) avec règles d'accès restreintes
- Interface réseau (NIC) associée au NSG
- Machine virtuelle Windows Server (
Standard_B2s) - Disque de données managé de 50 Go
- Extension de script personnalisé pour l'installation d'IIS
Réseau et sécurité (NSG)
Le réseau virtuel utilise l'espace d'adressage 10.0.0.0/16 et un sous-réseau 10.0.1.0/24. Le NSG définit deux règles d'entrée:
security_rule {
name = "Allow-HTTP-MyIP"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_address_prefix = var.my_ip
destination_port_range = "80"
...
}
security_rule {
name = "Allow-RDP-MyIP"
priority = 110
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_address_prefix = var.my_ip
destination_port_range = "3389"
...
}
Note: Les deux règles utilisent
var.my_ipcomme source. Seule mon adresse IP peut accéder au port 80 (HTTP) et au port 3389 (RDP). C'est une posture de sécurité minimale mais efficace pour un lab: on évite d'exposer la VM à l'internet public.
La machine virtuelle
La VM tourne sur Windows Server (SKU configurable via var.windows_sku), taille Standard_B2s, avec un disque OS de 127 Go en Standard_LRS. L'adresse IP privée est allouée dynamiquement via la NIC.
resource "azurerm_windows_virtual_machine" "vm" {
name = var.vm_name
size = "Standard_B2s"
admin_username = var.admin_username
admin_password = var.admin_password
network_interface_ids = [azurerm_network_interface.nic.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
disk_size_gb = 127
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = var.windows_sku
version = "latest"
}
}
Disque de données (50 Go)
Un disque managé de 50 Go (Standard_LRS) est créé séparément et attaché à la VM au LUN 0:
resource "azurerm_managed_disk" "disk2" {
disk_size_gb = 50
storage_account_type = "Standard_LRS"
create_option = "Empty"
}
resource "azurerm_virtual_machine_data_disk_attachment" "attach" {
managed_disk_id = azurerm_managed_disk.disk2.id
virtual_machine_id = azurerm_windows_virtual_machine.vm.id
lun = 0
caching = "ReadWrite"
}
Séparer le disque de données du disque OS permet de recréer ou redimensionner la VM sans perdre les données applicatives.
Installation d'IIS via extension
L'extension CustomScriptExtension exécute un script PowerShell au premier démarrage de la VM. Elle installe IIS, crée une page d'accueil et active Windows Defender:
resource "azurerm_virtual_machine_extension" "iis" {
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.10"
settings = <<SETTINGS
{
"commandToExecute": "powershell -ExecutionPolicy Unrestricted -Command \"Install-WindowsFeature -Name Web-Server -IncludeManagementTools; New-Item -Path 'C:\\inetpub\\wwwroot' -ItemType Directory -Force; Set-Content -Path 'C:\\inetpub\\wwwroot\\index.html' -Value 'Hello World from Azure VM'; Set-MpPreference -DisableRealtimeMonitoring $false\""
}
SETTINGS
}
Cette approche évite de se connecter manuellement via RDP pour configurer le serveur. C'est la base d'une automatisation plus poussée (Ansible, cloud-init, scripts plus complexes).
Variables et Outputs
variables.tf expose les paramètres configurables: emplacement Azure, nom de la VM, SKU Windows, identifiants admin, adresse IP autorisée (my_ip) et tags.
outputs.tf affiche l'adresse IP privée de la VM après le déploiement, ce qui permet de vérifier que la ressource a bien été créée.
Note: Ne jamais committer
*.tfvars. Ce fichier contient les identifiants (admin_password) et l'adresse IP personnelle (my_ip). Il est dans.gitignorepour cette raison.
Commandes utilisées
| Commande | Description |
|---|---|
terraform init |
Télécharge le provider AzureRM et prépare le répertoire |
terraform validate |
Vérifie la syntaxe des fichiers .tf |
terraform plan |
Affiche ce que Terraform va créer ou modifier |
terraform apply |
Déploie l'infrastructure (confirmation requise) |
terraform output |
Affiche les valeurs définies dans outputs.tf |
terraform destroy |
Détruit toutes les ressources créées |
Pour passer des variables via CLI, la syntaxe diffère selon le shell:
# Bash
terraform apply -var="resource_group_name=rg-lab"
# PowerShell
terraform apply -var 'resource_group_name=rg-lab'
Ce que j'ai retenu
- Reproductibilité: l'infrastructure entière se recrée ou se détruit en une commande. Idéal pour des labs temporaires où on ne veut pas laisser des ressources Azure tourner inutilement.
- Sécurité réseau: restreindre le NSG à une seule IP est simple à faire avec Terraform et déjà bien meilleur qu'un accès ouvert à tout internet. En production, on irait plus loin avec des règles par CIDR ou un bastion.
- Extension VM: le
CustomScriptExtensionest puissant mais limité pour des configs complexes. Pour une vrai automatisation, on passerait à Ansible ou à des images pré-configurées (Packer). - Séparation variables/config: garder
variables.tfetoutputs.tfséparés demain.tfrend la configuration réutilisable dans d'autres environnements sans toucher à la logique principale.
J'ai trouvé très pratique d'avoir un fichier transférable et sécure que je pouvais réutiliser et modifier selon mes besoins.
NOTE: la majeure partie du code utilisé provient de la documentation et est générique. Cela me suffit, mais c'est "worth diving" et de l'explorer davantage si vous envisagez une utilisation en prod.
Références
- AzureRM Provider
- azurerm_resource_group
- azurerm_virtual_network
- azurerm_subnet
- azurerm_network_security_group
- azurerm_network_interface
- azurerm_network_interface_security_group_association
- azurerm_windows_virtual_machine
- azurerm_managed_disk
- azurerm_virtual_machine_data_disk_attachment
- azurerm_virtual_machine_extension
- Terraform Variables
- Terraform Outputs