Powershell Open File Dialog Box

Do you ever require the users of your script to select which file to use?

I do. And it’s no fun trying to do decent error-handling when a user has to type the full path to a file. Nor is it any fun to be that user. So why not use the Windows Forms class of .Net to show the Open File Dialog Box?

Select-FileDialog function (rename to .ps1 or copy into your profile)

User happy. Me happy. You happy?

Setting Video Hardware Acceleration Level

It’s about time I follow up on the post Getting Video Hardware Acceleration Level.

I have created a simple, fire-and-forget script that sets the Hardware Video Acceleration Level for all your (Windows Server 2003 and Powered On) VM’s to Full. This way you can always enjoy smooth mouse movements when working in the console!

Set-VMVideoAccLevel (Rename to .ps1)

Enjoy!

Setting of advanced options with the VI Toolkit

It’s a bit tricky to find, but the API reference showed me where to look. And now I’m posting it here, so you don’t have to search any further.

First, connect to the Option Manager:
$SI = Get-View $ServiceInstance
$OM = Get-View $SI.Content.Setting

Query current values of advanced options:
$OM.QueryOptions(‘ADV_OPTION_NAME’)

To change or add an advanced option:

Create a new object to hold the new settings.
$newOption = New-Object VMware.Vim.OptionValue

Set the Key and Value you would like to add or change.
$newOption.Key = ‘ADV_OPTION_NAME’
$newOption.Value = 300

Use the UpdateOptions method to execute the change.
$OM.UpdateOptions($newOption)

Tadaa! :)

»crosslinked«

Track Datastore Free Space

I’ve got a great new script for you today!

We all love VMware for consolidating our servers. And we all know that virtualization requires lot’s of SAN space. But how do you keep track of the amount of space left on all your datastores? It’s pretty easy to create a report using the Powershell VI Toolkit. But who is going to read all those reports and compare them to yesterday’s values? You will most likely end up with a mailbox full of reports and still be surprized when yet another datastore fills up.

PeetersOnline to the rescue! How about storing the report every day and comparing today’s values to yesterday’s values? Then you can email yourself only the differences. The overview shows the datastores that changed since yesterday, the change rate and the current value. This way, you can easily assess if the datastore is about to fill up.

Because it is quite a large script (in Powershell terms) and it contains long lines that are not easily readable on my site, I have attached the script for you to download. It contains lot’s of comments, so you should be able to follow exactly what it does and how it works. If not, post a comment.

Enjoy!
Hugo

Track-Datastores (Rename to .ps1)

VMware Stats Oneliner

Today’s oneliner is a nifty little function that reports the average CPU and Memory usage for one or more of your VMs, calculated over the last x hours:

function Get-VMStat
{
param( $VM,[Int32]$Hours = 1 )
 $VM | Sort Name |
 Select Name, @{N=”CPU”;E={[Math]::Round((($_ |
 Get-Stat -Stat cpu.usage.average -Start (Get-Date).AddHours(-$Hours) -IntervalMins 5 -MaxSamples ($Hours*12) |
 Measure-Object Value -Average).Average),2)}}, @{N=”MEM”;E={[Math]::Round((($_ |
 Get-Stat -Stat mem.usage.average -Start (Get-Date).AddHours(-$Hours) -IntervalMins 5 -MaxSamples ($Hours*12) |
 Measure-Object Value -Average).Average),2)}}
}

A great way to use it is like this:

Get-VMStat (Get-VM) -Hours 4 | Where {$_.CPU -gt 50 -or $_.MEM -gt 75}

I’m sure you can figure out what that does. Be the first to give the correct answer in the comments to win everlasting glory! (or at least until the next oneliner…)

Enjoy!

Oneliner of the day

Do your fellow VMware Admins sometimes forget to fill in the Notes field for a new vm they create? Or would you like to change those inconsistent notes to something more accurate? Todays oneliner simply copies the Active Directory description field into your Virtual Infrastructure. It’s quite blunt about it. Besides asking if you really want to do so, it shows a lot of errors for servers that are powered off or cannot be found in AD. But it pretty much does the trick.

Get-VM | ForEach { Set-VM -VM $_ -Description (Get-QADComputer ($_ | Get-View).Guest.HostName.Split(“.”)[0]).Description }

If your vm’s always have the same name as defined in the VM Guest OS, you can suffice with:

Get-VM | ForEach { Set-VM -VM $_ -Description (Get-QADComputer $_.Name).Description }

If you want more advanced description synching, check out the full script.

Enjoy!

How many VMs in each LAN?

A nice challenge today, when trying to answer a question in the VMware VI Toolkit community:

How to get an overview of all my networks and the number of VMs connected to each of them?

It’s unfortunately not possible to get VMs, based on which port group they are connected to. So something like this will NOT work: Get-VirtualPortGroup | Get-VM. Let’s try to do it the other way around: Get all the VMs we have. Get the portgroups they are connected to. And then figure out a way to invert the table.

First step: Getting the VMs and their port groups:

$VMs = Get-VM | Select Name, @{N=”PortGroups”;E={Get-VirtualPortGroup -VM $_ | %{$_.Name}}}

OK. That wasn’t too bad. The old Get-VM doing it’s job there. And the Get-VirtualPortGroup can return all port groups for each vm. We are only interested in the name of each port group, though. Using Calculated Properties we can stuff it into a single line.

Now to turn this table around. Piping to Group-Object does not quite deliver the desired result. That’s because some VMs have multiple port groups assigned. Group-Object then groups by each unique combination of port groups instead of per unique port group. Bummer. Let’s just try to list all unique port groups first then:

$PGs = $VMs | %{$_.PortGroups} | Select -Unique

