仮想マシンが起動するたびに、WindowsUpdate.vbs が毎回実行されるのは避けたいので、共有フォルダーに特定のファイルが存在する場合にのみ、Windows Update.vbs を実行するようにしました。多数の仮想マシンの更新を連続して、シリアルに行うというのが目的なので、Windows Update.vbs で更新後、再起動の必要性の有無に関係なく、仮想マシンをシャットダウンするようなバッチを組みました。また、更新状況を追跡するために、WindowsUpdate.vbs の標準出力を共有フォルダー上のログ ファイルにリダイレクトします。そのため、共有フォルダーには「Everyone:変更」または「コンピューター アカウント: 変更」の権限が必要です。
1 台の仮想マシンのスタートアップ スクリプトにバッチを仕込み、バッチが期待どうりに動作することを確認します。どうやらうまくいったようです。
バッチをもう少し工夫してみます。同じ日に 2 回実行されることがないようにしてみます。いまのままだと、共有フォルダーにファイルが存在する限り、シャットダウンしてしまうからです。
次の checkupdate.cmd が、ゲストのスタートアップ スクリプトに仕込むバッチ ファイルの例です。共有フォルダー上に WindowsUpdate_コンピューター名_YYMMDD.log というファイルが存在する場合は、その日に WindowsUpdate.vbs を実行済みということで、スルーするようにしました。
[checkupdate.cmd (ゲスト OS 側)]
@echo off
SET dt=%date:~-10%IF EXIST \\SERVERNAME\SHARENAME\patchtuesday.txt GOTO UPDATESOON
GOTO END
:UPDATESOON
IF EXIST \\SERVERNAME\SHARENAME\WindowsUpdate-%COMPUTERNAME%-%dt:~0,4%%dt:~5,2%%dt:~8,2%.log GOTO END
cscript WindowsUpdate.vbs //NoLogo >> \\SERVERNAME\SHARENAME\WindowsUpdate-%COMPUTERNAME%-%dt:~0,4%%dt:~5,2%%dt:~8,2%.log
shutdown /s /t 0
:END
ホスト側のバッチ ファイルは次の updatevm.cmd のようにしました。最初に共有フォルダー上に WindowsUpdate.vbs をキックするためのファイルを作成し、すべての仮想マシンを処理したあとにファイルを削除して終了します。hvvmup2down.vbs は、オフラインの仮想マシンをシリアルに次々に更新していくために、仮想マシンを起動して、更新後、シャットダウンするまでを監視するスクリプトです。
[updatevm.cmd (ホスト OS 側)]
@echo off
echo PatchNow > \\SERVERNAME\SHARENAME\patchtuesday.txt
cscript hvvmup2down.vbs VirtualMachineName1
cscript hvvmup2down.vbs VirtualMachineName2
cscript hvvmup2down.vbs VirtualMachineName3
cscript hvvmup2down.vbs VirtualMachineName4
cscript hvvmup2down.vbs VirtualMachineName5
del \\SERVERNAME\SHARENAME\patchtuesday.txt
echo PatchNow > \\SERVERNAME\SHARENAME\patchtuesday.txt
cscript hvvmup2down.vbs VirtualMachineName1
cscript hvvmup2down.vbs VirtualMachineName2
cscript hvvmup2down.vbs VirtualMachineName3
cscript hvvmup2down.vbs VirtualMachineName4
cscript hvvmup2down.vbs VirtualMachineName5
del \\SERVERNAME\SHARENAME\patchtuesday.txt
hvvmup2down.vbs の役割は、仮想マシンを起動して、シャットダウンするまでを監視することですが、仮想マシンが一時停止または保存状態のことも考慮しなければなりません。一時停止または保存状態の仮想マシンは、すでに起動が完了している可能性があるり、復帰時にスタートアップ スクリプトが実行されないかもしれないからです。そこで、仮想マシンが一時停止または保存状態の場合は、仮想マシンを開始後に、1 分待って、シャットダウンを開始し、シャットダウンしたらもう一度起動するようにスクリプトを組んでいます。
[hvvmup2down.vbs (ホスト OS 側)]
Option Explicit
Dim arg, targetVM, WMIService, VMs, InputKey,timeInterval, VMshut, VMshutRet
If Right((LCase(WScript.FullName)),11) <> "cscript.exe" then
WScript.Echo "このスクリプトはCSCRIPT.EXEを使用して実行して下さい。"
WScript.Quit
End if
Set arg = WScript.Arguments
if arg.Count = 0 then
WScript.StdOut.Write "開始する仮想マシン名を入力して下さい: "
InputKey = Trim(WScript.StdIn.ReadLine)
If InputKey <> "" then
targetVM = InputKey
Else
WScript.Echo "仮想マシン名が入力されませんでした。中止します。"
WScript.Quit
End If
else
targetVM = arg(0)
end if
timeInterval = 1000 '1秒
Set WMIService = GetObject("winmgmts:\\.\root\virtualization")
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
Select Case VMs.ItemIndex(0).EnabledState
Case 3
WScript.StdOut.Write targetVM & " を開始します"
VMs.ItemIndex(0).RequestStateChange(2)
Do while VMs.ItemIndex(0).EnabledState > 2
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.Echo "..完了。"
Case 32768, 32769 '32768=一時停止/32769=保存完了
WScript.StdOut.Write targetVM & " を再開します"
VMs.ItemIndex(0).RequestStateChange(2)
Do while VMs.ItemIndex(0).EnabledState <> 2
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.Echo "..完了。"
timeInterval = 60000 '1分
WScript.sleep(timeInterval)
WScript.StdOut.Write targetVM & " のシャットダウンを開始します "
Set VMshut = WMIService.ExecQuery("SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" & VMs.ItemIndex(0).Name & "'")
VMshutRet = VMshut.ItemIndex(0).InitiateShutdown(True,"shutdown by script")
if VMshutRet <> 0 then
WScript.Echo "... 失敗しました。監視を終了します。"
WScript.Quit
end if
timeInterval = 10000 '10秒
Do while VMs.ItemIndex(0).EnabledState <> 3
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.StdOut.Write vbCrLF & targetVM & " のシャットダウンが完了しました。クリーンブートを開始します"
VMs.ItemIndex(0).RequestStateChange(2)
Do while VMs.ItemIndex(0).EnabledState <> 2
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.Echo "..完了。"
Case 2
WScript.Echo targetVM & " は既に動作しているため、監視対象外です。"
WScript.Quit
Case Else
WScript.Echo targetVM & " を開始できません。監視を終了します。"
WScript.Quit
End Select
WScript.Echo "仮想マシンが停止するまで待機します..."
timeInterval = 600000 '5分
Do while timeInterval <> 0
WScript.Sleep(timeInterval)
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
Select Case VMs.ItemIndex(0).EnabledState
Case 3
WScript.Echo targetVM & " は停止しました(シャットダウン)。"
timeInterval = 0
Case 32769
WScript.Echo targetVM & " は停止しました(保存)。"
timeInterval = 0
Case 32773
WScript.Echo targetVM & " は保存中です..."
timeInterval = 5000
Case 32774
WScript.Echo targetVM & " は停止です..."
timeInterval = 5000
Case Else
timeInterval = 60000
End Select
Loop
Dim arg, targetVM, WMIService, VMs, InputKey,timeInterval, VMshut, VMshutRet
If Right((LCase(WScript.FullName)),11) <> "cscript.exe" then
WScript.Echo "このスクリプトはCSCRIPT.EXEを使用して実行して下さい。"
WScript.Quit
End if
Set arg = WScript.Arguments
if arg.Count = 0 then
WScript.StdOut.Write "開始する仮想マシン名を入力して下さい: "
InputKey = Trim(WScript.StdIn.ReadLine)
If InputKey <> "" then
targetVM = InputKey
Else
WScript.Echo "仮想マシン名が入力されませんでした。中止します。"
WScript.Quit
End If
else
targetVM = arg(0)
end if
timeInterval = 1000 '1秒
Set WMIService = GetObject("winmgmts:\\.\root\virtualization")
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
Select Case VMs.ItemIndex(0).EnabledState
Case 3
WScript.StdOut.Write targetVM & " を開始します"
VMs.ItemIndex(0).RequestStateChange(2)
Do while VMs.ItemIndex(0).EnabledState > 2
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.Echo "..完了。"
Case 32768, 32769 '32768=一時停止/32769=保存完了
WScript.StdOut.Write targetVM & " を再開します"
VMs.ItemIndex(0).RequestStateChange(2)
Do while VMs.ItemIndex(0).EnabledState <> 2
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.Echo "..完了。"
timeInterval = 60000 '1分
WScript.sleep(timeInterval)
WScript.StdOut.Write targetVM & " のシャットダウンを開始します "
Set VMshut = WMIService.ExecQuery("SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" & VMs.ItemIndex(0).Name & "'")
VMshutRet = VMshut.ItemIndex(0).InitiateShutdown(True,"shutdown by script")
if VMshutRet <> 0 then
WScript.Echo "... 失敗しました。監視を終了します。"
WScript.Quit
end if
timeInterval = 10000 '10秒
Do while VMs.ItemIndex(0).EnabledState <> 3
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.StdOut.Write vbCrLF & targetVM & " のシャットダウンが完了しました。クリーンブートを開始します"
VMs.ItemIndex(0).RequestStateChange(2)
Do while VMs.ItemIndex(0).EnabledState <> 2
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
WScript.StdOut.Write(".")
WScript.sleep(timeInterval)
Loop
WScript.Echo "..完了。"
Case 2
WScript.Echo targetVM & " は既に動作しているため、監視対象外です。"
WScript.Quit
Case Else
WScript.Echo targetVM & " を開始できません。監視を終了します。"
WScript.Quit
End Select
WScript.Echo "仮想マシンが停止するまで待機します..."
timeInterval = 600000 '5分
Do while timeInterval <> 0
WScript.Sleep(timeInterval)
Set VMs = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & targetVM & "'")
Select Case VMs.ItemIndex(0).EnabledState
Case 3
WScript.Echo targetVM & " は停止しました(シャットダウン)。"
timeInterval = 0
Case 32769
WScript.Echo targetVM & " は停止しました(保存)。"
timeInterval = 0
Case 32773
WScript.Echo targetVM & " は保存中です..."
timeInterval = 5000
Case 32774
WScript.Echo targetVM & " は停止です..."
timeInterval = 5000
Case Else
timeInterval = 60000
End Select
Loop
これでようやく、我が家の Hyper-V テスト環境で仮想マシンをバッチ的に更新できるようになりました。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。