Save to My DOJO
Table of contents
We love PowerShell. The task automation tool enhances productivity and also enables you to do tasks that simply wouldn’t be possible manually. It also just plain cool to sit back and watch scripts do their work! The tool enables endless possibilities in terms of specific tasks relating to individual set-ups and environments however the following 5 PowerShell applications should be applicable to all Hyper-V users. Sit back and enjoy the top 5 PowerShell hacks for Hyper-V.
Before we begin, make sure you have PowerShell enabled and up-to-date in Hyper-V. Assuming you have kept up to date, you are managing Hyper-V servers running Windows Server 2016 from a Windows 10 desktop. At least, that’s the smart way. The Windows 10 desktop does not need to have a hypervisor enabled, but you need the PowerShell module for Hyper-V. In Control Panel check installed Windows Features.
At a minimum check the box for the “Hyper-V Module for Windows PowerShell”. Then open a PowerShell prompt and run this command:
Get-Module Hyper-V -ListAvailable
It should show a version of 2.0.0.0. It is possible you might see older versions, but you can ignore them. PowerShell will use the latest version when you run any Hyper-V command.
5. Set Server Default
When you remotely manage a Hyper-V infrastructure from your desktop, you’ll find yourself constantly specifying the server name.
Get-VM -ComputerName chi-p50 -Credential globomanticsadministrator
You might even need to specify a credential. You can simplify your life by setting a default parameter value. There is a special variable called PSDefaultParameterValues that is a hashtable. The key takes the format “cmdletname:parameter”. So to set a default value for the -Computername parameter of Get-VM, I can run code like this:
$PSDefaultParameterValues.add("Get-VM:computername","CHI-P50")
Now, every time I run Get-VM it will connect to the specified host. If I want to connect to a different host all I have to do is specify it.
Get-VM -ComputerName chi-hv2
You can add values for just the cmdlets you use, or use wildcards. Put this code in your PowerShell profile script to add all the necessary entries into $PSDefaultParameterValues.
$cred = Get-Credential globomanticsadministrator $PSDefaultParameterValues.Add("*-VM*:Computername","CHI-P50") $PSDefaultParameterValues.Add("*-VHD*:Computername","CHI-P50") $PSDefaultParameterValues.Add("*-VM*:Credential",$cred) $PSDefaultParameterValues.Add("*-VHD*:Credential",$cred)
This will handle almost all of the cmdlets in the Hyper-V module. Every time I start PowerShell I’ll get prompted for the password. From then on, every Hyper-V command I run will automatically use the Computername and Credential value without me having to type anything.
All of that said, you will have to determine how much you want to set to a default. Adding default values might complicate some of your pipeline expressions, which you’ll see as you keep reading.
4. Measure VM Performance
Another feature I love is the ability to monitor VM performance. However, in order to do this, you must enable VM resource metering on each virtual machine. If it is enabled, then the virtual machine will have a property called ResourceMeteringEnabled set to True. This makes it easy to find virtual machines that do not have the setting enabled.
Since my primary VMs all start with CHI, I will go ahead and enable resource metering. This should take a one line command.
Get-VM -name CHI* | where {-not $_.ResourceMeteringEnabled} | Enable-VMResourceMetering
But because I’m using default parameter values everywhere, this fails. Without getting sidetracked into why, I can get this to work with a slight modification.
Get-VM -name CHI* | where {-not $_.ResourceMeteringEnabled} | foreach-object { Enable-VMResourceMetering -VM $_ }
Now that the virtual machines are properly configured I can gather usage information. Again, this command would normally work:
Get-VM | where ResourceMeteringEnabled | Measure-VM
But because of my default parameter values, I need to adjust the expression:
Get-VM | where {$_.ResourceMeteringEnabled -AND $_.State -eq 'Running'} | foreach-object { Measure-VM -vm $_ }
Even though this is a nicely formatted table, don’t forget that this is a collection of objects, so you can sort, filter, group, export or convert as much as you want. Think of the reporting possibilities. I know I am. Keep an eye out for a future article.
By the way, if you see an error about “Sequence contains no elements”, try disabling resource metering (I bet you can guess the cmdlet to use) and then re-enabling it.
3. Copy VM File
Here’s a nifty feature, especially if you are building an automatic provisioning system or spinning up a virtual machine as a part of a continuous integration build pipeline. With PowerShell and Hyper-V you can copy files from the host to the guest VM. With one caveat that the VM must be running Windows. Here’s a simple example.
Copy-VMFile -Name chi-win10 -SourcePath C:scriptssetup.txt -DestinationPath C:datasetup.txt -CreateFullPath -FileSource Host
The copy occurs from the Hyper-V host. So even though I ran this from a client desktop, the copy happened from the server. I’m using my PSDefaultParameterValue for the computername which is why you don’t see it. The SourcePath parameter value is relative to the Hyper-V server. The DestinationPath parameter is relative to the virtual machine. You can even force the command to create the full destination path structure. One thing you have to include is the -FileSource parameter which always has a value of Host. This could be a good candidate for a PSDefaultParameterValue to save yourself some typing. This is a handy tool to copy a single file to multiple virtual machines.
Copy-VMFile -VM (Get-VM CHI* | where state -eq 'running') -SourcePath C:scriptssetup.txt -DestinationPath C:datasetup.txt -CreateFullPath -FileSource Host
In this variation, I have an implicit pipelined expression for the -VM parameter to get all running virtual machines that start with CHI*. And even though I don’t show it, the command won’t copy the file if it already exists. So if you need to overwrite existing files, include the -Force parameter.
Unfortunately, this command can only copy a single file at a time. You can’t use it to copy an entire directory. However, you can use Foreach-Object and copy multiple files. Since the copy has to occur from the host, enumerating files also needs to happen on the host. But that’s no problem. I can use PowerShell remoting from the client to copy all files from C:Scripts on CHI-P50 to the CHI-WIN10 virtual machine.
Invoke-Command { dir c:scripts -file | foreach-object { Copy-VMFile -source $_.fullname -name chi-win10 -DestinationPath $_.FullName -CreateFullPath -FileSource host } } -computername CHI-P50
Or if you are inclined, you could create your own function that wraps around Copy-VMFile to make it easier to copy entire folders, assuming you didn’t want to use PowerShell Direct.
2. Measure Snapshot Usage
It is simple enough to list all snapshots on your Hyper-V server with the Get-VMSnapshot command.
What would be helpful would be to know how much space each snapshot is consuming. This isn’t especially difficult to calculate. Although you can’t do it remotely since the snapshots are stored on the Hyper-V host. Each snapshot includes a property indicating where the snapshot is stored. By combining that piece of information with the virtual machine ID, you can run code like this on the server. I’ll go ahead and wrap it in an Invoke-Command expression and run it remotely.
Invoke-command { Get-VMSnapshot * | Select VMName,Name,CreationTime, @{Name="SnapshotAge";Expression = { (Get-Date) - $_.CreationTime }}, @{Name="TotalSizeKB";Expression = { $m = Get-Childitem $_.path -Recurse -filter "$($_.id).*" -file | measure-object length -sum #round to the nearest KB $m.sum/1KB -as [int32] }} } -computername chi-p50
That’s nice information. Or I might want to format the results since it is possible a virtual machine may have multiple snapshots.
Invoke-Command { Get-VMSnapshot * | Select VMName,Name,CreationTime, @{Name="SnapshotAge";Expression = { (Get-Date) - $_.CreationTime }}, @{Name="TotalSizeKB";Expression = { $m = Get-Childitem $_.path -Recurse -filter "$($_.id).*" -file | measure-object length -sum #round to the nearest KB $m.sum/1KB -as [int32] }} | Sort VMName } -computername chi-p50 | Format-Table -GroupBy VMName -Property Name,CreationTime,SnapshotAge,TotalSizeKB
In this example, I’m getting the information remotely but formatting it locally.
Or do whatever you want with the results.
1. Total Storage
Lastly, here’s a fun one line command to figure out how much disk storage all of your virtual machines are taking or are prepared to take.
Get-VMHardDiskDrive -VMName * | Get-VHD | Measure-Object FileSize,Size -sum | Select Count,Property,Sum
The tricky part is that the Get-VHD command needs to be able to inspect the disk file where it resides which is most likely on the server. Once again, the easy fix is to use PowerShell Remoting.
Invoke-Command { Get-VMHardDiskDrive -vmname * | Get-VHD | Measure-Object FileSize,Size -sum | Select Count,Property,Sum } -computername chi-p50
If you try this, you’ll most likely get an eye-straining large number. It might be easier to format it to GB.
Invoke-Command { Get-VMHardDiskDrive -vmname * | Get-VHD | Measure-Object FileSize,Size -sum } -computername chi-p50 | Select Count,Property,@{Name="TotalGB";Expression={$_.Sum/1GB -as [int32]}}
I rounded the values to the nearest GB. Isn’t that easier to read? What you do with the information is up to you. But as you can see it isn’t difficult to discover.
The more you use the Hyper-V module in PowerShell, the more opportunities you’ll discover. I love putting these things together to solve problems or fill a particular need. I’ll be back with more PowerShell and Hyper-V fun in future articles.
Comments or Suggestions?
What do you think of the list? Are there any PowerShell hacks you’d like to see here? Let me know in the comments below and if they are worthy of inclusion I’ll add them to the list! Also, if anything is unclear don’t hesitate to let me know below and I’ll ping an answer right back as soon as possible.
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!