TUTOS.EU

Installer automatiquement les patchs critiques de Windows Update en Vbscript

Comment télécharger et installer automatiquement les patchs critiques et de sécurité de Windows Update en VbScript

Au préalable, si vous avez besoin de paramétrer Windows Update pour :
• Ne pas authoriser la mise à jour automatique
• Rechercher les mises à jours mais laisser choisir pour téléchargement et installation
• Décocher "Recevoir les mises à jour recommandées de la même façon que vous recevez les mises à jour importantes"
• Ne pas authoriser tous les utilisateurs à installer les mises à jour (donc il faudra être admin)

Alors entrez les commandes suivantes :

REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "NoAutoUpdate" /t REG_DWORD /d 0 /f
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "AUOptions" /t REG_DWORD /d 2 /f
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "IncludeRecommendedUpdates" /t REG_DWORD /d 0 /f
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "ElevateNonAdmins" /t REG_DWORD /d 0 /f
Lien vers le fichier : cliquez ici

Pour télécharger les maj mais ne pas installer

REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "AUOptions" /t REG_DWORD /d 3 /f
Lien vers le fichier : cliquez ici

On peut donc faire un bat qui pose ces clés de registre et qui lance le .vbs situé dans le même répertoire

cd /d %~dp0

REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "NoAutoUpdate" /t REG_DWORD /d 0 /f
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "AUOptions" /t REG_DWORD /d 3 /f
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "IncludeRecommendedUpdates" /t REG_DWORD /d 0 /f
REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v "ElevateNonAdmins" /t REG_DWORD /d 0 /f

cscript.exe VbsWindowsUpdate.vbs -reboot 0
Lien vers le fichier : cliquez ici

Et une tâche planifiée déclenche le bat

Le script vbs qui télécharge les patchs critiques et de sécurité puis qui les installe

'Version du 23/02/2015 : ajout du passage de paramètres
'Version du 12/02/2015 : ajout de l'écriture d'un fichier de log

'VbScript inspiré à partir de celui-ci :
'https://msdn.microsoft.com/en-us/library/aa387102%28VS.85%29.aspx

Dim objFSO, objTextFile
Dim NomFichier, CheminFichier, CheminScriptActuel, ScriptFileName, Position
Dim ActualDay, MyDay, MyMonth, varTime
Dim WSHShell, vCOMPUTERNAME
Dim varTypeDePatchsAinstaller, varReboot

'Déclaration des constantes
Const ForReading = 1
Const ForWritting = 2
Const ForAppending = 8

varTypeDePatchsAinstaller = "" 'Valeur par défaut
varReboot = "" 'Valeur par défaut
call ParseCommand() 'Parse the command line.

MyDay = Day(Now)
MyMonth = Month(Now)

If Len(MyDay) = 1 Then MyDay = "0" & MyDay
If Len(MyMonth) = 1 Then MyMonth = "0" & MyMonth
ActualDay = Year(Now) & "-" & MyMonth & "-" & MyDay

varTime = Time
varTime = Replace(varTime, ":", "-") 'Remplacement des : par - car : est un caractère interdit dans les noms de fichiers

Set WSHShell = CreateObject("WScript.Shell")
vCOMPUTERNAME = WSHShell.ExpandEnvironmentStrings("%COMPUTERNAME%")

ScriptFileName = wscript.scriptname
Position = InstrRev(ScriptFileName,".")
if (Position > 0) Then ScriptFileName = Left(ScriptFileName, Position - 1)

NomFichier = ScriptFileName & "_Log_" & vCOMPUTERNAME & "_" & ActualDay & "_" & varTime & ".txt"
CheminScriptActuel = Left(wscript.scriptfullname,Len(wscript.scriptfullname)-Len(wscript.scriptname)-1)
CheminFichier = CheminScriptActuel & "\" & NomFichier 'Déclaration du chemin et du nom du fichier


Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile(CheminFichier, ForWritting, True)

Set updateSession = CreateObject("Microsoft.Update.Session")
Set updateSearcher = updateSession.CreateupdateSearcher()

'ServerSelection values
ssDefault = 0
ssManagedServer   = 1
ssWindowsUpdate   = 2
ssOthers          = 3


Set updateSession = CreateObject("Microsoft.Update.Session")
'updateSession.ClientApplicationID = "MSDN Sample Script"

Set updateSearcher = updateSession.CreateUpdateSearcher()
updateSearcher.ServerSelection = ssWindowsUpdat

