Be More Efficient with PowerCLI functions

Running a VMware environment is all about automation and how much you can reduce the overhead of RUN operations and daily maintenance tasks to get more quality time working on evolution projects, architecture and BUILD among other things. As a VI admin, I always loathed these daily tasks that I saw as a waste of time as they were not interesting from a technical point of view and could almost always be automated in some way or another.

Obviously, the best practice when it comes to automation is to implement a proper platform that is built for this very purpose such as Ansible, vRealize Automation and the likes, there are plenty of choices out there. However, it is not always possible to go to this length for various reasons in which case you need to get creative. PowerCLI becomes a prime choice for light automation tasks and being more efficient in general by working in the CLI rather than using the vSphere client. In this blog, I take it back to the basics and wanted to show you how you can build your own custom PowerCLI commands by using PowerShell functions. Note that I already talked about PowerShell function a few years back in the use case of interacting with REST APIs and in the SRM in PowerCLI series

Protect Your Data with BDRSuite

Cost-Effective Backup Solution for VMs, Servers, Endpoints, Cloud VMs & SaaS applications. Supports On-Premise, Remote, Hybrid and Cloud Backup, including Disaster Recovery, Ransomware Defense & more!

What are PowerShell functions?

Most scripting languages offer the possibility to create functions, others also call them subroutines I believe. Functions let you wrap a number of actions via a chunk of code under a single command. For instance, the function I used the most was one that would get me expanded information about datastores that would normally not get with “Get-Datastore” like provisioned space or a function to get guest volumes usage in VMs.

You can obviously get all that information by drilling into the PowerShell objects but it is time consuming and doesn’t scale when you need it across a large number of VMs.

Functions are useful both interactively to save time and be more efficient in the command line with your own “cmdlets” but also in scripts to avoid repetition and improve maintainability by establishing said set of actions only once.

Download Banner

In a most simplified manner, Functions are made of 4 main building blocks:

  1. A Name: This is what you will use to call your function
  2. Parameters: These are the named or positional arguments you pass to the function when calling it. (Optional)
  3. Output: Whatever the command will produce as an output. Never use “Write-Host” as it is only printed and not processed in the pipeline. (Optional)
  4. Error handling: This is incredibly important to ensure expected behavior and facilitate troubleshooting. Many admins out there don’t bother with error handling. While sometimes it is fine for a simple function you know like the back of your hand that never errors out, it is highly recommended to implement it in your functions

There are of course a lot more characteristics to PowerShell functions that could be mentioned but those make for a good starting point.

How to write a PowerShell function?

A PowerShell function can be only a few lines long or it can be as complicated as you make it. However, this is an introductory article so we will stick to a simple use case that you can build on to fit your specific needs down the line.

We will use one of my functions to list datastore space usage as an example here, one of the custom-made functions I used the most by the way, my number one function probably being Pingou to test TCP ports that I still use regularly more than 6 years later.

  • First define your function with a name
  • Function Get-VMFSDatastore {

    }

  • Then add parameters. Here I only have one. Note that you can add all sorts of checks to validate the arguments passed to the parameters such as authorized values, scripts, but the most common one is to check for a type by setting it in between square brackets

For instance, [string] means it must be a single string. [string[]] means it can be a collection of several strings.

Below we define that $Datastore is in first position as a positional parameter, it can be fed from the pipeline, must be of type VmfsDatastore and can be a collection of several objects.

I also set a default value by setting $Datastore to all datastores of type VMFS.

Function Get-VMFSDatastore {
param(
[parameter(position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyname=$True)]
[VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.VmfsDatastore[]]
$Datastore = (Get-Datastore | where type -eq VMFS)
)

}

  • Now this is specific to using a function in the pipeline. Meaning the objects can come from the pipeline instead of directly set as an argument manually

Process tells PowerShell to process each object from the pipeline. You can also use Begin and End to run commands before and after that.

Function Get-VMFSDatastore {
param(
[parameter(position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyname=$True)]
[VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.VmfsDatastore[]]
$Datastore = (Get-Datastore | where type -eq VMFS)

)

Process {

}

}

  • Next, we are going to loop through each datastore object in a ForEach loop

Function Get-VMFSDatastore {

param(
[parameter(position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyname=$True)]
[VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.VmfsDatastore[]]
$Datastore = (Get-Datastore | where type -eq VMFS)

)

Process{

ForEach ($DS in $Datastore) {

}

}

}

  • At this point we are set and just need to write the code that will do what we want, in this case it’s a bit of easy math using values from the extensionData property of the datastore object to get things like provisioned space in GB and Percent as well as usage, free… I like having too many metrics than not enough, this also came in handy in a few reporting scripts

