Aller au contenu

WinRM et Sysprep

Configuration de WinRM over HTTPS pour le provisioning Ansible et préparation de Sysprep pour la generalisation de l'image.


WinRM : le protocole de gestion Windows

WinRM (Windows Remote Management) est le protocole standard de Microsoft pour l'administration distante. Contrairement a SSH sur Linux, WinRM utilisé SOAP over HTTP/HTTPS.

Aspect SSH (Linux) WinRM (Windows)
Protocole SSH SOAP/HTTP(S)
Port par défaut 22 5985 (HTTP) / 5986 (HTTPS)
Authentification Clés SSH NTLM, Kerberos, certificats
Chiffrement Natif Via HTTPS (TLS)

Sécurité

N'utilisez jamais WinRM over HTTP en production. Toujours WinRM over HTTPS avec un certificat (même auto-signe pour le build Packer).

Script de bootstrap WinRM

Ce script PowerShell active WinRM/HTTPS avec un certificat auto-signe. Packer l'exécuté au démarrage de la VM via les metadata GCP.

Créez scripts/setup-winrm.ps1 :

# Configurer WinRM HTTPS avec certificat auto-signe
$ErrorActionPreference = "Stop"

# Creer un certificat auto-signe
$cert = New-SelfSignedCertificate `
    -DnsName $env:COMPUTERNAME `
    -CertStoreLocation Cert:\LocalMachine\My `
    -NotAfter (Get-Date).AddYears(1)

# Activer WinRM
Enable-PSRemoting -Force -SkipNetworkProfileCheck

# Supprimer les listeners existants
Get-ChildItem WSMan:\localhost\Listener | Remove-Item -Recurse -Force

# Creer le listener HTTPS
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS `
    -Address * -CertificateThumbPrint $cert.Thumbprint -Force

# Configurer les limites WinRM
Set-Item WSMan:\localhost\MaxTimeoutms 1800000
Set-Item WSMan:\localhost\Service\AllowUnencrypted $false
Set-Item WSMan:\localhost\Service\Auth\Basic $true
Set-Item WSMan:\localhost\Service\Auth\Negotiate $true

# Ouvrir le firewall pour WinRM HTTPS
New-NetFirewallRule -Name "WinRM-HTTPS" `
    -DisplayName "WinRM HTTPS (5986)" `
    -Protocol TCP -LocalPort 5986 `
    -Action Allow -Direction Inbound

# Redemarrer le service
Restart-Service WinRM

Configuration du communicator Packer

Le communicator Packer pour Windows utilisé WinRM. Voici le bloc source pour GCP :

source "googlecompute" "windows-vdi" {
  project_id          = var.project_id
  zone                = var.zone
  source_image_family = "windows-2022"
  source_image_project_id = "windows-cloud"
  machine_type        = var.machine_type
  disk_size           = var.disk_size
  disk_type           = "pd-ssd"

  image_name          = "vdi-windows-${var.profile}-{{timestamp}}"
  image_family        = "vdi-windows-${var.profile}"
  image_description   = "VDI Windows Server 2022 - profil ${var.profile}"

  universe_domain     = var.universe_domain

  communicator        = "winrm"
  winrm_username      = "packer_user"
  winrm_insecure      = true
  winrm_use_ssl       = true
  winrm_timeout       = "10m"

  tags = ["packer-build"]

  metadata = {
    sysprep-specialize-script-ps1 = file("../scripts/setup-winrm.ps1")
  }
}

Metadata GCP

GCP exécuté automatiquement le script passe dans sysprep-specialize-script-ps1 au premier démarrage de la VM. C'est le mécanisme idéal pour activer WinRM avant que Packer tente de se connecter.

Provisioner Ansible

Le bloc build utilise le provisioner Ansible :

build {
  sources = ["source.googlecompute.windows-vdi"]

  provisioner "ansible" {
    playbook_file = "../ansible/playbook.yml"
    user          = "packer_user"
    use_proxy     = false
    extra_arguments = [
      "--extra-vars", "ansible_winrm_server_cert_validation=ignore profile=${var.profile}"
    ]
  }
}

Sysprep : generaliser l'image

Sysprep supprimé les identifiants uniques (SID, nom machine) pour que chaque VM créée depuis l'image soit unique.

Fichier unattend.xml

Créez scripts/unattend.xml :

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="generalize">
    <component name="Microsoft-Windows-PnpSysprep"
               processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35"
               language="neutral" versionScope="nonSxS">
      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
    </component>
  </settings>
  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-Shell-Setup"
               processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35"
               language="neutral" versionScope="nonSxS">
      <OOBE>
        <HideEULAPage>true</HideEULAPage>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <NetworkLocation>Work</NetworkLocation>
        <ProtectYourPC>3</ProtectYourPC>
      </OOBE>
      <TimeZone>Romance Standard Time</TimeZone>
    </component>
  </settings>
</unattend>

Script Sysprep

Créez scripts/sysprep.ps1 :

# Nettoyer avant Sysprep
Remove-Item -Path "C:\Windows\Temp\*" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue

# Copier unattend.xml
Copy-Item -Path "C:\scripts\unattend.xml" -Destination "C:\Windows\System32\Sysprep\unattend.xml"

# Lancer Sysprep
& "$env:SystemRoot\System32\Sysprep\sysprep.exe" `
    /generalize /oobe /shutdown /quiet `
    /unattend:"C:\Windows\System32\Sysprep\unattend.xml"

Ordre d'exécution

Sysprep doit être la dernière étape du build Packer. Ajoutez-le comme dernier provisioner dans le bloc build, après Ansible.

Provisioner Sysprep dans Packer

Ajoutez ce bloc à la fin du build :

  provisioner "file" {
    source      = "../scripts/unattend.xml"
    destination = "C:\\scripts\\unattend.xml"
  }

  provisioner "powershell" {
    script = "../scripts/sysprep.ps1"
  }

Note sur OpenSSH natif

Depuis Windows Server 2019, Microsoft fournit un serveur OpenSSH natif. Ansible peut l'utiliser comme sur Linux :

# Installer OpenSSH Server (optionnel)
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic

Cette approche est emergente et moins documentee dans l'écosystème Ansible/Windows. WinRM reste le standard recommande pour le provisioning Packer.