objTextFile.WriteLine("Recherche des updates...")

'On lance une recherche sur les softs non installés non cachés (de ce que j'en comprends)
Set searchResult = updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
objTextFile.WriteLine("Recherche effectuee")

'Affichage du résultat
objTextFile.WriteLine("Liste des patchs disponibles :")
For I = 0 To searchResult.Updates.Count-1
    Set update = searchResult.Updates.Item(I)
	objTextFile.WriteLine(I + 1 & "> " & update.Title)
Next

'Si pas de résultat, arret du script
If searchResult.Updates.Count = 0 Then
	objTextFile.WriteLine("There are no applicable updates.")
    WScript.Quit
End If

'Si résultat, création d'une collection contenant les patchs à télécharger
objTextFile.WriteLine("Creation de la liste des patchs à télécharger:")
Set updatesToDownload = CreateObject("Microsoft.Update.UpdateColl")

'On scrute tous les patchs disponibles pour téléchargement puis on va faire une sélection
For I = 0 to searchResult.Updates.Count-1
	'IUpdate properties : https://msdn.microsoft.com/en-us/library/windows/desktop/aa386915%28v=vs.85%29.aspx
    Set update = searchResult.Updates.Item(I) 'Création d'un objet qui correspond à 1 update en particulier parmis ceux disponibles au téléchargement
    addThisUpdate = false 'Valeur par défaut
	
	objTextFile.WriteLine(I + 1 & ") " & update.Title) ' "Affichage du titre du patch"

	'Affichage d'infos
	objTextFile.WriteLine(VbTab & "Severity : " & update.MsrcSeverity)
	'WScript.Echo VbTab & "IsMandatory : " & update.IsMandatory
	'WScript.Echo VbTab & "DownloadPriority : " & update.DownloadPriority

	'Affichage de la catégorie
	Set objCategories = update.Categories 'On stocke dans l'objet objCategories la catégorie de l'update analysé
	strCatName = lcase(objCategories.Item(0).Name) 'On stocke dans une variable la première entrée contenu dans l'objet qui a les infos sur la catégorie

	objTextFile.WriteLine(VbTab & "Catégorie : " & strCatName)
	'If 	strCatName = "critical updates" Then addThisUpdate = True 'Si c'est un patch de sécurité, on le prend
	'If 	strCatName = "security updates" Then addThisUpdate = True 'Si c'est un patch critique, on le prend
	'If 	InStr(strCatName,"office") And 	InStr(update.description,"security") Then addThisUpdate = True 'Si c'est un patch office en relation avec la sécurité, on le prend

	
	'Si le patch demande une intervention de l'utilisateur, on l'ignore
    If update.InstallationBehavior.CanRequestUserInput = true Then
		objTextFile.WriteLine(VbTab & " Patch ignoré car demande intervention de l'utilisateur")
    Else 'Si le patch ne demande pas d'intervention de l'utilisateur

		If Lcase(varTypeDePatchsAinstaller) = "all" Then 'Si on a demandé à prendre tous les patchs alors on accepte forcément celui-ci
			addThisUpdate = true		
		Else 'Sinon on ne prend que les patchs Critical et Important
			'If update.MsrcSeverity = "Critical" Or update.MsrcSeverity = "Important" Or update.MsrcSeverity = "Moderate" Or update.MsrcSeverity = "Low" Then addThisUpdate = true
			If update.MsrcSeverity = "Critical" Or update.MsrcSeverity = "Important" Then addThisUpdate = true
		End If
		
		'Select case Lcase(varTypeDePatchsAinstaller)
			'Case "all"
				'addThisUpdate = true		
		'End Select

		'Normalement on demande au user si il veut accepter la licence. Pour des besoins d'automatisation on dit oui tout le temps
		If update.EulaAccepted = false Then
			objTextFile.WriteLine(VbTab & "Acceptation automatique du Eula pour le patch " & update.Title)
			update.AcceptEula() 'On accepte automatiquement le Eula
		End If
		
		'Si au final le patch a retenu une catégorie qui nous intéresse, on le retient pour téléchargement
		If addThisUpdate = true Then updatesToDownload.Add(update)

	End If

Next

'Si rien à télécharger, alors on quitte
If updatesToDownload.Count = 0 Then
	objTextFile.WriteLine("All applicable updates were skipped.")
    WScript.Quit
End If

objTextFile.WriteLine("")

