Writing effective scripts using VMware PowerCLI

20171224-00Lately I have been busy writing some Windows PowerShell scripts for a vSphere environment. I noticed that there are some similarities between learning a spoken language and a programming language. In both cases you start by learning the grammar and vocabulary and develop your skills by a lot of practicing. But for both skills, when you have not used them for a while, the skills will fade.
While writing and testing my scripts, I realized that a good preparation and a structured way of working will help you becoming more productive and making fewer mistakes.
This post is not a full blown Windows PowerShell course, but contains some insights I would like to share with you. If this is all new, I recommend following a PowerShell Getting Started training. Pluralsight offers over 11 Windows PowerShell courses from beginner to expert level. So if you are relatively new to Windows PowerShell and the VMware PowerCLI, please read on.

Preparation

Windows PowerShell has been a regular component of Windows desktop (XP, 7, 8.x, 10) and Windows Server (2003, 2008, 2012, 2016) versions. You can start by typing “PowerShell” in the Windows 10 “Search Windows”. Windows PowerShell appears to be a blue version of the well known “Command Prompt”, but has a lot more to offer. Now, you can run PowerShell commands, which are called a cmdlets. BTW, PowerShell is also available for Linux and macOS, but that is a different story.

If you want to interact with VMware vSphere and other VMware products, you will need some additional cmdlets. These are offered as a package named VMware PowerCLI. Version 6.5.0R1 can be downloaded as an executable.

Now you can start running cmdlets from the prompt. However if you want to run the same series of commands on a regular basis, it is more convenient to store these in a script. To create a script, you will need a text editor. Windows Notepad or a more advanced editor, like Notepad++ will do.
Windows PowerShell also comes with an “Integrated Scripting Environment”, the Windows PowerShell ISE. The ISE offers many benefits over Notepad, for an overview, see this one.
There are also some other great tools like Atom and Visual Studio Code, which not only support PowerShell, but practically all popular programming languages.

To have the VMware PowerCLI cmdlets automatically loaded, create a profile file.

Start the ISE and copy / paste the following command:

if (!(Test-Path -Path $PROFILE.AllUsersAllHosts)) { New-Item -Type File -Path $PROFILE.AllUsersAllHosts -Force }

20171224-01Figure 1

Open the profile file with following command:

psedit $profile.AllUsersAllHosts

and add the following line:

& "C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1"

20171224-02Figure 2

Now save your work, and close the ISE. Then start the ISE again.

20171224-03Figure 3

Now, the PowerCLI will automatically be loaded.

Note: VMware PowerCLI Version 6.5.1 comes with a new way to install, see this post for a detailed explanation and how to change the profile file.

More on profiles in this post.

Can you read this?

You wrote a nice script that does the tedious job in seconds. However many months later, your nice scripts needs some modification. What was pretty clear when you wrote the script, now looks like a big question mark. It takes a lot of time to understand your creation and to make the necessary adjustments. How can you prevent this?

Here is a random piece of code:

$i=0; $ListofServers=@()
foreach($line in get-content $inputfile){if($i -eq 0 -and $line -match "^Hostname,") {continue}
if($line -notmatch "^\s*#"){$X=$line.Split(",")
if($X.Count -lt 3)
{write-host -foregroundcolor Red "Bad format found in CSV file - please use Syntax: Hostname,IP address,User"}
$ListofServers += @{Hostname=$X[0] ; IP=$X[1] ; User=$X[2]}}
$i++}

Here we have the same piece of code, but with with some enhancements. In contrast to, for example Python where the use of indentation is mandatory for functions, similar rules do not apply to PowerShell. However indentation improves the readability of code, especially with loops and conditional statements.

$i=0;
$ListofServers=@()
foreach($line in Get-Content $inputfile){
   if($i -eq 0 -AND $line -match "^Hostname,") {continue}  #headline starts with "Hostname" and must not be used
   if($line -notmatch "^\s*#"  )
   {
        $X=$line.Split(",")
        # input file must have 3 fields
        if($X.Count -lt 3)
        {
            Write-Host -ForegroundColor Red "Bad format found in CSV file - please use Syntax: Hostname,IP address,User"
        }
        $ListofServers += @{Hostname=$X[0] ; IP=$X[1] ; User=$X[2]}
    }
    $i++
}

Comment please

Adding comments is also a way to improve your code. The golden rule is to only add comments where questions may arise. Do not add comments where the functionality of the code is already obvious from the cmdlets used.

Single line comments start with a hash symbol, everything to the right of the # will be ignored. You can start a new line with a comment, or at the end of a line with code

