Skip to content

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_ip comme 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 .gitignore pour 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 CustomScriptExtension est 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.tf et outputs.tf séparés de main.tf rend 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