'Listing des patchs retenus
objTextFile.WriteLine("Liste des patchs retenus :")
For I = 0 to updatestoDownload.Count-1
	 objTextFile.WriteLine(VbTab & updatestoDownload.Item(I))
	'Set objCategories = updatestoDownload.Item(I).Categories 'On stocke dans l'objet objCategories la catégorie de l'update analysé
	'strCatName = lcase(objCategories.Item(0).Name) 'On stocke dans une variable la première entrée contenu dans l'objet qui a les infos sur la catégorie
	' Wscript.echo VbTab & strCatName
Next

'Si téléchargements à faire, go
objTextFile.WriteLine("Lancement des téléchargements ...")

'Création d'un objet Downloader et assignation des updates à télécharger
Set downloader = updateSession.CreateUpdateDownloader() 
downloader.Updates = updatesToDownload
downloader.Download() 'Lancement du download



Set updatesToInstall = CreateObject("Microsoft.Update.UpdateColl") 'Création d'un objet Updates
rebootMayBeRequired = false 'Valeur par défaut. Sera ensuite à 1 si un patch demande un reboot

'On rescan l'objet Updates et on regarde ceux dispos après téléchargement. Si un patch demande reboot, on le note dans la variable rebootMayBeRequired
'Les patchs à installer sont stockés dans la collection updatesToInstall
objTextFile.WriteLine(vbCRLF & "Successfully downloaded updates:")
For I = 0 To searchResult.Updates.Count-1
    set update = searchResult.Updates.Item(I)
    If update.IsDownloaded = true Then
		objTextFile.WriteLine(VbTab & I + 1 & "> " & update.Title)
        updatesToInstall.Add(update) 
        If update.InstallationBehavior.RebootBehavior > 0 Then
            rebootMayBeRequired = true
        End If
    End If
Next

'Si pas de patch à installer, alors on arrête
If updatesToInstall.Count = 0 Then
	objTextFile.WriteLine("No updates were successfully downloaded.")
    WScript.Quit
End If

'Si on a noté que des patchs demandent reboot, on le dit
If rebootMayBeRequired = true Then
	objTextFile.WriteLine("Des patchs demandent un reboot")
End If

'On lance l'installation des updates
'Pour cela on crée l'objet "installer", on lui assigne les updates à installer qui sont stockées dans updatesToInstall puis on lance l'install
objTextFile.WriteLine("Installing updates...")
Set installer = updateSession.CreateUpdateInstaller()
installer.Updates = updatesToInstall 'Assignation des updates à installer
Set installationResult = installer.Install() 'Lancement des installations

'On affiche le résultat global des installations
objTextFile.WriteLine("Installation Result: " & installationResult.ResultCode)

'On indique si un reboot est nécessaire
objTextFile.WriteLine("Reboot Required: " & installationResult.RebootRequired)

'Affichage de la bonne installation des patchs, patch par patch
objTextFile.WriteLine("Listing of updates installed and individual installation results:" )
For I = 0 to updatesToInstall.Count - 1
	'objTextFile.WriteLine(I + 1 & "> " & updatesToInstall.Item(i).Title & ": " & installationResult.GetUpdateResult(i).ResultCode)
	objTextFile.WriteLine(I + 1 & "> " & updatesToInstall.Item(i).Title & ": " & installationResult.GetUpdateResult(i).ResultCode & ". HResult : " & installationResult.GetUpdateResult(i).HResult)
Next
		
If 1 = 2 Then
	WScript.Echo  vbCRLF & "Would you like to install updates now? (Y/N)"
	strInput = WScript.StdIn.Readline
	WScript.Echo 

	If (strInput = "Y" or strInput = "y") Then
		WScript.Echo "Installing updates..."
		Set installer = updateSession.CreateUpdateInstaller()
		installer.Updates = updatesToInstall
		Set installationResult = installer.Install()
	 
		'Output results of install
		WScript.Echo "Installation Result: " & _
		installationResult.ResultCode 
		WScript.Echo "Reboot Required: " & _ 
		installationResult.RebootRequired & vbCRLF 
		WScript.Echo "Listing of updates installed " & _
		"and individual installation results:" 
	 
		For I = 0 to updatesToInstall.Count - 1
			WScript.Echo I + 1 & "> " & _
			updatesToInstall.Item(i).Title & _
			": " & installationResult.GetUpdateResult(i).ResultCode   
		Next
	End If
End If

