#目的
Windows Updateを完全自動化します。
#環境
OS : Windows Vista 以降
PowerShell: Version 2 以降
#使用方法
ソース中に記載(英語版のみです。悪しからず。)
#備考
- Windows10のCreaters Updateなどの大型アップデートについては未検証です。
- Windows Updateが「更新プログラムを自動的にインストールする」設定になっている場合、競合が発生してエラーとなる場合があります。
- コンソールウィンドウの幅に合わせて更新のタイトルを切り詰めます。ウィンドウ幅をなるべく広げて実行してください。タイトルの切り詰めを無効にするにはFormatTitle関数の1行目のコメントをはずしてください。
#コード
PowerShellスクリプトですが、Windows バッチファイルとしても実行できる特殊な構造になっています。
(http://earthdiver.net/download/WUA.zip)
WUA.bat
<# : Batch Commands (PowerShell Comments) Start
@echo off & setlocal
rem
rem Windows Update Automation
rem
rem
rem SYNOPSIS 1: Execute the following command from a console started as admin.
rem
rem WUA.bat [<password>] [<service_type>] [<feature_update>] [<important_updates_only>]
rem [-ExcludeList/Exclude/EL <exclude_list *1>]
rem [-KBArticleIDs/KBs/IDs <include_list *1>]
rem [-NonInteractive/NI *2]
rem *1 comma-delimited list of KBArticle IDs
rem *2 switch for non-interactive mode
rem
rem <service_type> 0:*default, 1:WSUS, 2:Windows Update, 3:Microsoft Update
rem <feature_update> 0:*exclude, 1:include (service pack is also included.)
rem <important_updates_only> 0:include preferred updates
rem 1:*important updates only
rem
rem * default for non-interactive mode
rem
rem If no command arguments are specified, you will be prompted for
rem <password> and <service_type>.
rem
rem Examples (non-interactive mode)
rem 1. WUA.bat -NI <password> 3
rem 2. WUA.bat -NI @@@@@ 3 (in case of credential information
rem being stored on disk)
rem
rem
rem
rem SYNOPSIS 2: Right click the WUA.bat icon and select "Run as Administrator".
rem You will be prompted for password, service_type, etc.
rem
rem
rem
rem NOTES: -Administrator rights are required for execution of this script.
rem -Your computer will be automatically restarted as needed.
rem -A log file will be generated in the same folder as this script.
rem The log file generation is suppressed if $LogDir is blank.
rem -'SavedCredential.dat' will be generated in the same folder as this script
rem in order to store the credential information for later use.
rem -The first command argument (password) is required in non-interactive mode
rem (unless a file containing the credential information has been generated).
rem To specify the second argument (service_id) without explicitly specifying
rem password, '@@@@@' may be used in place of the password.
rem -If the same IDs are specified in both ExcludeList and KBArticleIDs,
rem the corresponding updates are excluded.
rem -KBArticleIDs may be input from pipe(stdin).
rem -Files with the following names will be executed at the end if exists.
rem '<basename of this script>_PP.XXX'
rem where XXX is either one of BAT, PS1, or VBS.
rem
rem Created by earthdiver1 (inspired by https://gist.github.com/ijprest/d55c3754650cd81df3a5)
rem Version 1.17
rem Licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
rem
rem -------------------------------------------------------------------------------
rem The following is a preamble for converting a PowerShell script into polyglot
rem one that also runs as a batch script. Change the file extension to .ps1 and
rem run it from PowerShell console when debugging.
set BATCH_ARGS=%*
if defined BATCH_ARGS set BATCH_ARGS=%BATCH_ARGS:"=\"%
set P_CMD=$PSCP='%~f0';$input^|^&([ScriptBlock]::Create((${%~f0}^|Out-String)))
endlocal & Powershell -NoProfile -Command "%P_CMD%" %BATCH_ARGS%
exit/b
rem -------------------------------------------------------------------------------
: Batch Commands (PowerShell Comments) End #>
param (
[Parameter(Position=0)]
[String]$Password = "@@@@@",
[Parameter(Position=1)]
[Int]$Server = -1, # the default value may be altered
[Parameter(Position=2)]
[Int]$SPack = -1, # the default value may be altered
[Parameter(Position=3)]
[Int]$AutoSelect = -1, # the default value may be altered
[Alias("EL","Exclude")]
[ValidateNotNullOrEmpty()]
[ValidateLength(8,9)]
[ValidatePattern("KB[0-9]+")]
[String[]]$ExcludeList, # the default value may be specified
[Alias("KBs","IDs")]
[ValidateNotNullOrEmpty()]
[ValidateLength(8,9)]
[ValidatePattern("KB[0-9]+")]
[String[]]$KBArticleIDs = @($input),
[Alias("NI")]
[Switch]$NonInteractive,
[Switch]$Resumed, # for internal control
[Alias("NL")]
[Int]$NotificationLevel # for internal control
)
$script:PSCommandPath = $PSCP
$ThisScript = $MyInvocation.MyCommand.Path # the path to this script (when invoked as .ps1 file)
if (-not $ThisScript) {
$ThisScript = $script:PSCommandPath # the path to this script (when invoked as scriptblock)
}
$script:PSScriptRoot = Split-Path $ThisScript -Parent # folder path in which this script is located
# uncomment the following line to prevent line breaks.
#$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size(512, $host.UI.RawUI.BufferSize.Height)
############### EDIT HERE ################
$InteractiveMode = 1 # interactive mode flag 0:OFF 1:ON
#$LogDir = "" # suppress log file generation
#$LogDir = $env:TEMP # %TEMP%
$LogDir = $script:PSScriptRoot # same folder as this script
#$LogFilename = "WUA_$((Get-Date).ToString('yyyyMMddHHmm')).log" # log file name (with date-time)
$LogFilename = "WUA.log" # log file name (fixed)
$MaxUpdatesToInstall = 100 # maximum number of updates to be processed at a time
$CreateRestorePoint = $False # switch for restore point creation
# (It seems like that a restore point is created automatically
# when updates exist. So maybe this is for paranoia only.)
$MinimumRestorePointInterval = 60 # minimum interval for restore point creation [min]
##########################################
Function Main() {
StartTranscript
try {
if ($KBArticleIDs) { $KBArticleIDs = $KBArticleIDs | Foreach-Object { $_.ToUpper() } }
if ($ExcludeList) { $ExcludeList = $ExcludeList | Foreach-Object { $_.ToUpper() } }
if ($NonInteractive) { $InteractiveMode = 0 }
$RegAuditModeKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State"
if ((Get-ItemProperty -Path $RegAuditModeKey).ImageState -ne "IMAGE_STATE_COMPLETE") {
Write-Host "Windows is in audit mode." -Fore Green
$AuditMode = 1
} else {
$AuditMode = 0
}
if ($InteractiveMode) {
Write-Host "Starting in interactive mode." -Fore Green
} else {
Write-Host "Starting in non-interactive mode." -Fore Green
Write-Host ""
Write-Host "Command Arguments" -Fore Green
Write-Host " Password : ``$Password``" -Fore Green
Write-Host " Server : $Server" -Fore Green
Write-Host " SPack : $SPack" -Fore Green
Write-Host " AutoSelect : $AutoSelect" -Fore Green
Write-Host " ExcludeList : $ExcludeList" -Fore Green
Write-Host " KBArticleIDs : $KBArticleIDs" -Fore Green
Write-Host " NonInteractive: $NonInteractive" -Fore Green
Write-Host ""
}
if (-not([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
if ($InteractiveMode) {
Write-Host "Administrator privileges required to continue." -Fore Red
Write-Host "Restarting by 'Run as Administrator' in a new console window." -Fore Red
$RemainingArgs = ""
if ($ExcludeList) {
$RemainingArgs += " -ExcludeList " + ($ExcludeList -Join ",")
}
if ($KBArticleIDs) {
$RemainingArgs += " -KBArticleIDs " + ($KBArticleIDs -Join ",")
}
$CmdArgs = "-NoProfile -Command `$PSCP='$ThisScript';&([ScriptBlock]::Create((Get-Content -LiteralPath `$PSCP|Out-String)))" `
+ " '$Password' $Server $SPack $AutoSelect $RemainingArgs"
Start-Process "powershell.exe" -Verb Runas -ArgumentList $CmdArgs
$InteractiveMode = $False
} else {
Write-Host "Administrator privileges required. Exiting ..." -Fore Red
}
exit
}
[Int]$Answer = -1
if ($InteractiveMode -and -not $Resumed) {
Write-Host "Your computer may be restarted during the update session." -Fore Yellow
Write-Host "Please save your work." -Fore Yellow
Write-Host "Do you want to exit now? 0:Yes, 1:No [0]:" -NoNewline -Fore Yellow
try { $Answer = Read-Host } catch [System.Exception] { $Answer = -1 }
switch ($Answer) {
0 { Write-Host "Exiting ..." -Fore Green ; exit }
1 { } #Continuing the update session
default { Write-Host "Invalid input detected. Exiting ..." -Fore Red ; exit }
}
Write-Host ""
Write-Host "(Press Ctrl+C to exit." -Fore Green
Write-Host " Press Ctrl+Break to abort." -NoNewline -Fore Green
Write-Host "(deprecated)" -NoNewline -Fore Yellow
Write-Host ") " -Fore Green
Write-Host ""
}
$UserName = "$((Get-ChildItem Env:USERDOMAIN).Value)\$((Get-ChildItem Env:USERNAME).Value)"
$CredentialFileOK = $False
if (-not $AuditMode) {
if ($Password -eq "@@@@@") {
$SavedCredential = "$script:PSScriptRoot\SavedCredential.dat"
if (Test-Path $SavedCredential) {
Write-Host "Validating the stored credential information." -Fore Green
try {
$SecurePassword = Get-Content $SavedCredential | ConvertTo-SecureString -ErrorAction SilentlyContinue
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserName,$SecurePassword
$Password = $Credential.GetNetworkCredential().Password
} catch [System.Exception] {}
if (CheckCredential) {
$CredentialFileOK = $True
} else {
Write-Host "Incorrect password in the stored credential information." -Fore Yellow
Write-Host "Deleting $($SavedCredential)." -Fore Yellow
try {
Remove-Item $SavedCredential -Force -ErrorAction SilentlyContinue
} catch [System.Exception] {
Write-Host "Failed to delete the file." -Fore Yellow
Write-Host $Error[0].Exception
Write-Host "Continuing the update session." -Fore Yellow
}
}
}
if (-not $CredentialFileOK) {
if (-not $InteractiveMode) {
Write-Host "Password not specified. Exiting ..." -Fore Red
Write-Host @"
WUA.bat [<password>] [<service_type>] [<feature_update>] [<important_updates_only>]
[-ExcludeList/Exclude/EL <exclude_list>]
[-KBArticleIDs/KBs/IDs <include_list>]
[-NonInteractive/NI]
The first argument (<password>) is mandatry in non-interactive mode
unless the credential information has been stored.
"@
exit
}
$Credential = $Host.UI.PromptForCredential("Credential Request","Please enter your password.",$UserName,$Null)
if (-not $Credential) {
Write-Host "Password input cancelled. Exiting ..." -Fore Red
exit
}
$Password = $Credential.GetNetworkCredential().Password
if (-not (CheckCredential)) {
Write-Host "Password incorrect. Exiting ..." -Fore Red
exit
}
if ($Password -ne "") {
try {
ConvertFrom-SecureString $Credential.Password | Set-Content $SavedCredential
$CredentialFileOK = $True
Write-Host "Credential has been saved in $SavedCredential." -Fore Green
} catch [System.Exception] {
Write-Host "Failed to save credential in $SavedCredential." -Fore Yellow
Write-Host "Continuing the update session." -Fore Yellow
}
}
}
} else {
if (-not (CheckCredential)) {
Write-Host "Password incorrect. Exiting ..." -Fore Red
exit
}
}
}
if ($Server -lt 0) {
if (-not $InteractiveMode) {
Write-Host "Service_type not specified. '0:default' will be used." -Fore Yellow
$Server = 0
} else {
Write-Host "Please enter service_type." -Fore Green
Write-Host " (0:default, 1:WSUS, 2:Windows Update, 3:Microsoft Update) [0]:" -NoNewline -Fore Green
try { $Answer = Read-Host } catch [System.Exception] { $Answer = -1 }
$Server = $Answer
}
}
switch ($Server) {
0 {}
1 {}
2 {}
3 { $ServiceID="7971f918-a847-4430-9279-4a52d1efe18d" }
default { Write-Host "Invalid value (service_type). Exiting ..." -Fore Red ; exit }
}
if ($SPack -lt 0) {
if (-not $InteractiveMode) {
Write-Host "feature_update switch not specified. '0:default(OFF)' will be used." -Fore Yellow
$SPack = 0
} else {
Write-Host "Include Feature Update, Service Pack? 0:No, 1:Yes [0]:" -NoNewline -Fore Green
try { $Answer = Read-Host } catch [System.Exception] { $Answer = -1 }
$SPack = $Answer
}
}
switch ($SPack) {
0 {}
1 {}
default { Write-Host "Invalid value (feature_update). Exiting ..." -Fore Red ; exit }
}
if ($AutoSelect -lt 0) {
if (-not $InteractiveMode) {
Write-Host "Search only important updates? '1:ON(important updates only)' will be used." -Fore Yellow
$AutoSelect = 1
} else {
Write-Host "Include preferred updates? (!= important updates) 0:No, 1:Yes [0]:" -NoNewline -Fore Green
try { $Answer = Read-Host } catch [System.Exception] { $Answer = -1 }
$AutoSelect = 1 - $Answer
}
switch ($AutoSelect) {
0 { Write-Host "->All preferred updates will be included." -Fore Green }
1 { Write-Host "->Only important updates will be searched." -Fore Green }
}
}
switch ($AutoSelect) {
0 { $Criteria = "BrowseOnly=0 and IsInstalled=0 and IsHidden=0 and Type='Software'" }
1 { $Criteria = "(IsAssigned=1 and IsInstalled=0 and IsHidden=0 and Type='Software') or " + `
"(AutoSelectOnWebSites=1 and IsInstalled=0 and IsHidden=0 and Type='Software')" }
default { Write-Host "Invalid value (important_updates_only). Exiting ..." -Fore Red ; exit }
}
if ($KBArticleIDs) { Write-Host "Include list:$($KBArticleIDs -Join ",")" -Fore Green }
if ($ExcludeList) { Write-Host "Exclude list:$($ExcludeList -Join ",")" -Fore Green }
if (-not $Resumed) {
if ($CreateRestorePoint) { CreateRestorePoint }
if ($Server -eq 3) { EnableMicrosoftUpdate }
$WUSettings = (New-Object -com "Microsoft.Update.AutoUpdate").Settings
if ($WUSettings.NotificationLevel -gt 1) {
try {
$tmp = $WUSettings.NotificationLevel
$WUSettings.NotificationLevel = 1
$WUSettings.Save()
Write-Host "Temporarily disabled OS standard Automatic Update." -Fore Yellow
$orgNotificationLevel = $tmp
} catch [System.Exception] {
Write-Host "Unable to disable OS standard Automatic Update. Continuing ..." -Fore Yellow
}
}
if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") {
Reboot_and_Rerun $SPack
}
} else { # after reboot
Write-Host ""
DisableAutoLogon
RemoveScheduledTask
if ($NotificationLevel) { $orgNotificationLevel = $NotificationLevel }
Write-Host "Resuming the previous Windows Update session ..." -Fore Green
if ($InteractiveMode) { timeout 30 }
}
# Search for updates -> download -> apply
$script:ULNotified = $False
if ($SPack) {
$Result = InstallUpdates "Feature Update, Service Packs"
}
$Result = InstallUpdates "Update Rollups, Quality Rollups"
$Result = InstallUpdates "Other Updates"
while ($Result) {
$Result = InstallUpdates "Remaining Updates"
}
Write-Host ""
Write-Host "Terminating the update session." -Fore Green
if ($script:Incomplete) {
Write-Host "Unapplied updates exist." -Fore Red
PostProcess "succeeded with errors"
} else {
PostProcess "succeeded"
}
Write-Host ""
if ([System.Environment]::OSVersion.Version.Major -eq 10) {
try { usoclient.exe startscan } catch [System.Exception] {}
}
} catch [System.Exception] {
Write-Host "System error occurred." -Fore Red
Write-Host $Error[0].ToString() $Error[0].InvocationInfo.PositionMessage
PostProcess "failed"
} finally {
if ($orgNotificationLevel) {
Write-Host "Reverting the OS standard Automatic Update setting.`n" -Fore Yellow
$WUSettings = (New-Object -com "Microsoft.Update.AutoUpdate").Settings
$WUSettings.NotificationLevel = $orgNotificationLevel
$WUSettings.Save()
}
StopTranscript
if ($InteractiveMode) { Pause }
}
}
Function StartTranscript() {
if (-not $LogDir) { return }
Write-Host "Creating a logfile in $LogDir." -Fore Green
try {
Start-TranScript "$LogDir\$LogFilename" -Append
} catch [System.Exception] {
Write-Host "Failed to open a log file." -Fore Red
Write-Host $Error[0].Exception
Write-Host "Destination folder changed to $env:TEMP." -Fore Yellow
$LogDir = $env:TEMP
try {
Start-TranScript "$LogDir\$LogFilename" -Append
} catch [System.Exception] {
Write-Host "Failed to open a log file." -Fore Red
Write-Host $Error[0].Exception
Write-Host "Exiting ..." -Fore Red
exit
}
}
}
Function CheckCredential() {
$Tmp = $UserName.Split("\")
$Domain = $Tmp[0]
$User = $Tmp[1]
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
if ($Domain -ne $env:COMPUTERNAME) { #domain user
$CT = [System.DirectoryServices.AccountManagement.ContextType]::Domain
} else { #local user
$CT = [System.DirectoryServices.AccountManagement.ContextType]::Machine
}
$PC = New-Object System.DirectoryServices.AccountManagement.PrincipalContext $CT,$Domain
return $PC.ValidateCredentials($User,$Password)
}
Function CreateRestorePoint() {
$RegSPP = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SPP\Clients"
if (-not ((Get-ItemProperty $RegSPP)."{09F7EDC5-294E-4180-AF6A-FB0E6A0E9513}")) { return }
$LastCP = (@(Get-ComputerRestorePoint))[-1]
if ($LastCP) { $LastCreationTime = $LastCP.ConvertToDateTime($LastCP.CreationTime) }
if ((Get-Date) -lt ($LastCreationTime).AddMinutes($MinimumRestorePointInterval)) {
Write-Host "Skipping a Restore Point creation because less than $($MinimumRestorePointInterval) minutes have passed since the last time." -Fore Green
} else {
Write-Host "Creating a Restore Point ..." -Fore Green
CheckPoint-Computer -Description "Automatic Windows Update" -RestorePointType MODIFY_SETTINGS `
-ErrorAction SilentlyContinue
}
}
Function EnableMicrosoftUpdate () {
$MU = New-Object -ComObject Microsoft.Update.ServiceManager -Strict
$ServiceExists = $False
foreach ($Service in $MU.Services) {
if ($Service.ServiceID -eq $ServiceID) { $ServiceExists = $True }
}
if (-not $ServiceExists) {
Write-Host "Opting in Microsoft Update service." -Fore Green
try {
$Result = $MU.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",2,"")
} catch [System.Exception] {
Write-Host "Failed to opt in Microsoft Update service." -Fore Red
Write-Host "Exiting ..." -Fore Red
exit
}
}
}
Function Reboot_and_Rerun($SP) {
Write-Host "Reboot required. Your computer will now be restarted." -Fore Red
if ($InteractiveMode) {
Write-Host "Waiting 30 seconds. Press any key to continue." -Fore Green
timeout 30 | Out-Null
}
EnableAutoLogon
AddScheduledTask $SP
Start-Sleep -s 3
Restart-Computer -Force
Stop-Process $PID
}
Function EnableAutoLogon() {
if ($AuditMode) { return }
$RegLogonKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
if ((Get-ItemProperty -Path $RegLogonKey).AutoAdminLogon -eq 1) { return }
Write-Host "Enabling automatic logon." -Fore Yellow
$Result = New-ItemProperty -Path $RegLogonKey -name AutoAdminLogon -value 1 -Force
$Result = New-ItemProperty -Path $RegLogonKey -name ForceAutoLogon -value 1 -Force # workaround for Win10's "Sign-in" button
$Result = New-ItemProperty -Path $RegLogonKey -name DefaultUserName -value $UserName -Force
$Result = New-ItemProperty -Path $RegLogonKey -name DefaultPassword -value $Password -Force
}
Function AddScheduledTask($SP) {
if (CheckTask) { return }
Write-Host "Enabling automatic restart of the update session." -Fore Yellow
$RemainingArgs = "-R"
if ($ExcludeList) {
$RemainingArgs += " -EL " + ($ExcludeList -Join ",")
}
if ($KBArticleIDs) {
$RemainingArgs += " -KBs " + ($KBArticleIDs -Join ",")
}
if (-not $InteractiveMode) {
$RemainingArgs += " -NI"
}
if ($CredentialFileOK) { $Password = "@@@@@" }
if ($orgNotificationLevel) {
$RemainingArgs += " -NL $orgNotificationLevel"
}
$Command = "PowerShell.exe -NoP -C `$PSCP='''$ThisScript''';&([ScriptBlock]::Create((gc -Li `$PSCP|Out-String)))" `
+ " '''$Password''' $Server $SPack $AutoSelect $RemainingArgs"
$Result = schtasks /create /tn Windows-Update-Script /ru $UserName /sc ONLOGON /tr $Command /it /f /rl HIGHEST 2>&1
if (-not $?) { throw $Result }
}
Function CheckTask() {
schtasks /query /tn Windows-Update-Script 2>&1 | Out-Null
return $?
}
Function DisableAutoLogon() {
if ($AuditMode) { return }
$RegLogonKey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
if (-not ((Get-ItemProperty -Path $RegLogonKey).AutoAdminLogon)) { return }
Write-Host "Disabling automatic logon." -Fore Yellow
Remove-ItemProperty -Path $RegLogonKey -name AutoAdminLogon -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $RegLogonKey -name ForceAutoLogon -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $RegLogonKey -name DefaultUserName -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $RegLogonKey -name DefaultPassword -ErrorAction SilentlyContinue
}
Function RemoveScheduledTask() {
if (-not (CheckTask)) { return }
Write-Host "Disabling automatic restart of the update session." -Fore Yellow
$Result = schtasks /delete /tn Windows-Update-Script /f 2>&1
if (-not $?) { throw $Result }
}
Function InstallUpdates($Type) {
$Msg = @( "not started", `
"in progress", `
"succeeded", `
"succeededwitherrors", `
"failed", `
"aborted" `
)
$script:Incomplete = $False
# Search for updates
Write-Host ""
Write-Host "Searching for updates. : $Type" -Fore Green
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
if (Get-Variable SearchResult -Scope script -ErrorAction SilentlyContinue) {
$SearchResult = $script:SearchResult
} else {
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$UpdateSearcher.ServerSelection = $Server
if ($Server -eq 3) { $UpdateSearcher.ServiceID = $ServiceID }
$SearchResult = $UpdateSearcher.Search($Criteria)
$script:SearchResult = $SearchResult
}
if ($Type -Match "Feature Update") {
[array]$Updates = $SearchResult.Updates | Where-Object {
$_.Title -Match "Feature Update" -or `
$_.Title -Match "機能更新プログラム" -or `
$_.Categories -Contains "68c5b0a3-d1a6-4553-ae49-01d3a7827828"
}
} elseif ($Type -Match "Rollups") {
[array]$Updates = $SearchResult.Updates | Where-Object {
$_.Title -Match "Quality Rollup" -or `
$_.Title -Match "品質ロールアップ" -or `
$_.Categories -Contains "28bc880e-0592-4cbf-8f95-c79b17911d5f"
}
} else {
[array]$Updates = $SearchResult.Updates | Where-Object {
$_.Title -NotMatch "Feature Update" -and `
$_.Title -NotMatch "機能更新プログラム" -and `
$_.Categories -NotContains "68c5b0a3-d1a6-4553-ae49-01d3a7827828"
}
}
if ($KBArticleIDs) {
$Updates = $Updates | Where-Object { $KBArticleIDs -Contains "KB$($_.KBArticleIDs)" }
}
if (-not $Updates) { $Updates = @() }
Write-Host " $($Updates.Count) update(s) found." -Fore Green
$NumDownload = 0
$NumInstall = 0
if ($Updates.Count) {
# Download updates
foreach ($Update in $Updates) {
if ($ExcludeList -Contains "KB$($Update.KBArticleIDs)") {
Write-Host " - " -NoNewline -Fore Green
Write-Host $(FormatTitle $Update.Title 23) -NoNewline
Write-Host " has been excluded." -Fore Green
Continue
}
if ($Update.IsDownloaded) {
Write-Host " - " -NoNewline -Fore Green
Write-Host $(FormatTitle $Update.Title 25) -NoNewline
Write-Host " has been downloaded." -Fore Green
$NumDownload++
} else {
Write-Host " - Downloading " -NoNewline -Fore Green
Write-Host $(FormatTitle $Update.Title 17) -NoNewline
Write-Host "." -Fore Green
$UpdatesToDownload = New-Object -ComObject Microsoft.Update.UpdateColl
$Result = $UpdatesToDownload.Add($Update)
$Downloader = $UpdateSession.CreateUpdateDownloader()
$Downloader.Updates = $UpdatesToDownload
$Result = $Downloader.Download()
if ($Result.ResultCode -eq 2) {
$NumDownload++
} else {
$Result = $Downloader.Download()
if ($Result.ResultCode -eq 2) {
$NumDownload++
} else {
Write-Host " Failed to download " -NoNewline -Fore Red
Write-Host $(FormatTitle $Update.Title 24) -NoNewline
Write-Host "." -Fore Red
$script:Incomplete = $True
}
}
}
if ($NumDownload -ge $MaxUpdatesToInstall) {
if (-not $script:ULNotified -and $Updates.Count -gt $MaxUpdatesToInstall) {
$script:ULNotified = $True
Write-Host " Maximum number of updates to be processed at a time is $($MaxUpdatesToInstall)." -Fore Yellow
Write-Host " The limit has been reached. NOTE:This message will not be shown again." -Fore Yellow
}
exit
}
}
# Apply updates
if ($NumDownload) {
Write-Host " ------------------------------------------------" -Fore Green
Write-Host " Applying $NumDownload update(s) ..." -Fore Green
$UpdatesToInstall = New-Object -Com Microsoft.Update.UpdateColl
foreach ($Update in $Updates) {
if ($Update.IsDownloaded) {
if ($Update.InstallationBehavior.CanRequestUserInput) {
Write-Host " - " -NoNewline -Fore Red
Write-Host $(FormatTitle $Update.Title 44) -NoNewline
Write-Host " *Skipping because user input required.*" -Fore Red
$script:Incomplete = $True
} else {
if ($Update.EulaAccepted) {
Write-Host " - " -NoNewline -Fore Green
Write-Host $(FormatTitle $Update.Title 4)
} else {
Write-Host " - " -NoNewline -Fore Yellow
Write-Host $(FormatTitle $Update.Title 22) -NoNewline
Write-Host " *Accepting EULA.*" -Fore Yellow
$Update.AcceptEULA()
}
$Result = $UpdatesToInstall.Add($Update)
}
}
}
if ($UpdatesToInstall.Count) {
Write-Host " ------------------------------------------------" -Fore Green
$Installer = $UpdateSession.CreateUpdateInstaller()
$Installer.Updates = $UpdatesToInstall
$Result = $Installer.Install()
Write-Host " Application result(s) of $($UpdatesToInstall.Count) update(s)" -Fore Green
for ($i = 0; $i -lt $UpdatesToInstall.Count; $i++) {
Write-Host " - Result of " -NoNewline -Fore Green
Write-Host $(FormatTitle $UpdatesToInstall.Item($i).Title 28) -NoNewline
Write-Host ": " -NoNewline -Fore Green
if ($Result.GetUpdateResult($i).ResultCode -eq 2) {
Write-Host ("$($Result.GetUpdateResult($i).ResultCode):" + `
$Msg[$Result.GetUpdateResult($i).ResultCode]) -Fore Blue
$NumInstall++
} else {
Write-Host ("$($Result.GetUpdateResult($i).ResultCode):" + `
$Msg[$Result.GetUpdateResult($i).ResultCode] + `
" (0x$('{0:x}' -f $Result.GetUpdateResult($i).HResult))") -Fore Red
}
}
Write-Host " ------------------------------------------------" -Fore Green
if ($Result.ResultCode -ne 2) {
Write-Host " More than 1 update could not be applied." -Fore Red
$script:Incomplete = $True
}
if ($Result.rebootRequired) {
Reboot_and_Rerun 0
}
} else {
Write-Host " No applicable updates available." -Fore Yellow
}
} else {
Write-Host " No applicable updates available." -Fore Yellow
}
} else {
# Write-Host " No applicable updates available." -Fore Green
}
if ($NumInstall) { Remove-Variable SearchResult -Scope script}
return $NumInstall
}
Function StopTranscript() {
if (-not $LogDir) { return }
try { Stop-Transcript | Out-Null } catch [System.Exception] {}
}
Function Pause() {
Write-Host "Press any key to continue."
$Host.UI.RawUI.FlushInputBuffer()
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") | Out-Null
}
Add-Type -AssemblyName "Microsoft.VisualBasic"
Function FormatTitle([String]$Title, $Margin) {
# return $Title # uncomment this line if you don't want truncation of update titles.
$MaxWidth = $host.UI.RawUI.WindowSize.Width - 1 - $Margin
try {
$Wide = [Microsoft.VisualBasic.Strings]::StrConv($Title,[Microsoft.VisualBasic.VbStrConv]::Wide)
} catch [System.ArgumentException] {
return ($Title -Replace "^(.{$($MaxWidth-3)}).{4,}$",'$1...')
}
$Width = 0
for ($Idx=0; $Idx -lt $Title.Length; $Idx++) {
$Width++
if ($Title[$Idx] -eq $Wide[$Idx]) { $Width++ }
}
if ($Width -le $MaxWidth) { return $Title }
$SB = New-Object System.Text.StringBuilder
$Width = 0
for ($Idx=0; $Idx -lt $Title.Length; $Idx++) {
$Width++
if ($Title[$Idx] -eq $Wide[$Idx]) { $Width++ }
if ($Width -gt $MaxWidth - 3) { Break }
[void]$SB.Append($Title[$Idx])
}
[void]$SB.Append("...")
return $SB.ToString()
}
Function PostProcess() {
$filter = ((Split-Path $ThisScript -Leaf) -Replace "\.[^.]+$","") + "_PP.???"
$files = Get-ChildItem -LiteralPath $script:PSScriptRoot -Filter $filter
:LOOP foreach ($file in $files) {
Switch ($file.Extension) {
".ps1" { $Cmd = { PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "&'$($file.FullName)'" $Args } }
".bat" { $Cmd = $file.FullName }
".vbs" { $Cmd = $file.FullName }
default { continue LOOP }
}
Write-Host "Executing an external command $($file.FullName) ..." -Fore Green
try {
& $Cmd $Args
} catch [System.Exception] {
Write-Host "System Error occorred while executing the external command." -Fore Yellow
Write-Host $Error[0].Exception
Write-Host "Continuing..." -Fore Yellow
}
break
}
}
Main