I really like the [pscustomobject] type and I probably use it way too much or in instances where other types would be more appropriate but I just love its flexibility and readability so much.

Function Get-VMFSDatastore {

param(
[parameter(position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyname=$True)]
[VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.VmfsDatastore[]]
$Datastore = (Get-Datastore | where type -eq VMFS)

)

Process{

ForEach ($DS in $Datastore) {

if ($ds.type -eq “VMFS”) {

# Check if the datastore is expandable.

if ($ds.Accessible) {

$hostId = [string]($ds.ExtensionData.Host | where {$_.mountinfo.Accessible -and $_.mountinfo.Mounted} | select -ExpandProperty key -First 1)
$DsHostDsView = get-view $hostId -Property ConfigManager.DatastoreSystem
$DsHostDsView = get-view $DsHostDsView.ConfigManager.DatastoreSystem

$Expandable = $DsHostDsView.QueryVmfsDatastoreExpandOptions($ds.id)
if ($Expandable.count -eq 0) {$Expandable = $false}
else {
$LunSize = ($Expandable.info.VmfsExtent.end.block – $Expandable.info.VmfsExtent.start.block) * $Expandable.info.VmfsExtent.start.blocksize / 1GB
$FreeSpaceOnLun = [math]::round($LunSize – $ds.CapacityGB,2)
$Expandable = “+$($FreeSpaceOnLun)GB”
}

} else {$Expandable = $false}

# Process capacity and provisioning data.

$CapacityGB = [Math]::Round(($ds.extensiondata.summary.capacity / 1GB),2)
$FreeGB = [Math]::Round(($ds.extensiondata.summary.FreeSpace / 1GB),2)
$UsedGB = [Math]::Round((($ds.extensiondata.summary.capacity / 1GB) – ($ds.extensiondata.summary.FreeSpace / 1GB)),2)
$ProvisionedGB = [Math]::Round((($ds.extensiondata.summary.capacity / 1GB) – ($ds.extensiondata.summary.FreeSpace / 1GB) + ($ds.extensiondata.summary.Uncommitted / 1GB)),2)

$ProvisionedPercent = [Math]::Round($ProvisionedGB / $CapacityGB * 100,1)

[pscustomobject]@{
Name = $ds.name
State = $ds.Accessible
CapacityGB = $CapacityGB
FreeSpaceGB = $FreeGB
FreeSpace = “$([math]::round($FreeGB / $CapacityGB * 100,1))%”
UsedSpaceGB = $UsedGB
ProvisionedGB = $ProvisionedGB
Provisioned = “$ProvisionedPercent%”
NbRunningVMs = ($ds | Get-VM | where powerstate -eq Poweredon).count
Expandable = $Expandable
}

} else {“$($Datastore.name) is not a VMFS datastore”}

}

}

}

Let’s try the function

Once the function is ready, you either import it somehow or add it to a module so it is auto-loaded (this is what I do, I have a collection of home-made modules in my PowerShell profile).

Note that you can use format-table as it is automatically set to list above 4 properties in the output.

> Get-VMFSDatastore

Name : RAID5-15K
State : True
CapacityGB : 1394,25
FreeSpaceGB : 500,59
FreeSpace : 35.9%
UsedSpaceGB : 893,66
ProvisionedGB : 2051,02
Provisioned : 147.1%
NbRunningVMs : 10
Expandable : False

As you can see, I obtain all the information I need about the datastores. I only have one VMFS datastore in this lab environment but I also have a VSAN datastore that doesn’t appear here because it doesn’t match the [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.VmfsDatastore[]] type we set in the parameter’s validation.

get-datastore | select name,type

Name Type
—- —-
LAB01-VSAN vsan
RAID5-15K VMFS

Wrap up

Still to this day, many VI admins are still reluctant to use the CLI as they are used to the vSphere client and feel like things would take longer to do otherwise. The reality is the opposite as using PowerShell lets you work programmatically and iterate over many entities, essentially making your workstation work while you go grab a coffee.

I also believe that PowerShell is one of the easiest languages to learn as the syntax is incredibly forgiving compared to bash or python and the cmdlets usually have descriptive names and parameters that makes it a fairly intuitive learning experience overall. PowerShell Functions will let you take this journey just a bit further and make your daily admin life that much easier.

Note that it is always a good measure to include error handling in your functions but this might be the subject for a future blog.

Check out what’s new in PowerCLI 12

Follow our Twitter and Facebook feeds for new releases, updates, insightful posts and more.

Rate this post