Hey, that’s pretty simple. Now a small script can build a new collection of objects representing port groups for us. And we can definitely calculate how many vms there are associated to each port group, by using the $VMs collection we created earlier!

$myCol = @()
ForEach ($PG in $PGs)
{
  $myObj = “” | Select PGName, NumVMs
  $myObj.PGName = $PG
  $myObj.NumVMs = ($VMs | Where {$_.PortGroups -contains $PG} | Measure-Object).Count
  $myCol += $myObj
}

That ought to do the job. Let’s give it a nice, orderly view and we’re good to go!

$myCol | Sort NumVMs -Descending | Format-Table -AutoSize

Mystery solved!

Some advice on creating Powershell scripts

Creating a Powershell script CAN be quite difficult. How to get the output you want? Where to start? Let me try to get you started by providing a structure you can follow. I’m not saying this is THE way, but it’s MY way. Hope it helps you and please do comment if you have additions or another way!

First: Scripts for gathering information.

1. What should my OUTPUT be?
It’s easier to start with the end. Start by determining what you would like your output to look like.

1.1 OBJECTS representing what?
It’s good practise generating objects as output. It allows you to use the full power of Powershell to further manipulate your output. So don’t use any Format-… cmdlets in your scripts! That being said, what should the output objects represent?
Let’s take a simple example, like generating an overview of server OS versions or free disk space or something similar. The output objects should represent the servers.

1.2 What PROPERTIES?
You all know objects have properties. If not, read Get-Help About_Object. What properties of the server objects are we interested in? In the example it could be Server Name (obviously), OS Version, Service Pack, Free Disk Space on C-partition, and so on.

2. WHERE to get the information?
Now that we know what objects to create and what properties to fill, let’s examine where to get all this info.

2.1 The EASY ones.
When the properties can all be found in one place, the script is simple. For example, the OS Version and Service Pack of a server, can all be found in Active Directory. The Quest AD cmdlet Get-QADComputer returns objects representing servers, which have properties including the ones we seek. All we have to do is FILTER the properties, like so:
Get-QADComputer | Select-Object Name, OSName, OSServicePack

And we’re all done!

2.2 Getting HARDER:
What if most properties can be found in this way, but some require additional calculations or “get-”ting? Then we can use CALCULATED PROPERTIES. The syntax is as follows:
Get-Something | Select-Object Name, @{N=”CalculatedPropertyName”;E={EXPRESSION THAT RETURNS THE DESIRED VALUE}}
For example, you want to get several properties of a harddisk, but one value should be converted from bytes to GB:
Get-WmiObject Win32_LogicalDisk | Select-Object DeviceID, @{N=”GBFreeSpace”;E={$_.FreeSpace/1GB}}

2.3 The CHALLENGE:
The most difficult scripts gather information from all over the place. But you’d still like to present it in a simple and convenient manner. This is when you should build your own output objects and create the properties yourself. In order to get this done for multiple objects in a collection, it is vital you loop through a collection of objects representing the same thing as your output objects. Consider the following example:
Let’s say you want to get some exotic characteristics for all your vm’s. For instance which server it is running on, the last 5 events that occurred and the average memory usage. These aren’t properties of any vm object you can get. So start by looping through all the vm objects you can get, but not before you create an empty collection to old your output objects:
$myCol = @()
$VC = Connect-VIServer MYVCSERVER
$vms = Get-VM
ForEach ($vm in $vms)
{
Then, inside the loop, create a custom output object along with the properties you’d like to see:
$myObj = “” | Select Name, Host, LatestEvents, AvgMemUsage
Then you can start calculating or “get-”ting the proper values for each of these properties. For example:
$myObj.Name = $vm.Name
$myObj.Host = ($vm | Get-VMHost).Name
$myObj.LatestEvents = ($vm | Get-Event | Select-Object -Last 5)
$myObj.AvgMemUsage = ($vm | Get-Stat -Memory -MaxSamples 1).Value
(These examples are off the top of my head and untested.)
Finally, add your output object to the collection and close the loop:
$myCol += $myObj
}

Next time: Scripts that actually change values or perform some other action. Stay tuned.
Hugo

Changing your VMware license server

Hi everybody, I’m back!

Before you ask, I’ve had a great holiday, thank you. Now let’s continue having fun with Powershell and the VI Toolkit!

I’ve showed you a trick earlier that allows you to find your Virtual Center Server Settings. The License Server settings for instance. But changing these settings can prove to be quite a challenge. Let’s take a look:

$SI = Get-View ServiceInstance
$LicMan = Get-View $SI.Content.LicenseManager

This is the way we connected to the License Manager. Get-Member shows us that the $LicMan variable has a method called ConfigureLicenseSource. This method required two parameters: first a reference to the ESX server and second an object with the new license server settings.

The “managed object reference” to the ESX server took some time to figure out, but it’s actually quite easy:

$ESXServerName = “ESXSERVER01″
$VMHost = Get-VMHost $ESXServerName
$hostref = ($VMHost | Get-View).MoRef

By now, you should know how to use Get-View a bit. The resulting object has a MoRef property which can be used in the ConfigureLicenseSource method.

The object containing the new license settings was also a bit of a puzzle. The method shows it wants an object of type “LicenseSource”. But after consulting the VMware Infrastructure API Reference, I found this type of object has no properties! Then how to set the license server name and port?
More searching yielded a different type of object “LicenseServerSource”. This object type has a property called LicenseServer, which accepts a string as input. This object extends the LicenseSource object (according to the API Reference) and it turns out you can use it in the method:

$LicServer = “27000@LicenseServer.domain.local”
$licsrc = New-Object VMware.Vim.LicenseServerSource
$licsrc.LicenseServer = $LicServer

Now we have all the info we need, and we can execute the method:

$LicMan.ConfigureLicenseSource($hostref,$licsrc)

Enjoy!