Windows Server Update Services (WSUS) est un rôle de Windows Server qui permet aux administrateurs de gérer, tester et distribuer les mises à jour Microsoft au sein d'un réseau d'entreprise de manière centralisée.
Sans WSUS, chaque poste de travail et serveur télécharge ses mises à jour directement depuis les serveurs Microsoft Update sur Internet. Avec WSUS, un seul serveur télécharge les mises à jour, et tous les autres postes les récupèrent depuis ce serveur interne.
| Problème sans WSUS | Solution apportée par WSUS |
|---|---|
| Chaque machine consomme de la bande passante Internet pour les MAJ | Une seule machine télécharge, les autres utilisent le réseau local |
| Aucun contrôle sur les mises à jour installées | L'admin approuve les MAJ avant déploiement |
| Impossibilité de tester les MAJ avant déploiement massif | Déploiement par groupe (test → pilote → production) |
| Aucune visibilité sur l'état des MAJ dans l'entreprise | Rapports centralisés par machine et groupe |
| MAJ potentiellement incompatibles avec les applications métier | L'admin peut refuser ou reporter une MAJ |
┌────────────────────────┐
│ Microsoft Update │
│ (Internet) │
└──────────┬─────────────┘
│ Synchronisation
▼
┌───────────────────────┐
│ Serveur WSUS │
│ (Windows Srv 2022) │
│ Port 8530 (HTTP) │
│ Port 8531 (HTTPS) │
└──────────┬────────────┘
┌─────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌─────────────────┐
│ Groupe TEST │ │ Groupe PROD │ │ Groupe SERVEURS│
│ (5 postes) │ │ (200 postes) │ │ (30 serveurs) │
└───────────────┘ └───────────────┘ └─────────────────┘
| Composant | Minimum recommandé |
|---|---|
| OS | Windows Server 2022 (Standard ou Datacenter) |
| RAM | 4 Go (8 Go recommandés) |
| CPU | 2 cœurs |
| Disque Système | 40 Go |
| Disque MAJ | 100 Go minimum (selon nombre de produits) |
| Base de données | WID (Windows Internal Database) ou SQL Server |
| Rôle IIS | Requis (installé automatiquement) |
| .NET Framework | 4.5+ (inclus dans WS 2022) |
⚠️ Important : Placez le dossier de stockage des mises à jour sur un volume dédié, séparé du volume système.
D:\WSUS)# Installer le rôle WSUS avec la base WID et la console de gestion
Install-WindowsFeature -Name UpdateServices, UpdateServices-WidDB, UpdateServices-Services, UpdateServices-RSAT -IncludeManagementTools
# Configurer le répertoire de contenu WSUS
# (à exécuter AVANT la configuration initiale)
& "C:\Program Files\Update Services\Tools\wsusutil.exe" postinstall CONTENT_DIR="D:\WSUS"
Après l'installation, lancer l'Assistant Configuration WSUS depuis la console WSUS :
Étape 1 – Connexion en amont
Synchroniser depuis Microsoft Update
OU
Synchroniser depuis un autre serveur WSUS (upstream)
→ Spécifier : nom du serveur + port
Étape 2 – Proxy (si applicable)
# Configurer un proxy pour WSUS
$wsus = Get-WsusServer
$config = $wsus.GetConfiguration()
$config.ProxyName = "proxy.entreprise.local"
$config.ProxyServerPort = 8080
$config.UseProxy = $true
$config.Save()
Étape 3 – Langues
Sélectionner uniquement les langues nécessaires (ex : Français, Anglais) pour limiter la taille des téléchargements.
Étape 4 – Produits
Choisir les produits à mettre à jour :
⚠️ Évitez de sélectionner tous les produits — chaque produit sélectionné augmente l'espace disque requis.
Étape 5 – Classifications
| Classification | Description |
|---|---|
| Mises à jour critiques | Correctifs de sécurité urgents |
| Mises à jour de sécurité | Correctifs de vulnérabilités |
| Mises à jour | Améliorations non critiques |
| Service Packs | Cumul de correctifs |
| Feature Packs | Nouvelles fonctionnalités |
| Pilotes | Drivers matériels |
| Outils | Utilitaires Microsoft |
| Mises à jour de définitions | Signatures antivirus/antimalware |
Étape 6 – Planification de synchronisation
Synchronisation automatique : Oui
Première synchronisation : 02:00 (hors heures de bureau)
Synchronisations par jour : 1
# Lancer manuellement la première synchronisation
$wsus = Get-WsusServer
$wsus.GetSubscription().StartSynchronization()
# Surveiller la progression
$wsus.GetSubscription().GetSynchronizationProgress()
# Afficher l'état de la dernière synchronisation
$wsus.GetSubscription().GetSynchronizationHistory() | Select-Object -First 5
Les groupes permettent d'organiser les postes clients et de cibler les déploiements. La stratégie recommandée est :
Tous les ordinateurs
├── Non affectés
├── Serveurs
│ ├── Serveurs-Test
│ └── Serveurs-Production
└── Postes de travail
├── Postes-Test (pilotes)
├── Postes-Comptabilité
├── Postes-RH
└── Postes-IT
Via la console WSUS :
Via PowerShell :
# Se connecter au serveur WSUS
$wsus = Get-WsusServer -Name "WSUS-SRV" -PortNumber 8530
# Créer les groupes
$wsus.CreateComputerTargetGroup("Serveurs-Test")
$wsus.CreateComputerTargetGroup("Serveurs-Production")
$wsus.CreateComputerTargetGroup("Postes-Test")
$wsus.CreateComputerTargetGroup("Postes-Production")
# Lister tous les groupes existants
$wsus.GetComputerTargetGroups() | Select-Object Name, Id
Affecter un ordinateur à un groupe :
# Trouver un ordinateur et l'affecter à un groupe
$wsus = Get-WsusServer
$computer = $wsus.SearchComputerTargets("PC-PILOTE01")
$group = $wsus.GetComputerTargetGroups() | Where-Object { $_.Name -eq "Postes-Test" }
$computer.SetGroups(@($group))
Les postes clients doivent être redirigés vers le serveur WSUS. Deux méthodes :
Créer une GPO liée aux OU contenant les ordinateurs :
Chemin : Configuration ordinateur > Modèles d'administration > Composants Windows > Windows Update
| Paramètre GPO | Valeur |
|---|---|
| Spécifier l'emplacement intranet du service de mise à jour Microsoft | http://wsus-srv.domaine.local:8530 |
| Planifier les installations automatiques | Activé — Chaque jour à 03:00 |
| Permettre à la cible automatique côté client | Activé — Nom du groupe WSUS |
| Ne pas redémarrer automatiquement... | Activé (selon politique) |
| Fréquence de détection des mises à jour | 22 heures |
Paramètre clé — Ciblage côté client :
Configuration ordinateur > Modèles d'administration > Composants Windows >
Windows Update > Permettre à la cible automatique côté client
Valeur : <Nom du groupe WSUS>
# Script à exécuter sur le poste client
$wsusServer = "http://wsus-srv.domaine.local:8530"
$targetGroup = "Postes-Production"
# Configurer les clés de registre
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
$regPathAU = "$regPath\AU"
# Créer les clés si inexistantes
New-Item -Path $regPath -Force | Out-Null
New-Item -Path $regPathAU -Force | Out-Null
# Pointer vers le serveur WSUS
Set-ItemProperty -Path $regPath -Name "WUServer" -Value $wsusServer
Set-ItemProperty -Path $regPath -Name "WUStatusServer" -Value $wsusServer
Set-ItemProperty -Path $regPath -Name "TargetGroup" -Value $targetGroup
Set-ItemProperty -Path $regPath -Name "TargetGroupEnabled" -Value 1 -Type DWord
# Activer Windows Update automatique
Set-ItemProperty -Path $regPathAU -Name "UseWUServer" -Value 1 -Type DWord
Set-ItemProperty -Path $regPathAU -Name "AUOptions" -Value 4 -Type DWord # 4 = Télécharger et planifier
Set-ItemProperty -Path $regPathAU -Name "ScheduledInstallDay" -Value 0 -Type DWord # 0 = chaque jour
Set-ItemProperty -Path $regPathAU -Name "ScheduledInstallTime" -Value 3 -Type DWord # 3h du matin
# Forcer la détection immédiate
Start-Service -Name wuauserv
wuauclt /detectnow
wuauclt /reportnow
Write-Host "Configuration WSUS appliquée sur $env:COMPUTERNAME" -ForegroundColor Green
# Vérifier la configuration WSUS du poste
$wuServer = (Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate").WUServer
$targetGroup = (Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate").TargetGroup
Write-Host "Serveur WSUS : $wuServer"
Write-Host "Groupe cible : $targetGroup"
# Voir les mises à jour en attente
Get-WUList # Nécessite le module PSWindowsUpdate
Synchronisation WSUS
↓
Revue des MAJ disponibles
↓
Approbation → Groupe TEST (semaine 1)
↓
Validation (aucun incident)
↓
Approbation → Groupe PILOTE (semaine 2)
↓
Validation (aucun incident)
↓
Approbation → Groupe PRODUCTION (semaine 3)
$wsus = Get-WsusServer -Name "WSUS-SRV" -PortNumber 8530
# Obtenir le groupe cible
$groupTest = $wsus.GetComputerTargetGroups() | Where-Object { $_.Name -eq "Postes-Test" }
# Rechercher les MAJ critiques non approuvées
$updates = $wsus.SearchUpdates("") | Where-Object {
$_.UpdateClassificationTitle -eq "Mises à jour de sécurité" -and
$_.IsApproved -eq $false -and
$_.IsDeclined -eq $false
}
Write-Host "Mises à jour à approuver : $($updates.Count)"
# Approuver pour le groupe Test
foreach ($update in $updates) {
$update.Approve([Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install, $groupTest)
Write-Host "Approuvé : $($update.Title)"
}
$wsus = Get-WsusServer
# Refuser une mise à jour spécifique
$update = $wsus.SearchUpdates("KB5034441") | Select-Object -First 1
if ($update) {
$update.Decline()
Write-Host "Mise à jour refusée : $($update.Title)"
}
# Refuser toutes les MAJ Itanium (exemple classique)
$wsus.GetUpdates() | Where-Object { $_.Title -like "*Itanium*" } | ForEach-Object {
$_.Decline()
Write-Host "Refusé : $($_.Title)"
}
$wsus = Get-WsusServer
# Lancer l'Assistant Nettoyage du serveur
$cleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope
$cleanupScope.DeclineExpiredUpdates = $true
$cleanupScope.DeclineSupersededUpdates = $true
$cleanupScope.CleanupObsoleteUpdates = $true
$cleanupScope.CleanupUnneededContentFiles = $true
$cleanupScope.CleanupObsoleteComputers = $true
$cleanupScope.CompressUpdates = $true
$cleanupManager = $wsus.GetCleanupManager()
$results = $cleanupManager.PerformCleanup($cleanupScope)
Write-Host "Espace disque récupéré : $([math]::Round($results.DiskSpaceFreed / 1GB, 2)) Go"
Write-Host "Mises à jour obsolètes supprimées : $($results.ObsoleteUpdatesDeleted)"
Write-Host "Ordinateurs obsolètes supprimés : $($results.ObsoleteComputersDeleted)"
:: Réinitialiser la base de données WSUS
wsusutil.exe reset
:: Exporter le catalogue WSUS (environnement déconnecté)
wsusutil.exe export export.xml.cab export.log
:: Importer un catalogue exporté
wsusutil.exe import export.xml.cab import.log
:: Afficher l'aide
wsusutil.exe help
# Importer le module WSUS (si non chargé automatiquement)
Import-Module UpdateServices
# Se connecter au serveur WSUS local
$wsus = Get-WsusServer
# Se connecter à un serveur WSUS distant
$wsus = Get-WsusServer -Name "WSUS-SRV.domaine.local" -PortNumber 8530
# Connexion HTTPS
$wsus = Get-WsusServer -Name "WSUS-SRV.domaine.local" -PortNumber 8531 -UseSsl
# Obtenir les statistiques générales
$wsus.GetStatus()
# Lister les ordinateurs enregistrés
$wsus.GetComputerTargets() | Select-Object FullDomainName, IPAddress, LastSyncTime, OSDescription |
Format-Table -AutoSize
# Lister les mises à jour approuvées
$wsus.GetUpdates() | Where-Object { $_.IsApproved } |
Select-Object Title, CreationDate, IsApproved |
Sort-Object CreationDate -Descending |
Select-Object -First 20
# Option A : Certificat auto-signé (test uniquement)
$cert = New-SelfSignedCertificate `
-DnsName "wsus-srv.domaine.local" `
-CertStoreLocation "Cert:\LocalMachine\My" `
-KeyUsage DigitalSignature, KeyEncipherment `
-NotAfter (Get-Date).AddYears(3)
Write-Host "Thumbprint : $($cert.Thumbprint)"
# Option B : Certificat de l'AC interne (recommandé en production)
# Demander un certificat via l'AC Active Directory Certificate Services
Import-Module WebAdministration
# Lier le certificat au site WSUS sur le port 8531
$certThumbprint = "VOTRE_THUMBPRINT_ICI"
$binding = Get-WebBinding -Name "WSUS Administration" -Protocol "https" -ErrorAction SilentlyContinue
if (-not $binding) {
New-WebBinding -Name "WSUS Administration" -Protocol https -Port 8531 -HostHeader ""
}
# Associer le certificat
$cert = Get-ChildItem "Cert:\LocalMachine\My" | Where-Object { $_.Thumbprint -eq $certThumbprint }
(Get-WebBinding -Name "WSUS Administration" -Protocol "https").AddSslCertificate($cert.Thumbprint, "My")
# Activer SSL sur WSUS (chemins IIS à sécuriser)
& "C:\Program Files\Update Services\Tools\WsusUtil.exe" configuressl wsus-srv.domaine.local
# Configurer le serveur WSUS pour utiliser SSL
$wsus = Get-WsusServer -Name "wsus-srv" -PortNumber 8530
$config = $wsus.GetConfiguration()
$config.MUUrl = "https://update.microsoft.com"
$config.RedundantPackages = $false
$config.Save()
Modifier la GPO pour pointer en HTTPS :
WUServer = https://wsus-srv.domaine.local:8531
WUStatusServer = https://wsus-srv.domaine.local:8531
$wsus = Get-WsusServer
# Activer la classification "Mises à niveau"
$subscription = $wsus.GetSubscription()
$config = $wsus.GetConfiguration()
# Vérifier les classifications actives
$subscription.GetUpdateClassifications() | Select-Object Title
# Ajouter la classification Upgrades si manquante
$allClassifications = $wsus.GetUpdateClassifications()
$upgradesClass = $allClassifications | Where-Object { $_.Title -eq "Upgrades" }
if ($upgradesClass) {
$currentClassifications = $subscription.GetUpdateClassifications()
if ($upgradesClass.Id -notin $currentClassifications.Id) {
$subscription.SetUpdateClassifications($currentClassifications + $upgradesClass)
$subscription.Save()
Write-Host "Classification 'Upgrades' ajoutée." -ForegroundColor Green
}
}
Configuration ordinateur > Modèles d'administration >
Composants Windows > Windows Update > Gérer les mises à jour proposées par Windows Update
Paramètre : "Sélectionner la version cible de mise à jour de Windows"
Valeur : Windows 11 + Version : 23H2
# Via le registre sur les clients (exemple : rester sur Windows 11 22H2)
$regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
Set-ItemProperty -Path $regPath -Name "TargetReleaseVersion" -Value 1 -Type DWord
Set-ItemProperty -Path $regPath -Name "TargetReleaseVersionInfo" -Value "22H2" -Type String
Set-ItemProperty -Path $regPath -Name "ProductVersion" -Value "Windows 11" -Type String
| Rapport | Description |
|---|---|
| Récapitulatif des mises à jour | Vue globale de toutes les MAJ par état |
| État des mises à jour | MAJ par ordinateur et groupe |
| État des ordinateurs | Conformité par machine |
| Ordinateurs non contactés | Postes sans communication depuis X jours |
| Synchronisation | Historique et résultats des synchronisations |
$wsus = Get-WsusServer -Name "WSUS-SRV" -PortNumber 8530
# ─── Rapport 1 : Ordinateurs non à jour ───────────────────────────────────────
Write-Host "`n=== ORDINATEURS NON CONFORMES ===" -ForegroundColor Yellow
$scope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$wsus.GetComputerTargets($scope) | ForEach-Object {
$computer = $_
$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateScope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::NotInstalled
$pendingUpdates = $computer.GetUpdateInstallationInfoPerUpdate($updateScope)
if ($pendingUpdates.Count -gt 0) {
[PSCustomObject]@{
Ordinateur = $computer.FullDomainName
MàJManquantes = $pendingUpdates.Count
DernierContact = $computer.LastSyncTime
}
}
} | Format-Table -AutoSize
# ─── Rapport 2 : Résumé par groupe ────────────────────────────────────────────
Write-Host "`n=== RÉSUMÉ PAR GROUPE ===" -ForegroundColor Yellow
$wsus.GetComputerTargetGroups() | Where-Object { $_.Name -ne "All Computers" } | ForEach-Object {
$group = $_
$computers = $group.GetComputerTargets()
[PSCustomObject]@{
Groupe = $group.Name
Ordinateurs = $computers.Count
}
} | Format-Table -AutoSize
# ─── Rapport 3 : Top 10 des MAJ manquantes ────────────────────────────────────
Write-Host "`n=== TOP 10 MAJ LES PLUS MANQUANTES ===" -ForegroundColor Yellow
$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateScope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::NotInstalled
$wsus.GetUpdates($updateScope) | Where-Object { $_.IsApproved } |
Select-Object Title, @{N="Machines"; E={ ($_.GetUpdateInstallationInfoPerComputerTarget() | Measure-Object).Count }} |
Sort-Object Machines -Descending |
Select-Object -First 10 |
Format-Table -AutoSize
# ─── Rapport 4 : Conformité globale ───────────────────────────────────────────
Write-Host "`n=== TAUX DE CONFORMITÉ GLOBAL ===" -ForegroundColor Yellow
$allComputers = ($wsus.GetComputerTargets()).Count
$compliantComputers = 0
$wsus.GetComputerTargets() | ForEach-Object {
$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateScope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::NotInstalled
$pending = $_.GetUpdateInstallationInfoPerUpdate($updateScope)
if ($pending.Count -eq 0) { $compliantComputers++ }
}
$conformite = [math]::Round(($compliantComputers / $allComputers) * 100, 1)
Write-Host "Ordinateurs conformes : $compliantComputers / $allComputers ($conformite%)" -ForegroundColor $(if ($conformite -ge 90) { "Green" } else { "Red" })
$wsus = Get-WsusServer
$rapport = $wsus.GetComputerTargets() | ForEach-Object {
$computer = $_
$notInstalled = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$notInstalled.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::NotInstalled
$failed = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$failed.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::Failed
[PSCustomObject]@{
Ordinateur = $computer.FullDomainName
Adresse_IP = $computer.IPAddress
OS = $computer.OSDescription
DernierContact = $computer.LastSyncTime
MàJ_Manquantes = ($computer.GetUpdateInstallationInfoPerUpdate($notInstalled)).Count
MàJ_Échouées = ($computer.GetUpdateInstallationInfoPerUpdate($failed)).Count
}
}
$rapport | Export-Csv -Path "C:\Rapports\WSUS_Conformite_$(Get-Date -Format 'yyyyMMdd').csv" `
-NoTypeInformation -Encoding UTF8 -Delimiter ";"
Write-Host "Rapport exporté." -ForegroundColor Green
# ── Connexion ──────────────────────────────────────────────────────────────────
$wsus = Get-WsusServer # Serveur local
$wsus = Get-WsusServer -Name "SRV" -PortNumber 8530 # Serveur distant
# ── Synchronisation ────────────────────────────────────────────────────────────
$wsus.GetSubscription().StartSynchronization() # Démarrer
$wsus.GetSubscription().GetSynchronizationProgress() # Progression
$wsus.GetSubscription().GetSynchronizationHistory() # Historique
# ── Groupes ────────────────────────────────────────────────────────────────────
$wsus.GetComputerTargetGroups() # Lister
$wsus.CreateComputerTargetGroup("NomGroupe") # Créer
# ── Ordinateurs ────────────────────────────────────────────────────────────────
$wsus.GetComputerTargets() # Tous
$wsus.SearchComputerTargets("NomPC") # Rechercher
# ── Mises à jour ───────────────────────────────────────────────────────────────
$wsus.GetUpdates() # Toutes
$wsus.SearchUpdates("KB5034441") # Par KB
$update.Approve([...UpdateApprovalAction]::Install, $group) # Approuver
$update.Decline() # Refuser
# ── Nettoyage ──────────────────────────────────────────────────────────────────
$wsus.GetCleanupManager().PerformCleanup($scope) # Nettoyage
# ── Statistiques ───────────────────────────────────────────────────────────────
$wsus.GetStatus() # État global
| ✅ À faire | ❌ À éviter |
|---|---|
| Tester les MAJ sur un groupe pilote avant la prod | Approuver les MAJ directement en production |
| Planifier la synchronisation hors heures de bureau | Synchroniser pendant les heures de pointe |
| Effectuer le nettoyage WSUS chaque semaine | Laisser s'accumuler les MAJ obsolètes |
| Activer SSL en production | Utiliser HTTP sur un réseau non de confiance |
| Sauvegarder la base WID régulièrement | Négliger la sauvegarde de la base WSUS |
| Surveiller l'espace disque du dossier de contenu | Laisser le disque se remplir |
| Documenter les MAJ refusées et leurs raisons | Refuser des MAJ sans documentation |
| Planifier des fenêtres de maintenance avec redémarrage | Laisser des postes sans redémarrage pendant des semaines |
*Cours rédigé pour le Cégep Ahuntsic par son excellence Pablo The God