PI Services

Le blog des collaborateurs de PI Services

Powershell : WSUS Report Servers States

Voici un script qui vous permettra de réaliser un état des lieu sur les mises à jour de sécurité nécessaires pour l'ensemble de vos serveurs rattachés à un WSUS.

Attention : Prenez soins de modifier les variables suivantes :

  1. Sur la ligne 12 vous pouvez remplacer le "-30" par un valeur plus élevé si vous souhaitez un delta plus large (le -30 correspondant à M - 1, et ainsi connaitre le nombre de mise à jours manquante à J-30 jours)
  2. Sur la ligne 14 $Alias (doit correspondra à l'alias renseigné dans votre DNS).
  3. Ligne 44 vous pouvez ajouter un -Credential (si vous n'exécutez pas ce script dans un contexte avec les droits nécessaires).

 

Ce dernier est une version pour un WSUS unique en mode autonome ou déconnecté, ne possédant pas de Downstram Server, je publierais une autre version pour ce cas de figure.

#################################
# Step 1 : Variables Definition #
#################################
#region - Variables
$RootFolder = "C:\Temp\WSUS"
$ResultFolder = "$RootFolder\Result"
$LogFolder = "$RootFolder\Logs"
$Archives = "$RootFolder\Archives"
$AllFolders = $RootFolder, $ResultFolder, $LogFolder, $Archives
$LogFile = "$LogFolder\GeneralLog.log"
$Date = Get-Date -Format M
$TargetDate = ((Get-Date).AddDays(-30)).ToString('MM/dd/yyyy')
$Array = @()
$Alias = "wsus"

# Folders creation
foreach ($Folder in $AllFolders) {
    If (!(Test-Path $Folder)) {
        New-Item $Folder -ItemType Directory
    }
}

# Old Files deletion
Get-ChildItem $LogFolder | Remove-Item -Force
Get-ChildItem $ResultFolder | Remove-Item -Force
#endregion - Variables

###############################
# Step 2 : Locate WSUS Server #
###############################
#region - Locate WSUS Server
Write-Output "Trying to locate WSUS server" | Add-Content $LogFile
Try {
    $WSUSServer = Resolve-DnsName $Alias -ErrorAction Stop | select -ExpandProperty Name -Last 1
    Write-Output "Successfully locate WSUS server" | Add-Content $LogFile
}
Catch {
    Write-Output "Failed to locate WSUS server" | Add-Content $LogFile
}

# Wsus Role Check
Write-Output "Verifying WSUS server role installation" | Add-Content $LogFile
Try {
    $WsusRoleInstalled = Invoke-Command -ComputerName $WSUSServer -ScriptBlock {Get-WindowsFeature | Where-Object {($_.InstallState -eq "Installed") -and ($_.Name -like "UpdateServices")}} -ErrorAction Stop | Select-Object -Property Installed
    Write-Output "Successfully Run the query" | Add-Content $LogFile
    If ($WsusRoleInstalled.Installed -ne $true) {
        # End of Script
        Write-Output "WSUS Server not found" | Add-Content $LogFile
        Exit
    }
}
Catch {
    Write-Output "Failed to verify WSUS server role installation" | Add-Content $LogFile
    Exit
}
#endregion - Locate WSUS Server

############################
# Step 3 : WSUS Connection #
############################
#region - WSUS Connection
# Try on default port 8530
Try {
    $WsusPort = "8530"
    [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
    $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)
    $Connection = $Wsus.Name
}
Catch {
    # Try on port 80
    Try {
        $WsusPort = "80"
        [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
        $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)
        $Connection = $Wsus.Name
    }
    Catch {
        # Try on port 8531
        Try {
            $WsusPort = "8531"
            [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
            $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$true,$WsusPort)
            $Connection = $Wsus.Name
        }
        Catch {
            # Try on port 443
            Try {
                $WsusPort = "443"
                [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
                $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$true,$WsusPort)
                $Connection = $Wsus.Name
            }
            Catch {
                # End of Script
                Write-Output "WSUS Server connection failed on all ports" | Add-Content $LogFile
                Exit
            }
        }
    }
}
#endregion - WSUS Connection

###########################
# Step 4 : WSUS Reporting #
###########################
#region - WSUS Reporting
# Variables Definition
$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateClassifications = $Wsus.GetUpdateClassifications() | Where-Object {$_.Title -eq "Security Updates"}
$UpdateScope.Classifications.AddRange($updateClassifications)
$Servers = $wsus.GetComputerTargets($computerscope) | Where-Object {($_.ComputerRole -eq "Server")}
$NBServers = $Servers.Count

Write-Output "WSUS Server has $NBServers to manage" | Add-Content $LogFile
$Servers | Foreach {
    $FullDomainName = $_.FullDomainName
    $WorkstationID = $_.Id
    $LastSyncClient = $_.LastSyncTime
    [STRING]$LastSyncResultClient = $_.LastSyncResult
    $LastReportedStatusTime = $_.LastReportedStatusTime
    $IPAddress = ($_.IPAddress).IPAddressToString
    $Id = $_.Id
    $Make = $_.Make
    $Model = $_.Model
    $OSLanguage = ($_.OSInfo).DefaultUILanguage
    $OSArchitecture = $_.OSArchitecture
    $OSFamily = $_.OSFamily
    $OSDescription = $_.OSDescription
    $ComputerTargetGroupIds = $_.ComputerTargetGroupIds
    $SyncsFromDownstreamServer = $_.SyncsFromDownstreamServer
    $UpdateServer = $_.UpdateServer.Name

    # Get AD Information
    Try {
        Write-Output "Trying to retrieve Active Directory Informations for $FullDomainName" | Add-Content $LogFile
        $CurrentServer = Get-ADComputer -Filter {DNSHostName -eq $FullDomainName} -Properties CanonicalName -ErrorAction Stop | Select-Object Enabled,CanonicalName,OperatingSystem,Name
        Write-Output "Successfully retrieve Active Directory Informations for $FullDomainName" | Add-Content $LogFile
    }
    Catch {
        Write-Output "Failed to retrieve Active Directory Informations for $FullDomainName" | Add-Content $LogFile
    }
    # Define state for Begining and End of year, plus a targeted date (here M-30)
    $updatescope.ToCreationDate=[DATETIME]"01/31/2022"
    $KBToInstallJanuary = $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | where {($_.ComputerTargetId -eq $WorkstationID)} |Select-Object -ExpandProperty NotInstalledCount
    $updatescope.ToCreationDate=[DATETIME]"12/31/9999"
    $KBToInstallUpToDate = $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | where {($_.ComputerTargetId -eq $WorkstationID)} |Select-Object -ExpandProperty NotInstalledCount
    $updatescope.ToCreationDate=[DATETIME]"$TargetDate"
    $KBToInstallJ30 = $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | where {($_.ComputerTargetId -eq $WorkstationID)} |Select-Object -ExpandProperty NotInstalledCount
    $IPAddress = ($_."IPAddress").IPAddressToString
    
    $Array += New-Object psobject -Property @{
        FullDomainName = $FullDomainName
        WorkstationID = $Id
        LastSyncClient = $LastSyncClient
        LastSyncResultClient = $LastSyncResultClient
        LastReportedStatusTime = $LastReportedStatusTime
        IPAddress = $IPAddress
        Enabled = $CurrentServer.Enabled
        CanonicalName =$CurrentServer.CanonicalName
        Name = $CurrentServer.Name
        KBNeededInJanuary = $KBToInstallJanuary
        KBNeededLastMonth = $KBToInstallJ30
        KBNeededToBeUpToDate = $KBToInstallUpToDate
        Id = $Id
        Make = $Make
        Model = $Model
        OSLanguage = $OSLanguage
        OSArchitecture = $OSArchitecture
        OSFamily = $OSFamily
        OSDescription = $OSDescription
        ComputerTargetGroupIds = $ComputerTargetGroupIds
        SyncsFromDownstreamServer = $SyncsFromDownstreamServer
        UpdateServer = $UpdateServer
    }
    # Release
    $FullDomainName = $null
    $WorkstationID = $null
    $LastSyncClient = $null
    $LastSyncResultClient = $null
    $LastReportedStatusTime = $null
    $IPAddress = $null
    $Id = $null
    $Make = $null
    $Model = $null
    $OSLanguage = $null
    $OSArchitecture = $null
    $OSFamily = $null
    $OSDescription = $null
    $ComputerTargetGroupIds = $null
    $SyncsFromDownstreamServer = $null
    $CurrentServer = $null
    $UpdateServer = $null
    $KBToInstallJanuary = $null
    $KBToInstallUpToDate = $null
    $KBToInstallJ30 = $null
}
#endregion - WSUS Reporting

########################
# Step 4 : Export Data #
########################
#region - Export Data
$Array | select WorkstationID,Make,Model,OSFamily,OSDescription,OSArchitecture,OSLanguage,IPAddress,FullDomainName,Name,CanonicalName,Enabled,SyncsFromDownstreamServer,UpdateServer,LastReportedStatusTime,LastSyncClient,LastSyncResultClient,KBNeededInJanuary,KBNeededLastMonth,KBNeededToBeUpToDate | Export-Csv $ResultFolder\Report.csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

#endregion - Export Data

 

 

MECM (SCCM) : Script de maintenance WSUS pour changer wsyncmgr purge des mises à jour expirées de 7 jours à 0 jour

Microsoft Endpoint Configuration Manager est capable de supprimer automatiquement le contenu des mises à jour logicielles des points de distribution lorsque ce contenu est lié à des mises à jour expirées. Ce processus permet de gérer l'espace disque sur vos points de distribution en supprimant tout contenu dont vous n'avez plus besoin.

Si celles-ci ne se trouvent dans aucun groupe de mises à jour logicielles, ConfigMgr purgera automatiquement les mises à jour expirées après 7 jours. Vous pouvez modifier le nettoyage à 0 jour si vous souhaitez que les composants wsyncmgr suppriment immédiatement les mises à jour expirées (en supposant qu'ils ne soient pas dans un groupe de mises à jour logicielles). On appelle ce nombre de jours, âge de nettoyage des mises à jour (Updates Cleanup Age)

Ci-dessous un script VBS qui permet de changer la valeur de l'âge de nettoyage des mises à jour sur votre ConfigMgr :

On Error Resume Next

'***  Define string variables for device, device Resource ID and user of interest

Class_Name = 		"SMS_SCI_Component"
Class_ItemName =	"SMS_WSUS_SYNC_MANAGER|<<<***CHANGEME***SCCMSERVERNAMEHERE>>>.YOURDOMAIN.COM"
Class_ItemType =	"Component"
Property_Name = 	"Updates Cleanup Age"
Property_SiteCode = 	"*****SCCMSITECODEHERE*****"
DesiredValue =		0

'***  Check parameters - we need the provider server name and the site code

set args=wscript.arguments

If args.Count = 2 then
	SMSProviderServer = UCASE(Wscript.Arguments(0))
	SiteCode = UCASE(Wscript.Arguments(1))
Else
	wscript.Echo "Incorrect command line arguments." & vbCrLf & "Usage: cscript /nologo ModifySCFProperty.vbs <smsproviderserver> <sitecode>" & vbCrLf & "Example: cscript /nologo ModifySCFProperty.vbs SERVER1 S01 > schedules.txt" & vbCrLf
	WScript.Quit(1)
End If


'***  Connect to the provider - report the error and terminate on failure

SMSProviderServer = "\\" + SMSProviderServer + "\"
Set ObjSvc = GetObject("winmgmts:" & "{impersonationLevel=Impersonate,authenticationLevel=Pkt}!" & SMSProviderServer & "root\sms\site_" & SiteCode)

If Err.Number <> 0 Then
	wscript.Echo "Failed to connect to provider server with code: " & Err.Number & ".  Aborting!"
	WScript.Quit(2)
End If

'***  Get the desired instance of the class

Set objInst = ObjSvc.Get(Class_Name & ".ItemName='" & Class_ItemName & "',ItemType='" & Class_ItemType & "',SiteCode='" & Property_SiteCode &"'")

If Err.Number <> 0 Then
	WScript.Echo "Failed to open desired object with error code " & Err.Number & " (" & Err.Description & ").  Aborting!"
	WScript.Quit(3)
End If

'***  Loop through the Properties until we find a match or run out

bFoundProperty = False

For Each objProp in objInst.Props
	If objProp.PropertyName = Property_Name Then
		bFoundProperty = True
		Exit For
	End If
Next

If bFoundProperty = False Then
	WScript.Echo "Desired object was found but property was not found.  Exiting without making any changes."
	WScript.Quit(4)
End If	

'***  Property found so check to see if existing value matches desired, changing it as appropriate

If objProp.Value = DesiredValue Then
	WScript.Echo "Property '" & Property_Name & "' found with desired value '" & DesiredValue & "'.  Not making any changes."
	WScript.Quit(0)
Else
	OriginalValue = objProp.Value
	objProp.Value = DesiredValue
	objProp.Put_
	objInst.Put_

	If Err.Number <> 0 Then
		wscript.Echo "Failed to save the desired change with code: " & Err.Number & ".  Aborting!"
		WScript.Quit(5)
	Else
		WScript.Echo "Property '" & Property_Name & "' successfully changed from '" & OriginalValue & "' to '" & DesiredValue & "'."
	End If
End If

 

Sources:

https://docs.microsoft.com/en-us/troubleshoot/mem/configmgr/software-update-maintenance

https://www.reddit.com/r/SCCM/comments/ab7v6f/software_updates_remove_old_ones/

https://youtu.be/wqBaTp855sk?t=1765

https://setupconfigmgr.com/wp-content/uploads/2018/06/Adjust-WSync_UpdateCleanupAge.zip

Powershell : Supprimer les KB "Declined"

Quand on veut faire du ménage sur son WSUS et également dans la base de ce dernier, il peut s'avérer utile de supprimer les KB dont le statut est "IsDeclied".

Pour réaliser cela rien de plus simple que quelques lignes de Powershell et de la patience (beaucoup de patience si ça fait longtemps que cela n'a pas été fait).

Connexion

# Define Server Name
$WsusServer = "Mon-Server-WSUS"
# Define connection port
$WsusPort = "8530"

# Initiate Connection
[void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
$Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)

# Verifying connection (Should be return the name of the wsus server)
$Wsus.name

 

Définition des Updates à supprimer

# List All Kb in WSUS database
$AllKb = $Wsus.GetUpdates()

# Filter on "IsDeclined" status
$IsDeclined = $AllKb.Where({$_.IsDeclined -eq $True})

 

Suppression des KB

# Delete KB
$IsDeclined | foreach {
    # Define Current Id
    $Id = $_.Id
    Try {
        $Wsus.DeleteUpdate($Id.UpdateId.ToString())
        }
    Catch {
        Write-Warning $($_)
        }
    # Release variable
    $Id = $null
    }

Cette commande est un peu longue mais si elle est exécutée régulièrement, la purge dure moins longtemps.

WSUS Server : Connection Error

Vous venez de déployer un serveur WSUS, finir sa configuration et rattacher les premières machine à se dernier; mais lorsque vous vous rexonnectez sur le serveur afin d'exécuter la console "Windows Server Update Services" vous avez le droit au message d'erreur suivant :

La cause :

Bien souvent cette erreur est lié à IIS, je vous invite donc à exécuter "Internet Information Services (IIS) Manager" puis déroulez le serveur et sélectionnez "Application Pool", ici vous pourrez constater que le "WsusPool" est arrêté.

La solution :

Sélectionnez le et faite "Advanced Settings..." dans le volet de droite, sélectionnez le "Start Mode" pour le passer de "OnDemand" à "AlwaysRunning" puis validez en faisant "OK".

Démarrez le "WsusPool", puis vous pourrez fermer la console IIS.

NB : Tant que le serveur WSUS n'aura pas redémarrez la configuration du IIS ne sera pas effective (vous devrez relancez le "WsusPool" Manuellement à chaque nouvelle connexion au serveur).

Retournez dans la console "Windows Server Update Services" et faites "Reset Server Node"

Enjoy

 

Windows Update : les Servicing Stack Updates expliquées

Bonjour à tous,

Avec Windows 10, Microsoft a introduit un nouveau type de mises à jour appellées Servicing Stack Updates.

Que concernent-elles ?

Ces mises à jour concernent uniquement le composant Servicing Stack de Windows.

Le Servicing Stack est responsable de deux choses principalement au sein de l'OS Windows :

  • L'installation de mises à jour
  • L'installation de rôles ou de fonctionnalités supplémentaires via la couche CBS (qui inclue elle même les composants SFC et DISM notamment)

Comment sont-elles distribuées ?

Les mises à jour SSU ne sont pas incluses dans les mises à jour mensuelles cumulatives de Microsoft.

Elles constituent des mises à jour entièrement indépendantes et doivent donc être approuvées séparement dans WSUS afin que celles-ci puissent être déployées sur les postes concernés.

Elles seront cependant déployées automatiquement sur les postes ayant pour source Microsoft Update et non WSUS.

Attention, chaque SSU (tout comme les patchs cumulatifs mensuels), sont spécifiques à chaque version (Servicing Branch) de Windows 10 (1607, 1703, ...).

Sont-elles obligatoires ?

La réponse est oui.

Les SSU sont un prérequis pour l'installation de certaines mises à jour mensuelles cumulatives.

Le cycle des SSU est différent de celui des mises à jour mensuelles cumulatives, donc il y a des mises à jour mensuelles cumulatives sans SSU correspondante. Si une SSU est un pré-requis à une MAJ mensuelle cumulative spécificfique, elle le sera également pour les MAJ mensuelles cumulatives suivantes.

Existe t'il une liste des SSU ?

Il est à noter qu'il n'existe actuellement pas de liste officielle référençant toutes les SSU. Si une SSU est un pré-requis à l'installation d'une MAJ mensuelle cumulative, vous la trouverez mentionnée dans le KB de la mise à jour correspondante.
Vous pouvez lancer une recherche à l'URL suivante afin de trouver la dernière SSU en date.

 

Sont-elles désinstallables ?

Au contraire des autres mises à jour Microsoft, les SSU concernant un composant clé de Windows et uniquement lui seul (d'ou leur petite taille contrairement à celle des MAJ cumulatives mensuelles), ces mises à jour ne sont pas désinstallables.

Seule la restauration système peut être envisagée si vous devez vraiment faire marche arrière.

 

 

 

Gestion de WSUS en Powershell - Partie 5

Comment Supprimer des KB dans son serveurs WSUS ?

Ce qui me déplait le plus dans un Serveur WSUS c'est de voir les KB dont je n'ai pas besoin (par Exemple les mises à jour pour Serveurs Itanium).

Toutes les mises à jours non désirées, remplacées ou expirées peuvent être déclinées, mais elles restent présentes en terme d'affichage.

Voyons comment les supprimer définitivement de la "WID", sans qu'elles ne reviennent après une synchronisation avec Windows Update.

Pour ce faire il faut donc:

  1. Définir les KB que l'on souhaite supprimer (on va créer une variable).
  2. Les supprimer à l'aide de Powershell.

1. Créer sa variable

Dans mon exemple je vais supprimer les KB déclinées et les Itanium.

$KB = $Wsus.GetUpdates()
$KBToDelete = $KB | Where-Object {($_.IsDeclined -eq $True) -or ($_.LegacyName -like "*Itanium*")}

2. Supprimer les KB

Maintenant supprimons les mises à jours associées à la variable.

# Check
$KBToDelete.count

# Action
$KBToDelete | Foreach-object {
  $Id = $_."Id"
  $KnowledgebaseArticles = $_."KnowledgebaseArticles"
  Write-Output "$KnowledgebaseArticles" | Add-Content C:\Temp\KBDeleteFromWid.txt
   $Wsus.DeleteUpdate($Id.UpdateId.ToString())
   }

# New Check
$KB = $Wsus.GetUpdates()
$KBToDelete = $KB | Where-Object {($_.IsDeclined -eq $True) -or ($_.LegacyName -like "*Itanium*")}
$KBToDelete.count

Et voila, comme ça je garde une vue relativement "propre" dans mes WSUS.

Si jamais vous souhaitiez de nouveau obtenir un KB supprimée, il faudra faire un import manuel depuis le serveur WSUS.

Import Manuel de mises à jour

  1. Sur le serveur WSUS développez "Update Services".
  2. Sur "Updates" faites un clic droit "Import Updates...".
  3. Vous serez redirigé vers la pages du Catalogue Microsoft®Update.
  4. Sur le Catalog Update, il faudra rechercher le numéro de KB que vous souhaitez importer.
  5. Sélectionnez votre KB, faites « ajouter » (répétez l'opération de recherche et d'ajout au Panier autant de fois que de KB à importer), puis aller dans votre panier.
  6. Cochez "Importer directement dans Windows Server Update Services" et faites « importer ».

Gestion de WSUS en Powershell - Partie 4

1- Décliner des KB

Voici comment décliner toutes les KB de sécurité remplacées (Superseded).

# Decline Action
$KBState = $Wsus.GetStatus()
$DeclinedBefore = $KBState.DeclinedUpdateCount
$DeclineSupersed = $Wsus.GetUpdates() | Where-Object {($_.UpdateClassificationTitle -eq "Security Updates") -and ($_.IsSuperseded -eq $True)}
$DeclineSupersed | ForEach-Object -Process {$_.Decline()}
$DeclinedAfter = $KBState.DeclinedUpdateCount

Write-Host "Before we had $DeclinedBefore KB declined, and now  $DeclinedAfter"

2 - Approuver des KB

 Pour approuver des KB de sécurité non approuvées et non remplacées.

# Approve Action
$AproveKB = $Wsus.GetUpdates() | Where-Object {($_.UpdateClassificationTitle -eq "Security Updates") -and ($_.IsSuperseded -eq $false) -and ($_.IsApproved -eq $false) -and ($_.State -ne "NotNeeded")}
$MyTarget = $wsus.GetComputerTargetGroups() | Where-Object {$_.Name -eq "TEST-Workstations"}
$AproveKB[0].ApproveForOptionalInstall($MyTarget)

Attention dans l'exemple ci-dessus nous approuvons les KB "nécessaires" pour un groupe appelé "TEST-Workstations", pensez à modifier la variable "$MyTraget" avec le nom de votre ou vos groupes.

Exemple:

# Approve Action
$ApprovedKB = $Wsus.GetUpdates() | Where-Object {($_.UpdateClassificationTitle -eq "Security Updates") -and ($_.IsSuperseded -eq $false) -and ($_.IsApproved -eq $false) -and ($_.State -ne "NotNeeded")}
$MyTarget = $wsus.GetComputerTargetGroups() | Where-Object {$_.Name -eq "Prod_Servers"}
$AproveKB[0].ApproveForOptionalInstall($MyTarget)

 

Gestion de WSUS en Powershell - Partie 3

Dans cette partie nous allons voir comment utiliser la fonction Cleanup via Powershell

1 - Les Variables

La fonction "Cleanup" permet de nettoyer la base du serveur WSUS.

Il est possible de supprimer :

  • Les Superseded Updates  ou mises à jour remplacées en Français
  • Les Expired Updates ou mises à jour expirées en Français
  • Les Obsolete Updates ou mises à jour obsolètes en Français
  • Les Compress Updates ou mises à jour inutiles en Français
  • Les Obsolete Computers ou les ordinateurs obsolètes en Français
  • Les Unneeded ContentFiles ou Fichiers de mise jour inutiles en Français

Dans l'exemple ci-dessous les variables seront par défaut à "$False" il suffit de mettre "$True" pour valider la fonction.

    # Variables de Cleanup: 
# Decline updates that have not been approved for 30 days or more, are not currently needed by any clients, and are superseded by an aproved update. 
[Boolean]$SupersededUpdates = $false 
# Decline updates that aren't approved and have been expired my Microsoft. 
[Boolean]$ExpiredUpdates = $false 
# Delete updates that are expired and have not been approved for 30 days or more. 
[Boolean]$ObsoleteUpdates = $false 
# Delete older update revisions that have not been approved for 30 days or more. 
[Boolean]$CompressUpdates = $false 
# Delete computers that have not contacted the server in 30 days or more. 
[Boolean]$ObsoleteComputers = $True 
# Delete update files that aren't needed by updates or downstream servers. 
[Boolean]$UnneededContentFiles = $false 

 

2 - La commande

Une fois les variables définies, il faut indiquer le "Cleanup Scope" qui permet d'établir quels paramètres seront nettoyer, pour cela nous utiliserons la commande suivante :

$CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($supersededUpdates,$expiredUpdates,$obsoleteUpdates,$compressUpdates,$obsoleteComputers,$unneededContentFiles)

Une fois le "Cleanup Scope" définit, il ne reste plus qu'a exécuter la commande de nettoyage ci-dessous : 

($Wsus.GetCleanupManager()).PerformCleanup($CleanupScope)

Ou

$Cleanup = $Wsus.GetCleanupManager()
$Cleanup.PerformCleanup($CleanupScope)

 

3 - Bonus

Si vous possédez plusieurs serveurs WSUS, il est possible d'exécuter ce script (dans cet exemple nous ciblons uniquement les Ordinateurs obsolètes, remplacez les "$False" par "$True" pour valider les autres paramètres) :

# Script de Cleanup

$LogCatch = "$env:USERPROFILE\Desktop\LogCatch.txt"

# Détection des WSUS
Get-ADComputer -Filter { (Name -like "*WSUS*") -and (Enabled -eq $true)} | Select-Object -Property DNSHostName | Sort-Object -Property DNSHostName | ForEach-Object {
    $DNSHostName = $_."DNSHostName"
    
#region - Connexion au WSUS
        # Varibles de connexions
            $WsusServer = $DNSHostName
            $WsusPort = "8530"
        # Valeur max de prise en compte d'une machine (ici 30 jours sans connexion au serveur WSUS)
            $thirtydaysago = (get-date).adddays(-30)
            $DaysComputerStale = "30"

        #region - Ouverture de la connexion au serveur 
        $ErrorActionPreference = 'SilentlyContinue'
        Try {
            [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
            $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)
            $Wsus.Name
            $Log = $Wsus.Name
            }
        Catch {
            Write-Warning "$($WsusServer)<$($WsusPort)>: $($_)" | Add-Content -Path $LogCatch
            $Connection = "Failed"
            $finalWorkSheet.Cells.Item($FinalExcelRow,9) = $Connection
            }
            If ($Log -eq $null){
                Try {
                    $WsusPort2 = "80"
                    [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
                    $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort2)
                    $Wsus.Name
                    }
                Catch {
            Write-Warning "$($WsusServer)<$($WsusPort2)>: $($_)" | Add-Content -Path $LogCatch
                        }
                }
        $ErrorActionPreference = 'SilentlyContinue'
        #endregion - Ouverture de la connexion au serveur
#endregion - Connexion au WSUS

#region - Cleanup
    # Variables de Cleanup: 
    # Decline updates that have not been approved for 30 days or more, are not currently needed by any clients, and are superseded by an aproved update. 
    [Boolean]$supersededUpdates = $false 
    # Decline updates that aren't approved and have been expired my Microsoft. 
    [Boolean]$expiredUpdates = $false 
    # Delete updates that are expired and have not been approved for 30 days or more. 
    [Boolean]$obsoleteUpdates = $false 
    # Delete older update revisions that have not been approved for 30 days or more. 
    [Boolean]$compressUpdates = $false 
    # Delete computers that have not contacted the server in 30 days or more. 
    [Boolean]$obsoleteComputers = $True 
    # Delete update files that aren't needed by updates or downstream servers. 
    [Boolean]$unneededContentFiles = $false 

    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($supersededUpdates,$expiredUpdates,$obsoleteUpdates,$compressUpdates,$obsoleteComputers,$unneededContentFiles) 

    ($Wsus.GetCleanupManager()).PerformCleanup($CleanupScope)

#endregion - Cleanup

#region - Release des Variables
$Name = $null
$WsusPort = $null
$Wsus = $null
$Log = $null
#endregion - Release des Variables
}

La nomenclature de mes serveurs comporte "WSUS" dans le nom, je passe donc par un "Get-Adcomputer", mais vous pouvez très bien remplacer cela par un "Import-CSV".

Gestion de WSUS en Powershell - Partie 2

Dans l'article précédent nous avons vu comment nous connecter au serveur WSUS en Powershell, voyons maintenant ce que l'on peut récupérer informations.

1 - Etat de la configuration du serveur WSUS

Pour obtenir la configuration du serveur WSUS faites :

$Wsus.GetConfiguration()

Cette commande vous permet d'obtenir l'ensemble de la configuration du serveur.

Nous pouvons par exemple définir cette commande comme variable afin de pouvoir récupérer des informations de manière plus précise (car la commande retourne beaucoup d'informations).

Dans notre exemple nous allons récupérer les informations ci-dessous (pour information mon infrastructure possède un UpstreamServer) :

  • Le nom de l'UpstreamServer
  • Le port de connexion à l'UpstreamServer
  • Le serveur est il autorisé à synchroniser avec Windows Update
  • Le chemin d'accès au stockage des mise à jours
  • L'emplacement du fichier de log

J'ai variabilisé les requêtes car elles me servent plusieurs fois dans le script.

$Config = $Wsus.GetConfiguration()
$UpstreamServer = $Config.UpstreamWsusServerName
$UpstreamPort = $Config.UpstreamWsusServerPortNumber
$SyncFromMU = $Config.SyncFromMicrosoftUpdate
$StoragePath = $config.LocalContentCachePath
$LogPath = $Config.LogFilePath

$Config | Select-Object -Property UpstreamWsusServerName,UpstreamWsusServerPortNumber,SyncFromMicrosoftUpdate,LocalContentCachePath,LogFilePath

Si le serveur WSUS n'est pas le serveur en Amont (Upstream Server), donc en aval (Downstream Server) il est possible de connaitre ses paramètres via la commande :

$Wsus.GetSubscription()

Comme vous le voyez une information n'est pas retournée, ce n'est rien d'autre que les paramètre du serveur auquel vous êtes connecté, afin de connaitre ces informations rien de plus simple:

($Wsus.GetSubscription()).UpdateServer

Ou

$Subscription = $Wsus.GetSubscription()
$Subscription.UpdateServer


 

Pour obtenir des informations relatives aux mises à jour (Updates) comme : le nombre de mise à jour, combien sont approuvées, combien sont déclinées, combien sont nécessaires... utilisez la commande:

  $Wsus.GetStatus()

Après ces quelques petits exemples, vous me direz c'est bien mais pour l'instant ce n'est que de la collecte d'informations, comment puis je exploiter le serveurs alors? 

Dans la prochaine partie nous verrons comment interagir avec le serveur WSUS et lancer le nettoyage (Cleanup) du serveur.

Gestion de WSUS en Powershell - Partie 1

Gérer Windows Server Update Services c’est bien, mais on n’a pas toujours envie de se connecter à un ou plusieurs serveurs pour le faire. Et si on troquait l’interface graphique pour Powershell ?

I- Les prérequis

  • Depuis un poste client en Windows 7 :

Pour un poste en Windows 7 il faut installer le RSAT "Windows Server Update Services 3.0 SP2" disponible ici:

https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=5216

  • Depuis un serveur en Windows Server 2012 R2 :

Pour un serveur en Windows Server 2012 R2 il faut installer la fonctionnalité "Windows Server Update Services Tools"

II - Connexion au Serveur WSUS

Pour se connecter il va falloir déterminer 3 variables:

  • Le nom du serveur WSUS
  • L'utilisation ou non du SSL ($true ou $false)
  • Le port de connexion (8530, 8531, 80 ou 443)

Dans l'exemple que j'utilise je me connecterai sur le port 8530 sans SSL, d'où la présence du $false.

$WsusServer = "Mon-Server-WSUS"
$WsusPort = "8530"
[void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
$Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)

Avec ces 4 lignes vous êtes connecté au serveur WSUS, pour le vérifier rien de plus simple, appelez $Wsus

III - Cmdlet disponibles

Pour déterminer les actions possibles sur le serveur regardons les cmdlet disponibles, pour cela utilisons : 

$Wsus | gm

Comme vous pouvez le voir nous avons un grand nombre d'actions possibles.

Dans la prochaine partie nous verrons les informations que nous pouvons récolter avec quelques lignes de commandes.