How to build a GUI tool for VMware PowerCLI

Save to My DOJO

How to build a GUI tool for VMware PowerCLI

If you are a vSphere administrator, you and your team most likely have a bunch of manual tasks that could be significantly sped up through automation in any shape or form. When it comes to it, VMware PowerCLI and PowerShell is a great option that allows you to easily add a GUI which will be beneficial for various reasons such as:

    • You may not feel comfortable handing the keys to the shell to inexperienced scripters who could, involuntarily, make mistakes in production. “With great power comes great responsibility!
    • You don’t want to fire up the shell and go through the usual Connect, change dir etc… every time you want to use this script.

A great way for offering quick and easy access to a custom script is through the use of a Graphical User Interface (GUI). In this example, we will use PowerShell forms to create a very simple GUI tool that will deploy a virtual machine based on a Template.

Download the tool from our Github repository.

Altaro Gui Tool

The tool we are going to build is purposefully simple and shouldn’t be used as-is in production as it has almost no checks and has no OS customization capabilities.

Windows Forms

In order to build a Graphical Interface, we are going to use Windows Forms. They are based on .Net and have been around since the dawn of times. It is what powers most of the windows you interact with. There are different ways to create a GUI:

    • Online tools such as PoshGUI lets you design your GUI and generates the PowerShell code for you. It is a time-saver; however, it requires a paid monthly subscription since March of 2021 which will set you back around $7 per month.
    • Visual Studio with PowerShell Pro Tools. Also, a paid option that starts at $10 per month. Most relevant option if you deal with advanced and complicated UIs.
    • Manually within PowerShell ISE. Slightly more labor-intensive option but sufficient for simple UIs.

In this blog, we are building the GUI manually as there isn’t much to it. It will leverage a few types of GUI components such as labels, textboxes, comboboxes, etc… However, there are plenty of other item types that you can use to enhance your graphical interface. Refer to the Microsoft documentation for more information.

Launching the script quickly

We can have the script behave like an installed software by launching it by double-clicking an icon. In this case, we will create a short .bat file in the same folder as the .ps1 file and add the following line.

powershell.exe -WindowStyle Hidden -file .\%~n0.ps1

Whenever you launch this file, it will only open the GUI. You can then place it on one of your organization’s network shares and instruct your colleague to always use this one so they always get the latest version should you make changes to it.

Step 1 – Make sure your script works

You will not build a GUI for the sake of it. Before starting with Windows Forms, you need to ensure that the script you want to build a GUI for has a use for it and is 100% functional. Building a GUI around a bad script is not advised. At the end of the day, a GUI is nothing but a wrapper for cmdlets and parameters.

For the sake of this demonstration, we are deploying a virtual machine from a template with only a few parameters. As you can see we don’t even customize it.

    • VM name
    • Number of CPU(s)
    • Template
    • Datastore
    • Whether the VM should be powered on or not at the end

In terms of PowerCLI commands, this can be achieved with the following:

Connect-VIServer -Server core-vc.lab.priv

$NewVM = New-VM -Name “My-Test-VM” -VMHost “r430.lab.priv” -Datastore “RAID5-15K” -Template “Ubuntu20LTS”

Set-VM -VM $NewVM -NumCpu 2

Start-VM -VM $NewVM

There is only 4 lines of code that could easily be parameterized and run interactively. Meaning, the rest of the code in the script we are writing is dedicated to the GUI.

Obviously, in a real-life scenario, there would be a lot more parameters, checks and error handling.

Step 2 – Building the GUI

In this section, we are breaking down the creation of the GUI component by component.

Note that I will only describe each type of component once along with properties of interest. You can easily figure out the rest of the script by reading through it and changing values to see what happens as it is rather simple.

    1. Create the main form. Amend the following code to fit your needs.

You can change the size of the window with $Form.ClientSize. Make sure that $Form.ShowDialog() is at the very bottom of the script.

The AcceptButton property of the $Form item lets you specify which button is highlighted by default. Meaning you can press Enter to connect instead of having to click on it.

I also added a line to disconnect the vCenter session when closing the form. This isn’t mandatory but it is a good measure, especially when working in PowerShell ISE as it retains active sessions.

Add-Type -AssemblyName System.Windows.Forms

[System.Windows.Forms.Application]::EnableVisualStyles()

$Form = New-Object system.Windows.Forms.Form

$Form.ClientSize = ‘400,400’

$Form.text = “My First GUI Tool”

$Form.TopMost = $false

$Form.MaximizeBox = $false

$Form.FormBorderStyle = ‘Fixed3D’

$Form.Font = ‘Microsoft Sans Serif,10’

# Press Enter to click the “Connect” button.

$Form.AcceptButton = $vcenterButton

# Disconnect vCenter when closing the form.

$Form.add_FormClosing({if ($VIServer.IsConnected) {Disconnect-VIServer $VIServer -Confirm:$false}})

# Display main form. To put at the end.

$Form.ShowDialog()

