How often have you been finding out the PowerShell version you were using, or to which vSphere Server you were connected, or in which git repo/branch your code was being stored, or… Despair no more, it can now be available at your fingertips.
The following is a write up of a part of session HBI1729BU ,that was presented at VMworld US 2019.
The code shown in this post is also available in the PowerCLI Community Repository.
Concept
The idea behind this snippet came when I had checked what was in $global:DefaultVIServers for the n-th time in a short PowerCLI session.
And it’s probably not only that specific variable that must be all too familiar to most of us. Any entry in the following list of variables and cmdlets must have been executed by most of us multiple times.
1 2 3 4 5 |
$Global:DefaultVIServer $Global:DefaultVIServers $PSVersionTable Get-Location Get-Module -Name VMware.PowerCLI |
It would be an important timesaver, when the information returned by those instructions, would be permanently in view. At your fingertips, so to say.
The use of the PowerShell profile(s) and the Prompt function, immediately came to mind.
Not to overload your PS prompt, I decided to show part of the information in the console’s Window Title bar. That can be done by assigning a value to $host.ui.RawUI.WindowTitle.
Implementation
The Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# # Provide environment information in the PS Console # # History: # 1.0 - August 4th 2019 - LucD # Initial version (for session HBI1729BU VMworld US 2019) # # 1) PS prompt # - current (local) time # - execution time of the previous command # - shortened PWD # 2) Window title # - User/Admin # - PS-32/54-Edition-Version # - PCLI version # - git repo/branch # - VC/ESXi:defaultServer-User [# connections] function prompt { # Current time $date = (Get-Date).ToString('HH:mm:ss') Write-Host -Object '[' -NoNewline Write-Host -Object $date -ForegroundColor Cyan -BackgroundColor DarkBlue -NoNewline Write-Host -Object ']' -NoNewline # Execution time previous command $history = Get-History -ErrorAction Ignore -Count 1 if ($history) { $time = ([DateTime](New-TimeSpan -Start $history.StartExecutionTime -End $history.EndExecutionTime).Ticks).ToString('HH:mm:ss.ffff') Write-host -Object '[' -NoNewLine Write-Host -Object "$time" -ForegroundColor Yellow -BackgroundColor DarkBlue -NoNewline Write-host -Object '] ' -NoNewLine } # Shorted PWD $path = $pwd.Path.Split('\') if ($path.Count -gt 3) { $path = $path[0], '..', $path[-2], $path[-1] } Write-Host -Object "$($path -join '\')" -NoNewline # Prompt function needs to return something, # otherwise the default 'PS>' will be added "> " # Refresh the window's title Set-Title } function Set-Title { # Running as Administrator or a regular user $userInfo = [Security.Principal.WindowsIdentity]::GetCurrent() if ((New-Object Security.Principal.WindowsPrincipal $userInfo).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) { $role = 'Admin' } else { $role = 'User' } # Usertype user@hostname $user = "$role $($userInfo.Name)@$($env:computername)" # PowerShell environment/PS version $bits = 32 if ([Environment]::Is64BitProcess) { $bits = 64 } $ps = " - PS-$($bits): $PSEdition/$($PSVersionTable.PSVersion.ToString())" # PowerCLI version (derived from module VMware.PowerCLI) $pcliModule = Get-Module -Name VMware.PowerCLI -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1 $pcli = " - PCLI: $(if($pcliModule){$pcliModule.Version.ToString()}else{'na'})" # If git is present and if in a git controlled folder, display repositoryname/current_branch $gitStr = '' if ((Get-Command -Name 'git' -CommandType Application -ErrorAction SilentlyContinue).Count -gt 0) { $gitTopLevel = git rev-parse --show-toplevel 2> $null if ($gitTopLevel.Length -ne 0) { $gitRepo = Split-Path -Path $gitTopLevel -Leaf $gitBranch = (git branch | Where-Object { $_ -match "\*" }).Trimstart('* ') $gitStr = " - git: $gitRepo/$gitBranch" } } # If there is an open vSphere Server connection # display [VC|ESXi] last_connected_server-connected_user [number of open server connections] if ($global:defaultviserver) { $vcObj = (Get-Variable -Scope global -Name 'DefaultVIServer').Value if ($vcObj.ProductLine -eq 'vpx') { $vcSrv = 'VC' } else { $vcSrv = 'ESXi' } $vc = " - $($vcSrv): $($vcObj.Name)-$($vcObj.User) [$($global:DefaultVIServers.Count)]" } # Update the Window's title $host.ui.RawUI.WindowTitle = "$user$ps$pcli$vc$gitStr" } # Set title after starting session Set-Title |
Annotations
Line 19-51: Replacement function for the PS prompt.
Line 22-25: Current time in the format ‘HH:mm:ss’. Note that ‘HH’ asks for a 24-hour value.
Line 28-35: The execution time of the previous command, in the format ‘HH:mm:ss:ffff’.
Line 28: The execution time is based on the content of PS History. With Get-History we obtain the start time of the command, and with New-TimeSpan we obtain the duration for running that command.
Line 38-43: These lines take the path to the current folder, and shorten that path to only show the start of the path and the last two directories. When the path is shortened, it is indicated by two dots.
Line 53-114: Function to populate the Windows Title bar
Line 56-64: These lines determine if the PS session is started “As Administrator” or as a regular user.
Line 67: The user and station information.
Line 70-75: The PS edition (Desktop or Core) and the PS version.
Line 78-81: If installed, the version of VMware PowerCLI. Note that this looks at the VMware.PowerCLI module to obtain that version number.
Line 84-94: If the git command is present on the station, these lines will check if the current directory is inside a git repository. It will then display the name of the git repository and the current branch.
Line 98-110: These lines check if there is an open connection to a vSphere Server. It will show if the connected vSphere Server is a vCenter or an ESXi node.
Line 109: If multiple connections to a vSphere Servers are open, this will display the number of these connections. The vSphere Server that is shown is the last connected one. This corresponds with what is kept in the $global:DefaultVIServer variable.
Line 117: Initial call to the Set-Title function. This will populate the Windows Title bar when the PS console is opened.
Installation
You can just copy and rename the code to one of the profile files. To make that process easier, I also provide the following installation script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
[cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( # [Parameter(Mandatory = $true)] [ValidateSet('CurrentUserCurrentHost', 'CurrentUserAllHosts', 'AllUsersCurrentHost', 'AllUsersAllHosts')] [string]$Scope, [switch]$NoClobber, [switch]$Backup, [string]$NewProfile = '.\NewProfile.ps1' ) if ($PSCmdlet.ShouldProcess("$($Profile.$Scope)", "Create $Scope profile")) { $profilePath = $Profile."$Scope" Write-Verbose -Message "Target is $profilePath" $createProfile = $true if (Test-Path -Path $profilePath) { Write-Verbose -Message "Target exists" if ($NoClobber) { Write-Verbose -Message "Cannot overwrite target due to NoClobber" $createProfile = $false } elseif ($Backup) { Write-Verbose -Message "Create a backup as $profilePath.bak" Copy-Item -Path $profilePath -Destination "$profilePath.bak" -Confirm:$false -Force } elseif (-not $NoClobber) { Write-Verbose -Message "Target will be overwritten" } else { Write-Verbose -Message "Use -NoClobber:$false or -Backup" } } if ($createProfile) { if (-not $NewProfile) { $script:MyInvocation.MyCommand | select * $folder = Split-Path -Parent -Path $script:MyInvocation.MyCommand.Path $folder = Get-Location $NewProfile = "$folder\NewProfile.ps1" } Write-Verbose -Message "New profile expected at $NewProfile" if (Test-Path -Path $NewProfile) { Write-Verbose -Message "Copy $NewProfile to $profilePath" Copy-Item -Path $NewProfile -Destination $profilePath -Confirm:$false } else { Write-Warning -Message "Could not find the new profile file!" Write-Warning -Message "Use the NewProfile parameter or store a NewProfile.ps1 file in folder $folder." } } } |
Save the above code to a .ps1 file, let’s say install-profile.ps1. Also copy the profile code to a .ps1 file, let’s say newprofile.ps1.
You can now install this new profile with a simple call.
1 |
.\install-profile.ps1 -NewProfile .\NewProfile.ps1 -Scope CurrentUserCurrentHost -Backup |
The installation script has a number of parameters:
- NewProfile : the file that contains the profile you want to install
- Scope : for which Scope you want to install the new profile.
- Backup : backup the current profile before overwriting it.
- NoClobber : avoids overwriting an existing profile file.
Usage
When the new profile is installed, and when you restart your console session, you will see the new profile in action.
The following screenshot shows the features of the new profile.

And the following short video shows the installation process and the features the profile offers.
Enjoy!
tom
It would be good to have “$pcliModule = Get-Module -Name VMware.PowerCLI…” embraced in “try” – “catch” , because your module is useful even without PCLI installed.
LucD
That is an excellent suggestion.
Try replacing the PCLI code with
# PowerCLI version (derived from module VMware.PowerCLI)
$pcliModule = Get-Module -Name VMware.PowerCLI -ListAvailable |
Sort-Object -Property Version -Descending |
Select-Object -First 1
$pcli = " - PCLI: $(if($pcliModule){$pcliModule.Version.ToString()}else{'na'})"
I’ll change the code on the repository as well.
MartinM
Thanks! It was a great session at VMworld.
LucD
Thanks.
We enjoyed doing the session.
tmack
Very cool! Thanks