Now that we are talking about comments, it is a good habit to start each script with a multi-line block comment, also known as Comment Based Help (CBH).
Here you can tell a bit more about the functionality of your script, mandatory and optional parameters, accompanied with some examples, version control and so on.
A multi-line block comment starts with “<#” and ends with “#>”. It is also a good habit to include topics, example:

<#
.SYNOPSIS
Brief description what the script does

.EXAMPLE
Some examples how to use the script and the parameters

.INPUT
Description of the mandatory and optional inputs

.OUTPUT
Description of the output

.VERSION
Version History

#>

More info on CBH can be found here. Later on in this post, I will point you to an example PowerShell script, provided with comments.

Break it into pieces

Scripts can be small and very effective. On the other hand, it can also be a necessity that your script must perform many tasks, some of these may be repetitive. It is time to divide your script in building blocks, also known as functions. More on functions can be found here, the following example also comes from this site. To create a function, call the function keyword followed by a name for the function, then include your code inside a pair of curly braces.

function Add-Numbers
{
 	$args[0] + $args[1]
}
PS C:\> Add-Numbers 5 10
15

Functions are also great for reuse of code. As an example, during the execution of more complex scripts, I like to see some progress on screen, but also information written into a log file. One day, in a script a found a nice function that performs this task very well. The example script, I mentioned before contains this “logit” function.

Variables also play an important role in scripts. A few rules; only create a variable when you really need one. Give variables an appropriate name, for long variable names, use capitalization. Talking about capitalization, PowerShell cmdlets and parameters also make use of capitalization and command completion.
Example, in the lower part of the ISE, type the following characters “
get-he”, followed by the [Tab] key :
As soon as you start typing, Intellisense jumps in. When you see “Get-Help” on your screen, type a space, followed by the minus sign “-”, now press the [Tab] key several times and see all parameters
pass by.

Think big, start small

Some tips on creating your scripts. I often use the saying, “When you have a question, probably you are not the first one to ask”. So quite often I start with a search for examples. In some cases it is a perfect match or it needs little modification, in other cases, creating your own script is a better option.

As an example, I want some information from an ESXi host regarding time configuration. The cmdlet Get-VMHost can retrieve a lot of information from a host.

20171224-04Figure 4

However, just executing the cmdlet, does not make it visible. Now we start by storing the output in a variable:

$hosts = GetVMHost

When we enter the name of the variable, we get the same output:

$hosts

However if we format the output we will see a lot more. Do not forget the pipe “|”:

$hosts | Format-List

Now we will see all output from all hosts. When we want to deep digger in the output, proceed as follows. Start by typing:

$hosts[0].

By adding “[0]”, the first host is selected. Now add the dot and see what happens.

20171224-05Figure 5

Now you can browse and select the information you need in your script. You repeat this process by selecting a category and adding another dot. Always end with “| fl”. “fl” is an alias for the Format-List cmdlet. Example:

$hosts[0].ExtensionData.Config.DateTimeInfo.NtpConfig | fl

If it is not what you wanted, arrow up to get the last line back, some Backspaces, add a dot, and so on.

This way, you get to know the Cmdlets and create the basis for your scripts.

Before we can use the cmdlet Get-VMHost, we must connect to an ESXi host or a vCenter Server. There are several ways. If you do not want to store credentials, use these lines:

$cred = Get-Credential
Connect-VIServer -Server <hostname or IP> -Credential $cred

Example

As my background is in system administration and not in development, I still have to learn a lot on this subject. However, I believe that following best practices will help you writing efficient code.

As promised Here is the link to my GitHub PowerShellExamples. The “ReadInputWriteOutput.ps1” script reads an input file, loops through the content and writes an output file. Furthermore the script contains functions and a multi-line block comment.

Some additional resources:

Official PowerShell documentation: https://docs.microsoft.com/en-us/powershell/

VMware PowerCLI documentation for all versions: https://code.vmware.com/web/dp/tool/vmware-powercli/6.5.4

Hopefully this was helpful. Enjoy coding!

Personal note: You may have noticed that that in the last few months no articles appeared on my blog. The cause was a stupid accident that prevented me from working for at least two months. As things are getting better now, my plan is to publish more in 2018. Also dear reader, I wish you all the best for 2018!


 

2 Responses to Writing effective scripts using VMware PowerCLI

  1. Thom says:

    Hi Paul,

    All the best wishes for 2018 to you, too. Very useful post, thanks.

    Hope you’ll fully recover from this stupid accident.

    Last but not least..
    Your background in DEV will be as developed as it’s already been for many years in your operational OPS life.

    Cheers,
    Thom

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: