Discovery with Powershell – Script to Get Screenshots for VMs
In the IT world, Administrators love to debate which technology is better. Mac vs. PC, Android vs iOS, you name it we’ve argued over it. One that I’ve been seeing a lot of over the last few years is vbScript vs Powershell. As you probably know, I stayed in the vbScript world for quite a while because it was a safe, known haven that suited me. But then I came to Powershell, and learned how you can discover what’s going on in Powershell so easily. This started with using a great Integrated Scripting Environment (ISE) like PowerGUI or PowerShellPlus, but then I discovered a few other commands that helped me discover information about the objects I was working with. Specifically Get-Member. Read on to see how I recently used Get-Member to solve a problem.
Background
It all started with a newsletter I received from Veeam. Veeam Backup and Replication software is a great little backup package for your virtual environment and they have a pretty good community over here. They also have a great community manager, Gostev–and a good community manager is absolutely key to growing a great community–who always puts a little blurb in front of the newsletter. What’s nice about this blurb is it’s not always about Veeam’s software. Often he talks about things that have happened, things he’s working on, etc. One thing he mentioned was an ability in vSphere 5 that allows you to get a screen shot of a VM’s console screen by simply putting in an address in your browser. How cool! The only problem is it requires a special identifier that, of course, isn’t that easy to discover. But after seeing this, I immediately thought this would be a fantastic opportunity for a little Powershell script. But how?
Research
Luckily, Gostev included a link to a blog article at VMware which showed a PowerCLI (that’s VMware’s name for their Powershell cmdlets) script for getting that reference name we needed (called MoRef). So I clicked on the link and started looking over the script when I ran into the first problem, which was the script was using a cmdlet I wasn’t familiar with–the second problem I already knew about was this technique requires vSphere 5 and I only had vSphere 4 installed at my work. I still wanted to try it, and who knows, maybe it will work for vSphere 4 too? I could get lucky, right? Listening to the crickets…
Foreach ($VM in Get-CIVM) {
I’m not familiar with Get-CIVM so it was time to use one of those discovery techniques to find out about it!
Get-Command Get-CIVM get-command : The term 'get-civm' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + get-command get-civm + ~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (get-civm:String) [Get-Command], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException,Microsoft.PowerShell.Commands.GetCommandCommand
Not unexpected, but clearly meant that discovering the MoRef value was going to be a bit more difficult. I know I can use Get-VM with the PowerCLI tools and get information about my vSphere VM’s, so we’ll have to look at it and see if we can get the MoRef from there. For reference (we’ll need it) here’s the code from VMware (I’ve snipped out the parts we don’t need):
Foreach ($VM in Get-CIVM) { $VM | Select Name,` @{N="VM MoRef";E={$VM.ExtensionData.VCloudExtension[0].Any[0].VmVimObjectRef.MoRef}},
So the first thing we want to do is use the Get-VM cmdlet on a VM and then use the Get-Member cmdlet on our new variable to see what properties and methods are available to us. I’m running this from the pre-configured PowerCLI Powershell prompt and I’ve already used Connect-VIServer to connect to my vCenter server. By the way, GM is the alias for Get-Member.
Interesting, notice towards the bottom of that screen shot there’s a property called ExtensionData? That’s the same property as the VMware code was using above. Let’s see where that leads us.
Isn’t that interesting! There’s actually a MoRef property right under there. You don’t suppose it could be that easy, could it? Notice I used the -MemberType on the GM command? I did this because there were a TON of methods for this property and I couldn’t fit everything into the screen shot, but it’s a nice little parameter to limit your output if all you’re looking for is a property, or a method.
So let’s take a look at this MoRef and see if it’s the data we want.
Well, turns out there are actually 2 properties to MoRef, Type and Value. But it looks like Value is exactly what we’re looking for.
The Script
Now we have the MoRef value, the rest of the script is pretty simple. Remember to make this work all we need to do is create a URL with the right format and the MoRef value in it. Then we need to launch a browser and navigate to that URL. I also want to give the script a ton of flexibility as far as accept input such as string parameters, piping strings and objects as well as simply prompting the user for the VM name.
Let’s tackle the input first in our Param’s section.
Param ( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String[]]$Name, [string]$VMHost = "vCenterServer" )
Two parameters for this script, $Name and $VMHost. $Name is where most of the work is happening. Using Parameter I set it to be a required field and to accept information from the Pipeline. Additionally I use ValueFromPipelineByPropertyName and what this does is lets me pick and choose exactly what information out of the piped object I want. Since Get-VM uses the property Name to store the name of the virtual machine I have to name my variable the same thing. Additionally I’ve defined name as an array of strings by using [string[]]. If no value for $Name is provided to the script it will prompt you, and you can enter multiple VM names that way. You can also pipe a value from Get-Content since the output to that is a string and you can pipe the results from Get-VM since that’s an object with the Name property.
The second parameter is much more straightforward and just tells the script what the vCenter server is. This doesn’t have to be a vCenter server either, it call also be the host name of the vSphere server.
Since we’re using the pipeline we need to use the Begin/Process/End blocks, so in Begin I load the PowerCLI snapin and connect to the vCenter server.
Begin { #Load VMWare CLI cmdlets Try { If (-not (Get-PSSnapin VMware.VimAutomation.Core)) { Add-PSSnapin VMware.VimAutomation.Core -ErrorAction Stop } } Catch { Write-Host "`n`nUnable to load VMware CLI cmdlets, are they installed on this host?"; Break } #Now connect to vCenter using your credentials Connect-VIServer $VMHost -ErrorAction Stop }
I also decided to use a Try/Catch block to detect if I have some problem loading the PowerCLI snapin, which could happen if PowerCLI isn’t installed on the workstation. I haven’t talked about Try/Catch before but it’s a great way of trapping errors for a very specific section of your script but it does require an actual stop in the code to work. You may have noticed that a lot of errors can happen in Powershell and it will just keep going without stopping, so to make sure it stops I use the -ErrorAction Stop parameter (which is a common parameter) on the Add-PSSnapin cmdlet. That way if there’s a problem it stops, which triggers the Catch scriptblock and reports the error.
The Process block is pretty easy. Simply loop through my $Name array, get the VM information and launch the browser.
Process { Foreach ($VM in $Name) { $MoRef = (Get-VM $VM).ExtensionData.MoRef.Value Start-Process -FilePath "https://$VMHost/screen?id=$MoRef" } }
Why use Start-Process instead of invoking the Internet Explorer ComObject, you ask? This goes back to making it easy for the user, as well as respecting their configuration settings. A lot of administrators–most?–don’t use Internet Explorer, myself included so I wanted to make sure to launch the default browser for this script. Start-Process with the -FilePath parameter is the technique you can use for that.
Testing
Here was where my second problem came in. The above script works great in my environment and I can test that the URL is properly formed but my web page comes up completely blank. No way of knowing it’s actually working right without testing it on a vSphere 5 system. Spiceworks to the rescue! I sent out a help request to the VMware forum group and Hubba Bubba (Ben) chimed in and gave it a whirl. Luckily everything worked out on the first try and he even posted a YouTube video of the results.
Notice all of the SSL errors? This is caused because he’s using a vCenter appliance, which isn’t integrated with his Active Directory. If you’re running full-blown vCenter you won’t run into that problem.
If you want the full source code to the script, you can click here to go to the Spiceworks Scripting Center.
Question: what if there is a black screen on a vm, and you would need to make a mouse movement in order to see what’s currently on the screen ?
Good question. In testing I never got a blank screen so I believe the process will give you SOMETHING. But I never specifically tested for THAT, so reality might be different. Let me know your results!