Altaro Gui Main Form

    1. LABEL, TEXTBOX and BUTTON – We now add a label, a text box and a button to connect to vCenter.

You can move the objects by changing the coordinates in system.drawing.point(xx,yy).

Whenever you add a GUI component, you need to declare it in $Form.controls.addrange(@(…)).

You need to use the Add_Click() method on button items to set an action on click. In this case, when the button is clicked, the “Invoke-vCenterButton” function is triggered (you can try it by replacing it with something like “Get-Service | ogv” for instance).

$vcenterLabel = New-Object system.Windows.Forms.Label

$vcenterLabel.text = “vCenter”

$vcenterLabel.AutoSize = $true

$vcenterLabel.width = 25

$vcenterLabel.height = 10

$vcenterLabel.location = New-Object System.Drawing.Point(17,18)

$vcentertextbox = New-Object system.Windows.Forms.TextBox

$vcentertextbox.multiline = $false

$vcentertextbox.width = 200

$vcentertextbox.height = 20

$vcentertextbox.location = New-Object System.Drawing.Point(105,14)

$vcenterButton = New-Object system.Windows.Forms.Button

$vcenterButton.text = “Connect”

$vcenterButton.width = 75

$vcenterButton.height = 20

$vcenterButton.location = New-Object System.Drawing.Point(314,14)

$vcenterButton.Font = ‘Microsoft Sans Serif,9’

$Form.controls.AddRange(@($vcenterButton,$vcentertextbox,$vcenterLabel))

$vCenterButton.Add_Click({Invoke-vCenterButton})

GUI tool Vcenter Connection

    1. COMBOBOX – You probably use normalized CPU counts on your VMs to ensure optimal NUMA placement. Let’s add a drop-down list to only allow certain CPU counts.

I used a hard-coded list of values that I pipe into the Item.Add() method in order to include them to the menu. Note that we will also use combobox items for VMHosts and Templates.

As specified previously, don’t forget to add the new items to $Form.controls.addrange(@(…)).

You will also notice that I disable the CPU drop-down list at the beginning. They will be unlocked when vCenter is connected. This is also applicable to other items such as datastores, which depend on the selected host or cluster.

The selected value is then stored in $cpuComboBox.Text.

$cpucnt_Label = New-Object system.Windows.Forms.Label

$cpucnt_Label.text = “CPU count”

$cpucnt_Label.AutoSize = $true

$cpucnt_Label.width = 25

$cpucnt_Label.height = 10

$cpucnt_Label.location = New-Object System.Drawing.Point(17,94)

$cpucnt_Label.Font = ‘Microsoft Sans Serif,10’

$cpuComboBox = New-Object system.Windows.Forms.ComboBox

$cpuComboBox.width = 200

$cpuComboBox.height = 20

$cpuComboBox.location = New-Object System.Drawing.Point(105,94)

$cpuComboBox.DropDownStyle = “DropDownList”

$cpuComboBox.SelectedItem = $cpuComboBox.Items[2]

@(1,2,4,8,12) | ForEach-Object {[void] $cpuComboBox.Items.Add($_)}

$cpuComboBox.enabled = $true

GUI CPU Count

    1. CHECKBOX – Let’s add a checkbox for the sake of it. We can use it to specify whether the VM starts after the deployment or not.

The label area is included in the checkbox item which simplifies its use. Use the “Checked” property in your conditional statements.

Like any other item, this should be added to $Form.Controls.AddRange

$PwrCheckbox = new-object System.Windows.Forms.checkbox

$PwrCheckbox.Location = new-object System.Drawing.Size(17,254)

$PwrCheckbox.Size = new-object System.Drawing.Size(250,50)

$PwrCheckbox.Text = “Power on new VM”

$PwrCheckbox.Checked = $false

GUI, Deploy New VM

    1. Complete the form with the items required for the parameters of the cmdlet you will run when you hit “Deploy”.

Keep in mind that this is a simplistic example aimed at providing understanding. It does not fulfill the requirements for a production-ready VM deployment tool.

Step 3 – Utility functions

Triggering functions when interacting with an item is a great way to make the script more readable, easier to maintain and is the best practice in general. You shouldn’t put processing tasks in the main Form section.

In this section, I will describe the purpose and actions of these functions.

GUI Utility functions

Connect button: Invoke-vCenterButton

Before doing anything else, we want the user to connect to vCenter to be able to pull information about the environment.

    • SECTION A: Try to connect to vCenter and put the output in a variable.
    • SECTION B: If the connection succeeded, disable the vCenter fields.
    • SECTION C: Then enable the other fields in the form.
    • SECTION D: Populate the vmhost and template comboboxes.
    • SECTION E: If the connection didn’t succeed, display a warning popup with the error message and change the button text to “Retry”.
