WindowsUpdateを自動化して工数を削減する

#WindowsUpdateってほんと手間・・・
WndowsServerの構築をしていると結構工数をを取られがちなのがWindowsUpdate。

手動でUpdateを行った場合、

WindowsUpdate → updateが完了したことを確認して再起動 → ログイン 
→　WindowsUpdate → （以降繰り返し） → 完了

のプロセスが必要な上に、たまーに様子を見てあげないとエラーが発生していたり、
再起動要求の画面で止まっていたりと手間なもの。
そこで今回はWindowsUpdateを自動化します。

#やること
PowerShellを利用してWindowsUpdateを自動化します。

事前準備 → WindowsUpdate →（放置） → 完了

を目指します。

#スクリプト作成
###プロキシ設定（必要あれば）
プロキシを利用したNW環境の場合、以下のスクリプトにてプロキシ設定を行う。

```powershell:proxy.bat
reg add "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyEnable /t reg_dword /d 1
reg add "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyServer /t reg_sz /d proxyURL:ポート番号
reg add "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyOverride /t reg_sz /d "<local>"
netsh winhttp import proxy source=ie
```

###自動アップデートスクリプト

**start.bat**、および**AutoWindowsUpdate.ps1**を作成

```powershell:start.bat
powershell Set-ExecutionPolicy RemoteSigned
powershell "C:\WindowsUpdate\AutoWindowsUpdate.ps1 Full"
powershell Set-ExecutionPolicy Restricted
pause
```