'Reboot
objTextFile.WriteLine("varReboot : " & varReboot)
If Cstr(varReboot) = "1" Then
	objTextFile.WriteLine("Launch reboot")
	objTextFile.Close 'Fermeture du fichier
	Commande = "Shutdown.exe -r -f -t 10 -d p:2:4"
	WSHShell.Run Commande
End If


Set WSHShell = Nothing
Set objTextFile = Nothing
Set objFSO = Nothing

Function ParseCommand()
	'
	' Parses the command line and fills the script variables 
	' with the appropriate values.
	'
	Dim ArgCount
	Dim objArgs

	Set objArgs = Wscript.Arguments

	ArgCount = 0
	if objArgs.Count = 0 then
		wscript.echo "No arguments specified."
		wscript.echo
'call Help()
	end if

	While ArgCount < objArgs.Count
		Select Case LCase(objArgs(ArgCount))
			Case "-type"
			ArgCount = ArgCount + 1
			varTypeDePatchsAinstaller=LCase(objArgs(ArgCount))
			wscript.echo "varTypeDePatchsAinstaller : " & varTypeDePatchsAinstaller

			Case "-reboot"
			ArgCount = ArgCount + 1
			varReboot=LCase(objArgs(ArgCount))
			wscript.echo "varReboot : " & varReboot
			
			Case Else:
				wscript.echo "Invalid command."
				wscript.echo
				call Help()
				wscript.quit
		End Select
		ArgCount = ArgCount + 1
	Wend
End Function

sub Help()
	'
	' Display command-line syntax for the script.
	'
	wscript.echo "Script Function details"
	wscript.echo "Syntax:"
	wscript.echo
	wscript.echo "-type ""type of patchs to install. It may be"""
	wscript.echo "Example"
	wscript.echo
	wscript.echo "Examples"
	wscript.echo "To install of kind of patchs :"
	wscript.echo "C:\MyScript.vbs -type ""all"""
	wscript.echo
	wscript.echo "To install only critical and security patchs :"
	wscript.echo "C:\MyScript.vbs"
	wscript.echo
	wscript.echo "To reboot in all cases when updates finished :"
	wscript.echo "C:\MyScript.vbs -reboot 1"
	wscript.echo
	wscript.quit
End Sub
Lien vers le fichier : cliquez ici

Juste pour trace, le code d'origine donné en exemple sur
http://ks-soft.net/cgi-bin/phpBB/viewtopic.php?t=6332&start=15&

const statusAlive = "scriptRes:Host is alive:"
const statusDead = "scriptRes:No answer:"
const statusUnknown = "scriptRes:Unknown:"
const statusNotResolved = "scriptRes:Unknown host:"
const statusOk = "scriptRes:Ok:"
const statusBad = "scriptRes:Bad:"
const statusBadContents = "scriptRes:Bad contents:"

' check this category for 'High Priority' updates
category = "UpdateClassification"
highpriority = ",Security Updates,Update Rollups,Critical Updates,"

' create instance of update.searcher (offline)
Set objSearcher = CreateObject("Microsoft.Update.Searcher")
objSearcher.Online = 1

' find and fetch collection of updates
Set objResults = objSearcher.Search("Type='Software' and IsInstalled=0")
Set colUpdates = objResults.Updates
count = 0
For i = 0 to colUpdates.Count - 1

' check categories
Set colCategories = colUpdates.Item(i).Categories
For c = 0 to colCategories.Count - 1
If colCategories.item(c).Type = category _
And InStr(highPriority, "," & colCategories.item(c).Name & ",") > 0 Then
count = count + 1
End If
Next
Next

'send results to host monitor
if count > 0 then
WScript.StdOut.Write statusBad & count & " high priority updates found!"
else
WScript.StdOut.Write statusOk & "No new high priority updates found"
End If 
Lien vers le fichier : cliquez ici

Notez que sur le post suivant on explique qu'il est préférable d'utiliser la classification au lieu de la sévérité :
http://ks-soft.net/cgi-bin/phpBB/viewtopic.php?t=6332&start=15&
sid=dcbfefcb3d35c6b2720bbcba63141528

Pages Web

Site WebDescription
MSDNSearching, Downloading, and Installing Updates
Ks-soft.net forumPost de forum avec un passage qui explique qui est préférable d'utiliser la classification au lieu de la sévérité
Ms technetExplication sur la valeur des clés de registre pour paramétrer Windosw Update
Technet.microsoft.comSecurity Bulletin Severity Rating System