Link to home
Start Free TrialLog in
Avatar of zorvek (Kevin Jones)
zorvek (Kevin Jones)Flag for United States of America

asked on

Using PowerShell Script from Batch to Control Hyper-V Machine

My source: https://community.spiceworks.com/scripts/show/1898-hyper-v-virtual-machine-backup

My .bat file:

%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Executionpolicy Bypass -command '"C:\Users\My Name\Documents\Scripts\Scripts - Other\Start Stop VMs.ps1 C:\Users\My Name\Desktop\hosts.txt 1"'

My .ps1 file:

$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
}

My hosts.txt:

Windows 10 64 Bit

I run the .bat script and nothing happens. No errors. No warnings. No logging. And the VM keeps chugging along.

Kevin
Avatar of zorvek (Kevin Jones)
zorvek (Kevin Jones)
Flag of United States of America image

ASKER

Making progress. The PowerShell script wasn't running. I corrected the call in the batch script to

%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -noninteractive -Executionpolicy Bypass -command "& 'C:\Users\My Name\Documents\Scripts\Scripts - Other\Start Stop VMs.ps1' 'C:\Users\My Name\Documents\Scripts\Scripts - Other\hosts.txt' 1"

and am now getting these errors:

gwmi : Invalid class "msvm_computersystem"
At C:\Users\Kevin Jones\Documents\Scripts\Scripts - Other\Start Stop VMs.ps1:26 char:7
+ $vm = gwmi -namespace root\virtualization -query "select * from msvm_ ...
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

gwmi : Invalid class "Msvm_ShutdownComponent"
At C:\Users\Kevin Jones\Documents\Scripts\Scripts - Other\Start Stop VMs.ps1:28 char:11
+ $vmshut = gwmi -namespace root\virtualization -query "SELECT * FROM M ...
+           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

You cannot call a method on a null-valued expression.
At C:\Users\Kevin Jones\Documents\Scripts\Scripts - Other\Start Stop VMs.ps1:29 char:1
+ $result = $vmshut.InitiateShutdown("$true","no comment")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull


unable to shutdown Windows 10 64 Bit

Kevin
Avatar of oBdA
oBdA

Try to replace all occurrances of
root\virtualization
with
root\virtualization\v2
That worked!

Now I'm stuck here:

You cannot call a method on a null-valued expression.
At C:\Users\Kevin Jones\Documents\Scripts\Scripts - Other\Start Stop VMs.ps1:32 char:1
+ $result = $vmshut.InitiateShutdown("$true","no comment")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull


unable to shutdown Windows 10 64 Bit

The problem is that name is not a property of $vm so $vmshut is nothing.

I'm trying to list the properties of $vm but so far am unable to do so.

Trying this:

Write-Host ($vm | Format-List -Force | Out-String)

but nothing is logged.

Kevin
Actually, I think name IS a property but it's empty.

Kevin
ASKER CERTIFIED SOLUTION
Avatar of oBdA
oBdA

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
No, no, you do not combine write-host and format-* ;-).
If you want to print out things for debugging, just use $vm | Format-List * .That should show all values, even of hidden properties (PS hides a lot from you to make it "easier" for you).
Yes, I think your WMI query has valid syntax, but does not return a value (nothing found?). Did you cross-check the vm name you get by the prior WMI query?
That seems to be an issue with some Windows versions (mine works, so I can only guess).
Try replacing
$vmname = $vm.name
with
$vm.__PATH -match '(?<GUID>[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12})' | Out-Null
$vmname = $Matches['GUID']

Open in new window

>Any specific reason you're not just using Stop-VM and Start-VM?

Because I have little to no idea what I am doing and depending on crap on the web.

I built two PowerShell scripts I can call from my batch scripts and they work perfectly:

Stop-VM -Name $args[0] -Force

and

Start-VM -Name $args[0]

The problem with the first PowerShell script was that, beside being uber complicated, the queries were not working. For example, this query just returns the physical box machine, not the Hyper-V virtual machines:

$vm = gwmi -namespace root\virtualization\v2 -query "select * from msvm_computersystem"

So $vm was nothing which is why $vm.name was empty and so on. I'm not sure how it ever worked.

Thank you for simplifying this for me!

Kevin
As far as "For example, this query just returns the physical box machine, not the Hyper-V virtual machines" is concerned: were you running the script from an elevated console? A regular user won't be allowed to see access virtual machines.
Yes. Everything was run as administrator.
function Set-VMs
{
  <#
    .SYNOPSIS
    Short Description
    .DESCRIPTION
    Detailed Description
    .EXAMPLE
    Set-VMs
    explains how to use the command
    can be multiple lines
    .EXAMPLE
    Set-VMs
    another example
    can have as many examples as you like
  #>
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$false, HelpMessage="Time to wait betwen Each VM Start (in seconds)",ValueFromPipeline=$true)]
    [int] $waitstart = 200,
    
    [Parameter(Mandatory=$false, HelpMessage="Time to wait between Each VM to Stop (in Seconds)",ValueFromPipeline=$true)]
    [int] $waitshutdown = 120,
     
     [Parameter(Mandatory=$true,HelpMessage='Valid options: Connect or Disconnect')]            
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Start', 'Stop')] 
        [string]$StartStopVM,
    
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [ValidateScript({test-path -path $_ -pathType Leaf})]
    [string]$hosts

  )
  
  if ($startstopVM -eq 'start') 
    {
    $inputfile=get-content -Path $hosts
    foreach ($guest in $inputfile) 
      {
      try{
       if ((get-vm -Name $guest | select-object -ExpandProperty State) -eq "Running") 
         { 
          Write-Host ('{0} is already running' -f $guest)
          }
        else
            {
            Write-Host -Message ('Starting {0}' -f $guest)
             try { 
                start-vm -Name $guest
                Write-Verbose -Message ('{0} is started' -f $guest)
                }
              catch{
                Write-Verbose -Message ''
                Write-Verbose -Message ('unable to start {0}' -f $guest)
                Write-Verbose -Message ''
                }
          }
   }
              catch { write-host ("VM $guest does not exist") }
    }
}
#not starting must be stopping
  else {
    $inputfile=get-content -Path $hosts
    foreach ($guest in $inputfile) {
      Write-Verbose -Message ('shutting down {0}' -f $guest)
     try {
        stop-vm -Name $guest  
        Write-Verbose -Message ('no error while shutting down {0}' -f $guest)
        Write-Verbose -Message ('shutdown of {0} completed' -f $guest)
      Write-Verbose -Message ''
      }
      catch 
        {
        Write-Verbose -Message ''
        Write-Verbose -Message ('unable to shutdown {0}' -f $guest)
        Write-Verbose -Message ''
        }
}
    }
}     

Open in new window