Le diagnostic des problèmes de Windows Update peut être une tâche fastidieuse pour les professionnels de l’informatique, en particulier lorsqu’ils gèrent plusieurs machines. Les retards ou les erreurs dans le processus de mise à jour peuvent entraîner des failles de sécurité, des problèmes de conformité et une instabilité générale du système.
Le script PowerShell fourni offre une approche rationalisée pour diagnostiquer et résoudre les problèmes de Windows Update courants, garantissant ainsi que les systèmes restent à jour et sécurisés. Cet article de blog se penche sur les fonctionnalités du script, explorant comment il peut être un outil inestimable pour les professionnels de l’informatique et les fournisseurs de services gérés (MSP).
Contexte
Windows Update est un élément essentiel du maintien de la santé et de la sécurité d’un système Windows. Cependant, divers facteurs peuvent entraver son bon fonctionnement, qu’il s’agisse de mauvaises configurations de services ou de problèmes de réseau. Les professionnels de l’informatique sont souvent confrontés à la tâche ardue de résoudre ces problèmes manuellement, ce qui peut prendre beaucoup de temps et être source d’erreurs humaines.
Le script PowerShell fourni relève ce défi en automatisant le processus de diagnostic, ce qui permet d’identifier et de résoudre rapidement les problèmes courants. En utilisant ce script, les équipes informatiques peuvent maintenir l’intégrité du système, réduire les temps d’arrêt et améliorer l’efficacité globale.
Le script :
#Requires -Version 5.1 <# .SYNOPSIS Diagnose Windows Update issues. .DESCRIPTION Checks that CryptSvc, and bits or running or not Checks that wuauserv is running and the startup type is set correctly. Checks WaaSMedic plugins doesn't have issues. (Only applies to OS Build Version is greater than 17600). Checks if NTP is setup. Checks Windows Update logs for any errors in the last week. .EXAMPLE (No Parameters) ## EXAMPLE OUTPUT WITHOUT PARAMS ## [Info] Last checked for updates on 4/29/2023 [Issue] Windows Update has not checked for updates in over 30 days. PARAMETER: -ResultsCustomField WindowsUpdate Saves results to a multi-line custom field. .EXAMPLE -ResultsCustomField WindowsUpdate ## EXAMPLE OUTPUT WITH ResultsCustomField ## [Info] Last checked for updates on 4/29/2023 [Issue] Windows Update has not checked for updates in over 90 days. .OUTPUTS None .NOTES Minimum OS Architecture Supported: Windows 10, Windows Server 2016 Release Notes: Initial Release By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://ninjastage2.wpengine.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). #> [CmdletBinding()] param ( [int]$Days = 30, [string]$ResultsCustomField ) begin { if ($env:Days) { $Days = $env:Days } if ($env:resultscustomfield -notlike "null") { $ResultsCustomField = $env:resultscustomfield } function Test-IsElevated { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) } function Test-WaaSMedic { [CmdletBinding()] param() $WaaS = 0 Try { $WaaS = New-Object -ComObject "Microsoft.WaaSMedic.1" } Catch { Write-Host "WaaS Medic Support: No" } Try { if ($WaaS -ne 0) { Write-Host "WaaS Medic Support: Yes" $Plugins = $WaaS.LaunchDetectionOnly("Troubleshooter") if ($Plugins -eq "") { [PSCustomObject]@{ Id = "WaaSMedic" Detected = $false Parameter = @{"error" = $Plugins } } } else { [PSCustomObject]@{ Id = "WaaSMedic" Detected = $true Parameter = @{"error" = $Plugins } } "Plugins that might have errors: " + $Plugins | Out-String | Write-Host } } } Catch { Write-Host "WaaS Medic Detection: Failed" } Finally { # Release COM Object if we aren't running test cases if (-not $env:NinjaPesterTesting) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WaaS) | Out-Null } } } function Get-TimeSyncType { [string]$result = "" [string]$registryKey = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" [string]$registryKeyName = "Type" if ((Test-Path $registryKey -ErrorAction SilentlyContinue)) { $registryEntry = Get-Item -Path $registryKey -ErrorAction SilentlyContinue if ($null -ne $registryEntry) { return Get-ItemPropertyValue -Path $registryKey -Name $registryKeyName } } return $result } function Test-ConnectedToInternet { $NLMType = [Type]::GetTypeFromCLSID('DCB00C01-570F-4A9B-8D69-199FDBA5723B') $INetworkListManager = [Activator]::CreateInstance($NLMType) return ($INetworkListManager.IsConnectedToInternet -eq $true) } function Get-ComponentAndErrorCode([string]$msg) { $Codes = [regex]::matches($msg, "0x[a-f0-9a-f0-9A-F0-9A-F0-9]{6,8}") if ($Codes.count -gt 1) { $CodeList = "" # there can be more than one error code can be returned for the same component at once foreach ($Code in $Codes) { $CodeList += "_" + $Code } return $CodeList } else { return $Codes[0].Value } } function Get-DatedEvents($EventLog) { $DatedEvents = @() if ($null -eq $EventLog) { return $null } foreach ($Event in $EventLog) { #$eventMsg = $event.Message $DatedEvents += $Event.Message } return $DatedEvents } function Get-SystemEvents($EventSrc, $Time) { $Events = Get-WinEvent -ProviderName $EventsSrc -ErrorAction 0 | Where-Object { ($_.LevelDisplayName -ne "Information") -and (($_.Id -eq 20) -or ($_.Id -eq 25)) -and ($_.TimeCreated -gt $Time) } return $Events } function Get-HasWinUpdateErrorInLastWeek([switch]$AllLastWeekError) { $Events = @() $EventsSrc = "Microsoft-Windows-WindowsUpdateClient" $startTime = (Get-Date) - (New-TimeSpan -Day 8) $wuEvents = Get-SystemEvents $EventsSrc $startTime if ($null -eq $wuEvents) { return $null } $Events += Get-DatedEvents $wuEvents $LatestError = Get-ComponentAndErrorCode $Events[0] $ErrorList = @{} $ErrorList.add("latest", $LatestError) if ($AllLastWeekError) { foreach ($str in $Events) { $ECode = Get-ComponentAndErrorCode $str if ($null -ne $ECode -and !$ErrorList.ContainsValue($ECode)) { $ErrorList.add($ECode, $ECode) } } } return $ErrorList } Function Get-LocalTime($UTCTime) { $strCurrentTimeZone = (Get-CimInstance -ClassName Win32_TimeZone).StandardName # If running test cases return current date if ($env:NinjaPesterTesting) { return Get-Date } $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone) Return [System.TimeZoneInfo]::ConvertTimeFromUtc($UTCTime, $TZ) } $IssuesFound = $false $Log = [System.Collections.Generic.List[String]]::new() } process { if (-not (Test-IsElevated)) { Write-Error -Message "Access Denied. Please run with Administrator privileges." exit 1 } if (-not $(Test-ConnectedToInternet)) { Write-Host "[Issue] Windows doesn't think it is connected to Internet." $IssuesFound = $true } # Check CryptSvc amd bits services $Service = Get-Service -Name CryptSvc if ($Service.StartType -notlike 'Automatic') { Write-Host "[Issue] (CryptSvc) CryptSvc service is set to $($Service.StartType) but needs to be set to Automatic" $Log.Add("[Issue] (CryptSvc) CryptSvc service is set to $($Service.StartType) but needs to be set to Automatic") $IssuesFound = $true } else { Write-Host "[Info] (CryptSvc) CryptSvc service is set to $($Service.StartType)" $Log.Add("[Info] (CryptSvc) CryptSvc service is set to $($Service.StartType)") } $Service = Get-Service -Name bits if ($Service.StartType -eq 'Disabled') { Write-Host "[Issue] (bits) BITS service is set to $($Service.StartType) but needs to be set to Manual" $Log.Add("[Issue] (bits) BITS service is set to $($Service.StartType) but needs to be set to Manual") $IssuesFound = $true } else { Write-Host "[Info] (bits) BITS service is set to $($Service.StartType)" $Log.Add("[Info] (bits) BITS service is set to $($Service.StartType)") } # Check that Windows Update service is running and isn't disabled $wuService = Get-Service -Name wuauserv -ErrorAction SilentlyContinue if ($wuService.Status -ne "Running") { $Service = Get-Service -Name wuauserv if ($Service.StartType -eq 'Disabled') { Write-Host "[Issue] (wuauserv) Windows Update service is set to $($Service.StartType) but needs to be set to Automatic (Trigger Start) or Manual" $Log.Add("[Issue] (wuauserv) Windows Update service is set to $($Service.StartType) but needs to be set to Automatic (Trigger Start) or Manual") $IssuesFound = $true } else { Write-Host "[Info] (wuauserv) Windows Update service is set to $($Service.StartType)" $Log.Add("[Info] (wuauserv) Windows Update service is set to $($Service.StartType)") } } # Check WaaSMedic $SupportWaaSMedic = [System.Environment]::OSVersion.Version.Build -gt 17600 if ($SupportWaaSMedic) { $Plugins = Test-WaaSMedic $PluginIssues = $Plugins | Where-Object { $_.Parameter["error"] } | ForEach-Object { $PluginErrors = $_.Parameter["error"] "[Potential Issue] WaaSMedic plugin errors found with: $($PluginErrors)" } if ($PluginIssues.Count -gt 1) { Write-Host "[Issue] Found more than 1 plugin errors." $Log.Add("[Issue] Found more than 1 plugin errors.") $PluginIssues | Write-Host $IssuesFound = $true } } # Check if NTP is setup if ("NoSync" -eq (Get-TimeSyncType)) { Write-Host "[Issue] NTP not setup!" $Log.Add("[Issue] NTP not setup!") $IssuesFound = $true } # Check Windows Update logs $EventErrors = Get-HasWinUpdateErrorInLastWeek -AllLastWeekError if ($EventErrors.Count -gt 0) { if (![string]::IsNullOrEmpty($allError.Values)) { Write-Host "[Issue] Event Log has Windows Update errors." $Log.Add("[Issue] Event Log has Windows Update errors.") $errorCodes = $allError.Values -join ';' Write-Host "[Issue] Error codes found: $errorCodes" $Log.Add("[Issue] Error codes found: $errorCodes") $IssuesFound = $true } } # If no issues found, get number of days since the last check for updates happened if (-not $IssuesFound) { $LastCheck = Get-LocalTime $(New-Object -ComObject Microsoft.Update.AutoUpdate).Results.LastSearchSuccessDate Write-Host "[Info] Last checked for updates on $($LastCheck.ToShortDateString())" $Log.Add("[Info] Last checked for updates on $($LastCheck.ToShortDateString())") $LastCheckTimeSpan = New-TimeSpan -Start $LastCheck -End $(Get-Date) if ($LastCheckTimeSpan.TotalDays -gt $Days) { $Days = [System.Math]::Round($LastCheckTimeSpan.TotalDays, 0) Write-Host "[Issue] Windows Update has not checked for updates in over $Days days." $Log.Add("[Issue] Windows Update has not checked for updates in over $Days days.") $IssuesFound = $true } } if ($ResultsCustomField) { Ninja-Property-Set -Name $ResultsCustomField -Value $($Log | Out-String) } if ($IssuesFound) { exit 1 } exit 0 } end { }
Description détaillée
Le script fonctionne en effectuant une série de vérifications sur les composants et services clés de Windows Update, chacun étant essentiel au bon fonctionnement du processus de mise à jour. Voici une description étape par étape du fonctionnement du script :
1. Vérification de l’élévation : Le script commence par vérifier qu’il est exécuté avec les privilèges d’administrateur, qui sont nécessaires pour modifier les services du système et accéder à des journaux spécifiques.
2. Test de connectivité Internet: Il vérifie si le système est connecté à l’internet, ce qui est indispensable pour télécharger les mises à jour.
3. Contrôles d’état des services:
- Cryptographic Services (CryptSvc): Assure que les services cryptographiques sont réglés sur « Automatique », une configuration nécessaire pour traiter les fichiers de mise à jour en toute sécurité.
- Background Intelligent Transfer Service (BITS): Vérifie que le BITS n’est pas désactivé, car il est responsable du transfert de fichiers en arrière-plan, y compris les mises à jour.
- Windows Update Service (wuauserv): Confirme que le service Windows Update est en cours d’exécution et qu’il est défini sur le type de démarrage correct.
4. WaaSMedic Check: Pour les systèmes dont la version est supérieure à 17600, le script vérifie les plugins WaaSMedic, qui sont chargés de corriger automatiquement les problèmes liés à la mise à jour.
5. Configuration NTP: Le script vérifie si le protocole NTP (Network Time Protocol) est correctement configuré, ce qui garantit que l’horloge du système est synchronisée avec une source de temps externe – un facteur crucial pour le processus de mise à jour.
6. Analyse du journal des événements: Il examine les journaux d’événements de Windows Update à la recherche de toute erreur enregistrée au cours de la semaine écoulée, en identifiant les codes d’erreur spécifiques susceptibles d’indiquer des problèmes sous-jacents.
7. Vérification de la dernière mise à jour: Enfin, le script détermine la date de la dernière vérification des mises à jour du système. Si ce délai dépasse le seuil défini par l’utilisateur (30 jours par défaut), le problème est signalé.
Chacune de ces vérifications est enregistrée et, si un problème est détecté, il est signalé dans un résumé qui peut être enregistré dans un champ personnalisé pour une analyse plus approfondie.
Cas d’utilisation potentiels
Imaginez un professionnel de l’informatique qui gère un parc de postes de travail pour une grande entreprise. Un jour, plusieurs utilisateurs signalent que leurs systèmes n’ont pas reçu de mises à jour depuis plusieurs semaines. Plutôt que de vérifier manuellement chaque système, l’informaticien déploie ce script sur tous les postes de travail.
Le script identifie que le service Windows Update est mal configuré sur plusieurs machines et que le BITS est désactivé sur d’autres. Elle souligne également que certains systèmes n’ont pas fait l’objet d’une vérification des mises à jour depuis plus de 60 jours.
Grâce à ces informations, le professionnel de l’informatique peut rapidement rectifier les problèmes, en veillant à ce que tous les systèmes soient mis à jour, en minimisant les risques de sécurité et en maintenant la conformité avec les politiques de l’entreprise.
Comparaisons
Ce script PowerShell offre une approche plus automatisée et plus complète que les méthodes traditionnelles, telles que la vérification manuelle de l’état des services ou l’analyse des journaux d’événements.
Si les outils basés sur une interface graphique, comme le Windows Update Troubleshooter, peuvent résoudre certains problèmes, ils sont souvent insuffisants pour fournir des informations détaillées ou pour gérer plusieurs machines simultanément.
Ce script, quant à lui, ne se contente pas d’identifier les problèmes, il offre également des informations claires et exploitables, ce qui en fait une option supérieure pour les environnements informatiques à grande échelle.
FAQ
1. Ce script peut-il résoudre les problèmes qu’il détecte ?
- Non, ce script est conçu pour diagnostiquer et signaler les problèmes. Cependant, il fournit suffisamment d’informations pour que les professionnels de l’informatique puissent prendre les mesures nécessaires pour résoudre les problèmes manuellement.
2. Ce script est-il compatible avec toutes les versions de Windows ?
- Le script prend en charge Windows 10 et Windows Server 2016 ou les versions ultérieures, ce qui garantit une large applicabilité dans les environnements Windows modernes.
3. Que dois-je faire si le script signale une erreur avec les plugins WaaSMedic ?
- Les problèmes liés à la WaaSMedic nécessitent généralement une intervention manuelle. Il se peut que vous deviez réinitialiser le service WaaSMedic ou utiliser des outils supplémentaires pour résoudre les erreurs spécifiques au plugin.
Implications
Les résultats de ce script peuvent avoir des conséquences importantes pour la sécurité informatique. L’identification et la résolution rapide des problèmes de Windows Update peuvent empêcher l’exploitation des vulnérabilités non corrigées, réduisant ainsi le risque de cyber-attaques.
De plus, le fait de veiller à ce que les mises à jour soient appliquées de manière cohérente contribue à maintenir la stabilité du système, évitant ainsi des temps d’arrêt imprévus qui pourraient perturber les activités de l’entreprise.
Recommandations
Lors de l’utilisation de ce script, il est recommandé de :
- Le faire fonctionner régulièrement: Intégrez-le dans votre programme de maintenance de routine pour vous assurer que les problèmes liés aux mises à jour sont détectés à temps.
- Analyser attentivement les journaux: Prêtez attention aux détails des journaux générés par le script, car ils peuvent fournir des informations essentielles sur les problèmes récurrents.
- Intégrer les outils d’automatisation: Pour les environnements à grande échelle, envisagez d’intégrer ce script à des plateformes d’automatisation telles que NinjaOne afin de rationaliser le processus de diagnostic sur plusieurs systèmes.
Conclusion
NinjaOne offre une plateforme puissante qui complète les fonctionnalités de ce script. En intégrant le script dans les flux de travail automatisés de NinjaOne, les professionnels de l’informatique peuvent améliorer leur capacité à diagnostiquer et à résoudre les problèmes de mise à jour de Windows sur de nombreuses machines simultanément.
Cette intégration permet non seulement de gagner du temps, mais aussi de garantir que tous les systèmes restent sécurisés et à jour, ce qui contribue en fin de compte à une infrastructure informatique plus stable et plus résistante.