Function Invoke-vCenterButton {

# SECTION A

$VIServer = Connect-VIServer -Server $vcenterTextBox.Text

if ($VIServer.IsConnected) {

 

# SECTION B

$vcenterButton.Enabled = $false

$vcenterButton.Text = “Connected”

$vcenterTextBox.Enabled = $false

# SECTION C

$deployButton.Enabled = $true

$VmName_textbox.Enabled = $true

$vmhComboBox.Enabled = $true

$templateComboBox.Enabled = $true

$cpuComboBox.enabled = $true

# SECTION D

$vmhost = Get-VMHost -Server $VIServer | where connectionstate -eq connected

$vmhComboBox.Items.Clear()

$vmhost.Name | Sort | ForEach-Object {[void] $vmhComboBox.Items.Add($_)}

$Templates = Get-Template

$templateComboBox.Items.Clear()

$Templates.Name | Sort | ForEach-Object {[void] $templateComboBox.Items.Add($_)}

} else {

 

# SECTION E

Invoke-WarningPopup -WarningTitle “Connection failed” -WarningBody $error[0].exception.message

$vcenterButton.text = “Retry”

}

}

Case of an unsuccessful vCenter connection.

Case of an unsuccessful vCenter connection.

Datastore button: Invoke-DatastoreButton

The datastore section could also be a drop-down menu, however, we made it a button here for the sake of the example.

The button item will be enabled once a host is selected. When a datastore is selected, the choice is stored in a label instance next to it. Every time the selected host changes, the datastore label is cleared as not all hosts will have the same connected datastores.

The function associated with the datastore button is a one-liner that displays the list of available datastore on the host.

It uses Out-GridView with the -passthru switch which lets you select a record and stores the result in the associated label item.

Function Invoke-DatastoreButton {

$datastoreLabel.text = Get-VMHost $vmhComboBox.Text | Get-Datastore -Server $VIServer | where state -eq available | Out-GridView -PassThru | select -ExpandProperty Name

}

Deploy button: Invoke-DeployButton

This script being simplified, the deploy button does a few checks and proceeds to the deployment. In a production-ready tool, you would need a function dedicated to running a large range of checks and verification to ensure a safe deployment such as compute resources, free storage, provisioned storage…

    • SECTION A: Series of checks to make sure all the fields are populated and the VM name is not already used. A variable is populated with the parameters of the New-VM cmdlet.
    • SECTION B: If something isn’t right, a warning popup is invoked including the list of missing items.
    • SECTION C: The button is disabled during the deployment and the VM is deployed with the parameter object.
    • SECTION D: If necessary, the CPU count is updated on the VM.
    • SECTION E: Power on the new VM if the checkbox is checked.
Function Invoke-DeployButton {

 

# SECTION A

$deployparams = @{Server = $VIServer}

if (!$templateComboBox.Text) {$WarningBody += “Template not set`n”} else {$deployparams.Add(‘Template’,$templateComboBox.Text)}

if (!$vmhComboBox.Text) {$WarningBody += “Host not set`n”} else {$deployparams.Add(‘VMHost’,$vmhComboBox.Text)}

if (!$datastoreLabel.Text) {$WarningBody += “Datastore not set`n”} else {$deployparams.Add(‘Datastore’,$datastoreLabel.Text)}

if (!$cpuComboBox.Text) {$WarningBody += “CPU not set`n”}

if (!$VmName_textbox.Text) {$WarningBody += “VM name not set`n”}

elseif (Get-VM $VmName_textbox.Text) {$WarningBody += $($VmName_textbox.Text) already used`n”}

else {$deployparams.Add(‘Name’,$VmName_textbox.Text)}

# SECTION B

if ($WarningBody) {

 

$WarningBody = @(“Issues:`n$WarningBody)

Invoke-WarningPopup -WarningTitle “Missing fields” -WarningBody $WarningBody

} else {

# SECTION C

$deployButton.Text = “Deploying”

$deployButton.enabled = $False

$NewVM = New-VM @deployparams

# SECTION D

if ($NewVM.NumCpu -ne $cpuComboBox.Text) {Set-VM -VM $NewVM -NumCpu $cpuComboBox.Text -confirm:$false}

# SECTION E

if ($PwrCheckbox.Checked) {Start-VM -VM $NewVM}

$deployButton.enabled = $True

$deployButton.Text = “Deploy”

}

}

Case of missing items.

Case of missing items

Wrap up

Writing PowerCLI scripts is a refreshing task for a vSphere administrator, and a favorite of mine, as it involves a great deal of creativity and problem-solving. However, resorting to a simple graphical user interface can open up a script to a wider population of users that may not have scripting experience.

If you have a use case and want to get started with PowerShell Forms, you can start by building on the example of this article to add features and customize it.

The GUI based script presented in this article shows you how to get started with the most common use case of deploying a virtual machine. However, keep in mind that this is a simplified version. Including error handling and a wide range of checks is paramount to ensure the script is used within certain boundaries and cannot cause any harm in the environment.

If you want to learn more about PowerCLI, you can get our free ebook PowerCLI – The Aspiring Automator’s Guide or watch our free on-demand webinar How to Become a PowerCLI Superhero.

Altaro VM Backup
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

Leave a comment

Your email address will not be published. Required fields are marked *