我试图使用closures我的Hyper-V虚拟机的脚本,将他们的VHD复制到目标共享,然后启动备份。 我几天前在香料厂find了这个剧本 。 我总是收到用于关机的系统名称为空值的错误。
基本信息:
下面是脚本和解释我遇到的错误,确切地说是什么错误:
$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”来确定“guests”文本列表中的虚拟机是否应该closures或启动。
作为pipe理员运行Powershell我可以成功运行以下查询:
$vm = gwmi -namespace root\virtualization -query "select * from msvm_computersystem where elementname='$guest'"
这将返回一个表示虚拟机系统名称的string。
但是,下面的查询总是返回一个空值:
$vmshut = gwmi -namespace root\virtualization -query "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$vmname'"
看起来好像'Msvm_ShutdownComponent'类在我的系统上不存在…当我执行下面的行:
$result = $vmshut.InitiateShutdown("$true","no comment")
我总是收到一个错误,指出“你不能调用一个空值expression式的方法”。 我花了大约一天半的时间试图find我造成的这个错误,但我无法缩小它的范围。
我创build了一个符合我需求的脚本,我将在这里发布。
该脚本执行以下任务:
该脚本可以在远程物理主机上运行,不需要在本地运行。 请务必使用pipe理凭据运行该脚本,否则将无法正常工作。
我正在使用Windows Server 2008 R2,因此我无法访问Windows Server 2012中所有可用的命令。
而且,这里也是“剧本小子”的终极performance。 我已经将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 "Md-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) } } }