Не удается завершить работу HyperV VM с помощью Powershell, несмотря на права администратора
Я пытаюсь использовать сценарий, который выключает мои виртуальные машины Hyper-V, копирует их виртуальные жесткие диски в целевую папку, а затем загружает их обратно. Я нашел скрипт на spiceworks всего пару дней назад. Я всегда получаю ошибки, что системное имя, предназначенное для выключения, является нулевым значением.
Общая информация:
- Я работал PowerShell в качестве администратора.
- Я установил для ExecutionPolicy значение "RemoteSigned", чтобы локальные файлы PowerShell выполнялись.
- Кажется, я использую PowerShell v 2.0
- Я использую HyperV в Server 2008 R2
- Я выполняю этот скрипт прямо на хосте VM
Ниже приведен сценарий и объяснение того, где я сталкиваюсь с ошибкой и какова именно эта ошибка:
$waitstart = 200
$waitshutdown = 120
if ($args[1] -match "0") {
$inputfile=get-content $args[0]
foreach ($guest in $inputfile) {
write-host "Starting $guest"
$vm = gwmi -namespace root\virtualization -query "select * from msvm_computersystem where elementname='$guest'"
$result = $vm.requeststatechange(2)
if ($result.returnvalue -match "0") {
start-sleep -s $waitstart
write-host ""
write-host "$guest is started" -foregroundcolor green
write-host ""
}
else {
write-host ""
write-host "unable to start $guest" -foregroundcolor red
write-host ""
}}}
if ($args[1] -match "1") {
$inputfile=get-content $args[0]
foreach ($guest in $inputfile) {
write-host "shutting down $guest"
$vm = gwmi -namespace root\virtualization -query "select * from msvm_computersystem where elementname='$guest'"
$vmname = $vm.name
$vmshut = gwmi -namespace root\virtualization -query "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$vmname'"
$result = $vmshut.InitiateShutdown("$true","no comment")
if ($result.returnvalue -match "0") {
start-sleep -s $waitshutdown
write-host ""
write-host "no error while shutting down $guest"
write-host "shutdown of $guest completed" -foregroundcolor green
write-host ""}
else {
write-host ""
write-host "unable to shutdown $guest" -foregroundcolor red
write-host ""
}}}
else {
write-host "USAGE: to shutdown VMs," -nonewline; write-host ".\managehyperV.ps1 c:\hosts.txt 1" -foregroundcolor yellow
write-host "USAGE: to start VMs," -nonewline; write-host ".\managehyperV.ps1 c:\hosts.txt 0" -foregroundcolor yellow
}
Сценарий принимает аргумент "1" или "0", чтобы определить, должны ли виртуальные машины в текстовом списке "гости" быть выключены или запущены.
Запустив Powershell от имени администратора, я могу успешно выполнить следующий запрос:
$vm = gwmi -namespace root\virtualization -query "select * from msvm_computersystem where elementname='$guest'"
Это возвращает строку некоторого вида, которая представляет системное имя виртуальной машины.
Однако следующий запрос всегда возвращает нулевое значение:
$vmshut = gwmi -namespace root\virtualization -query "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$vmname'"
Кажется, что класс "Msvm_ShutdownComponent" не существует в моей системе... когда я выполняю следующую строку:
$result = $vmshut.InitiateShutdown("$true","no comment")
Я всегда получаю сообщение об ошибке: "Вы не можете вызвать метод для выражения с нулевым значением". Я потратил около полутора дней, пытаясь найти совершаемую мной ошибку, но не могу ее сузить.
1 ответ
Я создал скрипт, который отвечает моим потребностям, и я опубликую его здесь.
Этот скрипт выполняет следующие задачи:
- Перебирает все перечисленные физические хосты
- Перебирает все неисключенные виртуальные машины
- Изящно завершает работу виртуальных машин и ожидает завершения работы
- Выполняет синхронную передачу битов всех файлов VHD (по одному за раз) для целевых виртуальных машин в указанное место
- Загрузка целевой виртуальной машины после завершения передачи
Этот скрипт может работать на удаленных физических хостах и не должен запускаться локально. Обязательно запустите сценарий с правами администратора, иначе он не будет работать правильно.
Я использую Windows Server 2008 R2 и поэтому у меня нет доступа ко всем модным командам, доступным в Windows Server 2012.
Кроме того, это окончательное выражение "сценарий детишек" здесь; Я собрал воедино около 4 различных сценариев, а затем добавил немного логики. Простите за несоответствия в синтаксисе и т. Д.
Этот сценарий был вручную переписан с автономной корпоративной системы, которую я использую, поэтому он может иметь несколько орфографических ошибок. Я не пытался выполнить этот скрипт напрямую. Тот, что в моей системе, кажется, работает просто отлично.
#Run this script in an administrative instance of powershell
#This script executes based on files in the C:\Scripts directory
cd "C:\Scripts"
#Import the BITS Library so that synchronous transfers can occur easily
Import-Module BitsTransfer
#Note that all these files are delimited by carriage returns - 1 host/vm name per line
#Place these files in the "C:\Scripts" directory on the system running this script
#HyperVParents is the list of remote systems that are hosting VMs that need backup.
$HyperVParents = Get-Content HyperV_Hosts.txt
#ExcludedVMs is the list of VMs that will not be turned off or booted during this process
#NOTE: All VM VHD files in Hyper-V are backed up with this script.
$ExcludedVMs = Get-Content ExcludedVMs.txt
#Build Date String
$filedate = get-date -format "M-d-yyyy"
#Create target directory on remote server for backup if it is not already present
#Replace REMOTESRV with your servername
$TargetPath = "\\REMOTESRV\VHDs\" + $filedate
If(!(Test-Path -Path $TargetPath))
{
New-Item -ItemType directory -Path $TargetPath
}
Foreach ($HyperVParent in $HyperVParents)
{
$VMManagementService = Get-WmiObject -class "Msvm_VirtualSystemManagementService" -namespace "root\virtualization" -ComputerName $HyperVParent
$VMs = Get-WmiObject -Namespace "root\virtualization" -ComputerName $HyperVParent -Query "Select * From MSVM_ComputerSystem where Caption='Virtual Machine'"
Foreach ($VM in $VMs)
{
#Set $VMExcluded to null in order to test the next VM for exclusion
#This routine could probably be more efficient.
$VMExcluded = $null
#Loop through list of excluded VMs to see if current VM is within this list
Foreach ($ExcludedVM in $ExcludedVMs)
{
If ($VM.ElementName -eq $ExcludedVM)
{
$VMExcluded = $true
Write-Host $VM.ElementName, ": Excluded from startup/shutdown process"
}
}
$VMSettingData = Get-WmiObject -Namespace "root\virtualization" -Query "Associators of {$VM} Where ResultClass=Msvm_VirtualSystemSettingData AssocClass=Msvm_SettingsDefineState" -ComputerName $HyperVParent
$VirtualDiskResource = Get-WmiObject -Namespace "root\virtualization" -Query "Associators of {$VMSettingData} Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_VirtualSystemSettingDataComponent" -ComputerName $HyperVParent | Where-Object {$_.ResourceSubType -match "Microsoft Virtual Hard Disk"}
$VHD_Path = $null
$vmGUID = $VM.name
#Start logical check that skips unused systems. They will not be powered off or on.
If ($VMExcluded -eq $null)
{
$Query = "select * from Msvm_computersystem where Name='" + $vmGUID + "'"
$VMTemp = gwmi -namespace root\virtualization -query $Query -ComputerName $HyperVParent
#Only attempt a shutdown if the system is powered on
If ($VMTemp.EnableState -ne "3")
{
Write-Host "System Requires Shutdown:", $VM.ElementName
#Build SQL Query - Use the target GUID here since the Msvm_ShutdownComponent seems to be only targeted with a GUID
$ShutdownQuery = "SELECT * from Msvm_ShutdownComponent WHERE SystemName ='" + $vmGUID + "'"
#Execute the query to select the shutdown component of the target VM
$vmshut = gwmi -namespace root\virtualization -query $ShutdownQuery -ComputerName $HyperVParent
$result = $vmshut.InitiateShutdown("$true","VHD Backup Process");
Write-Host "Shutting Down:", $VM.ElementName
#Wait for system to shutdown
Do {
#Increment 1 sec pauses for each loop
Start-Sleep -s 1
#Use a different variable here so that the original target VM is not modified
#Requery the VM each second to get an updated EnabledState
$VMTemp = gwmi -namespace root\virtualization -query $Query -ComputerName $HyperVParent
}
While ($VMTemp.EnabledState -ne "3");
Write-Host $VM.ElementName, "successfully shutdown"
}
Else
{
Write-Host $VM.ElementName, ": Already shutdown"
}
}
#End Logical check for systems that are unused
#Perform the file transfer of the VHD file afer the VM has shut down.
Foreach ($VHD in $VirtualDiskResource)
{
$VHD_PATH = $VHD.Connection[0] + ","
#Replace colon in path with dollar sign for UNC purposes
$VHD_PATH_TEMP = $VHD.Connection[0] -replace ':','$'
#Build Source String
$BackupSource = "\\" + $HyperVParent + "\" + "VHD_PATH_TEMP"
#Extract VHD Name from the path for use in the target file
$VHDName = Split-Path -Leaf $VHD.Connection[0]
#Build Target String; $TargetPath was built at the initiation of the script
$BackupTarget = $TargetPath + "\" + $VHDName
Write-Host "Beginning Backup:", $VM.ElementName, $BackupSource
#Transfer VHD file to the target server using BITS
StartBitsTransfer -Source $BackupSource -Destination $BackupTarget
Write-Host "Backup Complete:", $VM.ElementName, $BackupTarget
}
#Here backed up systems are turned back on after the file transfer of their VHD is complete.
#Filter out certain unused VMs - unused systems do not need to be booted.
If ($VMExcluded -eq $null)
{
Write-Host "Starting VM:", $VM.ElementName
#Boot the VM before the script loops and moves to the next system.
$result = $VM.requeststatechange(2)
}
}
}