At Your Fingertips

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.

$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

#
# 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.

[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.

.\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.

At Your Fingertips

Enjoy!

5 Comments

    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

Leave a Reply

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

*
*

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