```powershell:AutoWindowsUpdate.ps1
param (
		[ValidateSet("Full", "Minimum")][string]$Option,	# アップデートオプション
		[switch]$ConsiderationBU,							# Build Update を考慮
		[switch]$MessageTest								# Microsoft Teams メッセージ送信テスト
	)

# スクリプトの配置場所
$G_MyName = "C:\WindowsUpdate\AutoWindowsUpdate.ps1"

# 完了時刻記録ファイル
$G_SetTimeStampFilePath = "C:\WindowsUpdate"
$G_CompleteTimeStampFileName = "WU_TimeStamp.txt"

# 再起動時刻記録ファイル
$G_RebootTimeStampFileName = "Reboot_TimeStamp.txt"

# Build Vertion 記録ファイル
$G_BuildVertionFileName = "Build_Vertion.txt"

# 最大適用更新数
$G_MaxUpdateNumber = 100

# 再起動禁止時間
$G_BootProhibitionTime = 3

# Microsoft Teams メッセージ送信用 URI ファイル名
$G_MicrosoftTeamsUriFileName = "MST_URI.txt"

$G_LogPath = "C:\WU_Log"
$G_LogName = "WU_Log.txt"
##########################################################################
# ログ出力
##########################################################################
function Log(
			$LogString
			){

	$Now = Get-Date

	$Log = $Now.ToString("yyyy/MM/dd HH:mm:ss.fff") + " "
	$Log += $LogString

	if( $G_LogName -eq $null ){
		$G_LogName = "LOG"
	}

	$LogFile = $G_LogName + "_" +$Now.ToString("yyyy-MM-dd") + ".log"

	# ログフォルダーがなかったら作成
	if( -not (Test-Path $G_LogPath) ) {
		New-Item $G_LogPath -Type Directory
	}

	$LogFileName = Join-Path $G_LogPath $LogFile

	Write-Output $Log | Out-File -FilePath $LogFileName -Encoding utf8 -append

	Return $Log
}

##########################################################################
# レジストリ追加/更新
##########################################################################
function RegSet( $RegPath, $RegKey, $RegKeyType, $RegKeyValue ){
	# レジストリそのものの有無確認
	$Elements = $RegPath -split "\\"
	$RegPath = ""
	$FirstLoop = $True
	foreach ($Element in $Elements ){
		if($FirstLoop){
			$FirstLoop = $False
		}
		else{
			$RegPath += "\"
		}
		$RegPath += $Element
		if( -not (test-path $RegPath) ){
			Log "Add Registry : $RegPath"
			md $RegPath
		}
	}

	# Key有無確認
	$Result = Get-ItemProperty $RegPath -name $RegKey -ErrorAction SilentlyContinue
	# キーがあった時
	if( $Result -ne $null ){
		Set-ItemProperty $RegPath -name $RegKey -Value $RegKeyValue
	}
	# キーが無かった時
	else{
		# キーを追加する
		New-ItemProperty $RegPath -name $RegKey -PropertyType $RegKeyType -Value $RegKeyValue
	}
	Get-ItemProperty $RegPath -name $RegKey
}

##########################################################################
# Autoexec.ps1 有効
##########################################################################
function EnableAutoexec( $ScriptName, $Option, $ConsiderationBU ){
	if( -not (Test-Path $ScriptName)){
		Log "[FAIL] $ScriptName not found !!"
		exit
	}

	### boot 時に autoexec.ps1 を自動実行するレジストリ設定

	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"
	$RegKey = "FileSysPath"
	$RegKeyType = "String"
	$RegKeyValue = "C:\\Windows\\System32\\GroupPolicy\\Machine"
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegKey = "PSScriptOrder"
	$RegKeyType = "DWord"
	$RegKeyValue = 3
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\0"
	$RegKey = "Script"
	$RegKeyType = "String"
	$RegKeyValue = "autoexec.ps1"
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegKey = "Parameters"
	$RegKeyType = "String"
	if( $ConsiderationBU ){
		$RegKeyValue = "$Option -ConsiderationBU"
	}
	else{
		$RegKeyValue = "$Option"
	}
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegKey = "IsPowershell"
	$RegKeyType = "DWord"
	$RegKeyValue = 1
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0"
	$RegKey = "FileSysPath"
	$RegKeyType = "String"
	$RegKeyValue = "C:\\Windows\\System32\\GroupPolicy\\Machine"
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegKey = "PSScriptOrder"
	$RegKeyType = "DWord"
	$RegKeyValue = 3
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0\0"
	$RegKey = "Script"
	$RegKeyType = "String"
	$RegKeyValue = "autoexec.ps1"
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	$RegKey = "Parameters"
	$RegKeyType = "String"
	if( $ConsiderationBU ){
		$RegKeyValue = "$Option -ConsiderationBU"
	}
	else{
		$RegKeyValue = "$Option"
	}
	RegSet $RegPath $RegKey $RegKeyType $RegKeyValue

	### 自動実行するスクリプトを autoexec.ps1 に上書きコピー
	$TergetPath = "C:\Windows\System32\GroupPolicy\Machine\Scripts\Startup"
	$TergetFile = Join-Path $TergetPath "autoexec.ps1"
	Log "$ScriptName → $TergetFile"
	if( -not (Test-Path $TergetPath)){
		md $TergetPath
		Log "md $TergetPath"
	}
	copy $ScriptName $TergetFile -Force

	Log "[INFO] $ScriptName → $TergetFile copied"

	if( -not (Test-Path $TergetFile)){
		Log "[FAIL] autoexec.ps1 copy failed !!"
		exit
	}
}

##########################################################################
# Autoexec.ps1 無効
##########################################################################
function DisableAutoexec(){
	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"
	if(Test-Path $RegPath){
		Remove-Item $RegPath -Recurse -Force -Confirm:$false
	}

	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0"
	if(Test-Path $RegPath){
		Remove-Item $RegPath -Recurse -Force -Confirm:$false
	}

	$Terget = "c:\Windows\System32\GroupPolicy\Machine\Scripts\Startup\autoexec.ps1"
	if(Test-Path $Terget){
		del $Terget -Force
	}
	Log "[INFO] Autoexec Disabled."
}


##########################################################################
# WU で再起動されたかの確認
##########################################################################
function IsWURebooted(){

	# 前回再起動時刻
	$RebootTime = GetTimeStampFile $G_SetTimeStampFilePath $G_RebootTimeStampFileName
	if( $RebootTime -eq $null ){
		# 一度も再起動していない
		return $false
	}

	$TimeSpan = New-TimeSpan $RebootTime (Get-Date)
	[int]$TotalHours = $TimeSpan.TotalHours
	if( $TotalHours -le $G_BootProhibitionTime ){
		return $true
	}
	else{
		return $false
	}
}


##########################################################################
# タイムスタンプファイル出力
##########################################################################
function SetTimeStampFile($SetTimeStampFilePath, $SetTimeStampFileName){

	$SetTimeStampFileFullName = Join-Path $SetTimeStampFilePath $SetTimeStampFileName

	if( -not (Test-Path $SetTimeStampFilePath)){
		md $SetTimeStampFilePath
	}

	if( -not (Test-Path $SetTimeStampFilePath)){
		Log "[FAIL] !!!!!!!! $SetTimeStampFilePath not created. !!!!!!!!"
		exit
	}

	$NowTime = Get-Date

	try{
		Set-Content -Value $NowTime.DateTime -Path $SetTimeStampFileFullName  -Encoding UTF8
	}
	catch{
		Log "[FAIL] !!!!!!!! $SetTimeStampFileFullName not created. !!!!!!!!"
		exit
	}
}

##########################################################################
# タイムスタンプファイル読み込み
##########################################################################
function GetTimeStampFile($SetTimeStampFilePath, $SetTimeStampFileName){

	$SetTimeStampFileFullName = Join-Path $SetTimeStampFilePath $SetTimeStampFileName

	if( -not (Test-Path $SetTimeStampFileFullName)){
		return $null
	}

	try{
		[datetime]$GetTime = Get-Content -Path $SetTimeStampFileFullName
	}
	catch{
		Log "[FAIL] !!!!!!!! $SetTimeStampFileFullName read error. !!!!!!!!"
		exit
	}

	return $GetTime
}


##########################################################################
# タイムスタンプファイル削除
##########################################################################
function RemoveTimeStampFile($SetTimeStampFilePath, $SetTimeStampFileName){

	$SetTimeStampFileFullName = Join-Path $SetTimeStampFilePath $SetTimeStampFileName

	if( -not (Test-Path $SetTimeStampFilePath)){
		# Path が存在しない
		return
	}

	if( -not (Test-Path $SetTimeStampFileFullName)){
		# ファイルが存在しない
		return
	}

	del $SetTimeStampFileFullName
	return
}

##########################################################
# Windows Update Reboot を Teames に通知する
##########################################################
function NoticeWU($FilePath, $FileName){

	$FileFullPath = Join-Path $FilePath $FileName

	if( -not (Test-Path $FileFullPath)){
		# URI ファイルが無い時は何もしない
		Log "URI file not found : $FileFullPath"
		return
	}

	# Web API の URL
	[array]$Lines = Get-Content -Path $FileFullPath
	if( $Lines.Count -eq 0 ){
		# データが入っていない
		Log "URI file is empty : $FileFullPath"
		return
	}
	$url = $Lines[0]
	if( $url.Length -le 35 ){
		# URIが短すぎ
		Log "URI data is empty : $FileFullPath"
		return
	}

	# Invoke-RestMethod に渡す Web API の引数を JSON にする
	$HostName = hostname

	$body = ConvertTo-JSON @{
		text = "Windows Update reboot now ! : $HostName"
	}

	# API を叩く
	Invoke-RestMethod -Method Post -Uri $url -Body $body -ContentType 'application/json'
}

##########################################################################
# バージョンファイル出力
##########################################################################
function SetVersionFile($SetVersionFilePath, $SetVersionFileName, $BuildVertion ){

	$SetVersionFileFullName = Join-Path $SetVersionFilePath $SetVersionFileName

	if( -not (Test-Path $SetVersionFilePath)){
		md $SetVersionFilePath
	}

	if( -not (Test-Path $SetVersionFilePath)){
		Log "[FAIL] !!!!!!!! $SetVersionFilePath not created. !!!!!!!!"
		exit
	}

	$RegistryBuildNumber = $BuildVertion.RegistryBuildNumber
	$WinverBuildNumber = $BuildVertion.WinverBuildNumber
	$OSVertion = $BuildVertion.OSVertion

	$VertionDatas = @()

	if($RegistryBuildNumber -ne $null){
		$VertionDatas += $RegistryBuildNumber
	}
	else{
		return
	}

	if( $WinverBuildNumber -ne $null ){
		$VertionDatas += $WinverBuildNumber
	}

	if( $OSVertion -ne $null ){
		$VertionDatas += $OSVertion
	}

	try{
		Set-Content -Value $VertionDatas -Path $SetVersionFileFullName  -Encoding UTF8
	}
	catch{
		Log "[FAIL] !!!!!!!! $SetVersionFileFullName not created. !!!!!!!!"
		exit
	}
}


##########################################################################
# バージョンファイル読み込み
##########################################################################
function GetVersionFile($SetVersionFilePath, $SetVersionFileName){

	$SetVersionFileFullName = Join-Path $SetVersionFilePath $SetVersionFileName

	if( -not (Test-Path $SetVersionFileFullName)){
		return $null
	}

	try{
		[array]$VertionDatas = Get-Content -Path $SetVersionFileFullName
	}
	catch{
		Log "[FAIL] !!!!!!!! $SetVersionFileFullName read error. !!!!!!!!"
		exit
	}

	$ReturnData = New-Object PSObject | Select-Object RegistryBuildNumber, WinverBuildNumber, OSVertion

	if( $VertionDatas.Count -eq 0 ){
		return $null
	}

	if( $VertionDatas.Count -ge 1 ){
		$ReturnData.RegistryBuildNumber = $VertionDatas[0]
	}

	if( $VertionDatas.Count -ge 2 ){
		$ReturnData.WinverBuildNumber = $VertionDatas[1]
	}

	if( $VertionDatas.Count -ge 3 ){
		$ReturnData.OSVertion = $VertionDatas[2]
	}

	return $ReturnData
}


##########################################################################
# Build バージョン 取得
##########################################################################
function GetBuildVersion(){
	$ReturnData = New-Object PSObject | Select-Object RegistryBuildNumber, WinverBuildNumber, OSVertion, Edition

	# ビルド番号詳細
	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
	$RegKey = "BuildLabEx"
	$RegistryBuildNumber = (Get-ItemProperty $RegPath -name $RegKey).$RegKey
	if( $RegistryBuildNumber -ne $null ){
		$ReturnData.RegistryBuildNumber = $RegistryBuildNumber
	}
	else{
		$ReturnData.RegistryBuildNumber = $null
	}

	# Winver のビルド番号
	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
	$RegKey = "CurrentBuild"
	$MajorNumber = (Get-ItemProperty -Path $RegPath -name $RegKey).$RegKey
	$RegKey = "UBR"
	$MinorNumber = (Get-ItemProperty -Path $RegPath -name $RegKey).$RegKey
	$WinverBuildNumber = $MajorNumber + "." + [String]$MinorNumber
	if( $MajorNumber -ne $null ){
		$ReturnData.WinverBuildNumber = $WinverBuildNumber
	}
	else{
		$ReturnData.WinverBuildNumber = $null
	}

	# Winver のバージョン
	$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
	$RegKey = "ReleaseId"
	$OSVertion = (Get-ItemProperty $RegPath -name $RegKey -ErrorAction SilentlyContinue).$RegKey
	if( $OSVertion -ne $null ){
		$ReturnData.OSVertion = $OSVertion
	}
	else{
		$ReturnData.OSVertion = $null
	}

	# OS のエディション
	$Win32_OperatingSystem = Get-WmiObject Win32_OperatingSystem
	$OS = $Win32_OperatingSystem.Caption
	$SP = $Win32_OperatingSystem.ServicePackMajorVersion
	if( $SP -ne 0 ){ $OS += " SP" + $SP }
	$ReturnData.Edition = $OS

	return $ReturnData
}

##########################################################
# Windows Update 完了 を Teames に通知する
##########################################################
function NoticeFinishWU($FilePath, $FileName, $BuildVertion){

	$FileFullPath = Join-Path $FilePath $FileName

	if( -not (Test-Path $FileFullPath)){
		# URI ファイルが無い時は何もしない
		Log "URI file not found : $FileFullPath"
		return
	}

	# Web API の URL
	[array]$Lines = Get-Content -Path $FileFullPath
	if( $Lines.Count -eq 0 ){
		# データが入っていない
		Log "URI file is empty : $FileFullPath"
		return
	}
	$url = $Lines[0]
	if( $url.Length -le 35 ){
		# URIが短すぎ
		Log "URI data is empty : $FileFullPath"
		return
	}

	# Invoke-RestMethod に渡す Web API の引数を JSON にする
	$HostName = hostname
	$RegistryBuildNumber = $BuildVertion.RegistryBuildNumber
	$WinverBuildNumber = $BuildVertion.WinverBuildNumber
	$OSVertion = $BuildVertion.OSVertion
	$OSEdition = $BuildVertion.Edition

	$Message = "Windows Update finish : $HostName`n`r"
	$Message += "Registry Build Number : $RegistryBuildNumber`n`r"
	$Message += "Winver Build Number : $WinverBuildNumber`n`r"
	$Message += "OS Vertion : $OSVertion`n`r"
	$Message += "OS Edition : $OSEdition`n`r"

	[array]$HotFixs = Get-HotFix | sort InstalledOn -Descending
	foreach($HotFix in $HotFixs){
		$WUDate = ($HotFix.InstalledOn).ToString("yyyy/MM/dd")
		$KB = $HotFix.HotFixID
		$KBType = $HotFix.Description
		$Message += "$WUDate $KB $KBType`n`r"
	}

	# 末尾の CR/LF 削除
	$MessageLength = $Message.Length -2
	$Message = $Message.Substring(0, $MessageLength)

	$body = ConvertTo-JSON @{
		text = $Message
	}

	# API を叩く
	Invoke-RestMethod -Method Post -Uri $url -Body $body -ContentType 'application/json'
}

##########################################################
# Windows Update 完了
##########################################################
function FinishWU(){
	# バージョンファイル読み込み
	$LastVertion = GetVersionFile $G_SetTimeStampFilePath $G_BuildVertionFileName

	# Build バージョン 取得
	$NowVertion = GetBuildVersion

	# Build バージョン更新確認
	if( ($LastVertion.RegistryBuildNumber -eq $NowVertion.RegistryBuildNumber) -and
		($LastVertion.WinverBuildNumber   -eq $NowVertion.WinverBuildNumber) -and
		($LastVertion.OSVertion           -eq $NowVertion.OSVertion)){

		# バージョン更新なし
		return
	}
	else{
		# バージョン更新あり

		# バージョンファイル出力
		SetVersionFile $G_SetTimeStampFilePath $G_BuildVertionFileName $NowVertion

		# Windows Update 完了 を Teames に通知する
		NoticeFinishWU $G_SetTimeStampFilePath $G_MicrosoftTeamsUriFileName $NowVertion

		# バージョン情報をログに出力
		$RegistryBuildNumber = $NowVertion.RegistryBuildNumber
		$WinverBuildNumber = $NowVertion.WinverBuildNumber
		$OSVertion = $NowVertion.OSVertion

		Log $RegistryBuildNumber
		Log $WinverBuildNumber
		Log $OSVertion
	}
}


##########################################################################
#
# main
#
##########################################################################
if (-not(([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))) {
	Log "Administrative privileges are required to run"
	exit
}

# Microsoft Teams メッセージ送信テスト
if( $MessageTest ){
	Log "Message send test"
	NoticeWU $G_SetTimeStampFilePath $G_MicrosoftTeamsUriFileName
	exit
}

# 自動起動スクリプト停止
DisableAutoexec

# バージョン確認
$OSData = Get-WmiObject Win32_OperatingSystem
$BuildNumber = $OSData.BuildNumber
$strVersion = $OSData.Version
$strVersion = $strVersion.Replace( ".$BuildNumber", "" )
$Version = [decimal]$strVersion
if( $Version -lt 6.1 ){
	Log "Supports Windows Server 2008 R2/Windows 7 or later"
	exit
}

# スクリプト存在確認
if( -not(Test-Path $G_MyName )){
	Log "Put the script in $G _myname and run it"
	exit
}

# 既知の問題(KB2962824)対応
if(($Version -ge 6.3) -and ($Version -lt 6.4)){
	$OSData = Get-WmiObject Win32_OperatingSystem
	if( $OSData.Caption -match "Windows Server 2012 R2" ){
		Log "Windows Server 2012 R2"
		$OriginalProgressPreference = $ProgressPreference
		$ProgressPreference="SilentlyContinue"
		$BitLocker = Get-WindowsFeature BitLocker
		if( $BitLocker.Installed -eq $false ){
			Log "Install & Restart because BitLocker is not installed"
			EnableAutoexec $G_MyName $Option $ConsiderationBU
			Add-WindowsFeature BitLocker -Restart
		}
		$ProgressPreference = $OriginalProgressPreference
	}
}

Log "--- Running Windows Update ---"

# 完了タイムスタンプファイル削除
RemoveTimeStampFile $G_SetTimeStampFilePath $G_CompleteTimeStampFileName

Log "Searching for updates..."
$updateSession = new-object -com "Microsoft.Update.Session"

$updateSearcher = $updateSession.CreateupdateSearcher()

# アップデートタイプコントロール
if( $Option -match "ful" ){
	Log "Full Update"
	$Option = "Full"
	$searchResult = $updateSearcher.Search("IsInstalled=0 and Type='Software'")
}
else{
	Log "Minimum Update"
	$Option = "Minimum"
	$searchResult = $updateSearcher.Search("IsInstalled=0 and Type='Software' and AutoSelectOnWebSites=1")
}


Log "List of applicable items on the machine:"
if ($searchResult.Updates.Count -eq 0) {
	Log "There are no applicable updates."
	SetTimeStampFile $G_SetTimeStampFilePath $G_CompleteTimeStampFileName

	FinishWU
	Log "=-=-=-=-=- Windows Update finished -=-=-=-=-="
}
else{
	$downloadReq = $False
	$i = 0
	foreach ($update in $searchResult.Updates){
		$i++
		if ( $update.IsDownloaded ) {
			$UpdateTitol = $update.Title
			Log "$i : $UpdateTitol (downloaded)"
		}
		else
		{
			$downloadReq = $true
			$UpdateTitol = $update.Title
			Log "$i : $UpdateTitol (not downloaded)"
		}
	}
	if ( $downloadReq ) {
		Log "Creating collection of updates to download..."
		$updatesToDownload = new-object -com "Microsoft.Update.UpdateColl"
		foreach ($update in $searchResult.Updates){
			$updatesToDownload.Add($update) | out-null
		}
		Log "Downloading updates..."
		$downloader = $updateSession.CreateUpdateDownloader()
		$downloader.Updates = $updatesToDownload
		$downloader.Download()
		Log "List of downloaded updates:"
		$i = 0
		foreach ($update in $searchResult.Updates){
			$i++
			if ( $update.IsDownloaded ) {
				$UpdateTitol = $update.Title
				Log "$i : $UpdateTitol (downloaded)"
			}
			else
			{
				$UpdateTitol = $update.Title
				Log "$i : $UpdateTitol (not downloaded)"
			}
		}
	}
	else
	{
		Log "All updates are already downloaded."
	}
	$updatesToInstall = new-object -com "Microsoft.Update.UpdateColl"
	Log "Creating collection of downloaded updates to install..."
	$i = 0
	foreach ($update in $searchResult.Updates){
		if ( $update.IsDownloaded ) {
			$updatesToInstall.Add($update) | out-null
			$i++
			$UpdateTitol = $update.Title
			Log "$i / $G_MaxUpdateNumber : $UpdateTitol (Install)"
			if( $i -ge $G_MaxUpdateNumber ){
				Log "Break max update $G_MaxUpdateNumber"
				break
			}
		}
	}
	if ( $updatesToInstall.Count -eq 0 ) {
		Log "Not ready for installation."
		Log "=-=-=-=-=- Windows Update Abnormal End -=-=-=-=-="
	}
	else
	{
		$InstallCount = $updatesToInstall.Count
		Log "Installing $InstallCount updates..."
		$installer = $updateSession.CreateUpdateInstaller()
		$installer.Updates = $updatesToInstall
		$installationResult = $installer.Install()
		if ( $installationResult.ResultCode -eq 2 ) {
			Log "All updates installed successfully."
		}
		else
		{
			Log "Some updates could not installed."
		}
		if ( $installationResult.RebootRequired ) {
			# Build Update 考慮
			if( $ConsiderationBU ){
				Log "Build Update Consideration decision"
				$Status = IsWURebooted	# 指定時間内に WU 再起動さたか
				if( $Status -eq $true ){
					Log "For Build Updae Consideration, Windows does not Update if uptime is shorter than $G_BootProhibitionTime h : $TotalHours h"
					Log "=-=-=-=-=- Windows Update Abort -=-=-=-=-="
					exit
				}
				else{
					Log "Windows Update continues because it is not Build update"
				}
			}

			# WU reboot loop 対策
			# (短時間再起動の場合は、人が介入できるタイミングを稼ぐために5分待つ)
			$Status = IsWURebooted	# 指定時間内に WU 再起動さたか
			if( $Status -eq $true ){
				Log "WU loop protection. Wait 5 minutes."
				sleep 300
			}

			# WU 再起動
			Log "One or more updates are requiring reboot."
			Log "[INFO] Autoexec Enabled"

			EnableAutoexec $G_MyName $Option $ConsiderationBU
			sleep 30
			Log "Reboot system now !!"
			SetTimeStampFile $G_SetTimeStampFilePath $G_RebootTimeStampFileName
			NoticeWU $G_SetTimeStampFilePath $G_MicrosoftTeamsUriFileName
			Restart-Computer -Force
		}
		else
		{
			Log "Finished. Reboot are not required."
			SetTimeStampFile $G_SetTimeStampFilePath $G_CompleteTimeStampFileName

			FinishWU
			Log "=-=-=-=-=- Windows Update finished -=-=-=-=-="
		}
	}
}

```

###スクリプト実行
①（必要であれば）**proxy.bat**の実行
②**start.bat**を下記のディレクトリに登録
C:\Users\\**ユーザ名**\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
※「AppData」は隠しフォルダの為、表示されない場合はフォルダオプションにて表示するように設定する
③「C:\\**WindowsUpdate**」フォルダを作成し、**AutoWindowsUpdate.ps1**を配置
④start.batを実行


WindowsUpdateが実行され、

更新ファイルの確認→ダウンロード＆インストール→再起動

をしてくれます。
これをスタートアップに登録したバッチファイルから実行し、無限ループで行います。

**=-=-=- Windows Update finished -=-=-=-=-=**

と表示されればアップデートが完了です。

[参考にしたサイト](https://qwerty.work/blog/2017/05/windowsupdate-fullauto-script.php)