Magic of $MyInvocation in PowerShell


Do you know that we can convert a predominant parameter value into a true command? Yes, to test the connectivity of a server we usually use Test-Connection -ComputerName ServerName, but how about using the ServerName -Ping? I know what you are thinking, what if there are hundreds or thousands of servers, do we need to create those many functions? No, just only one function with the help of $MyInvocation automatic variable.

What is $MyInvocation?

$MyInvocation is an automatic variable which contains information about the invocation details of the current execution, such as function or script name, parameters, parameter values, script root, script path, invocation name and etc., and $MyInvocation works with scripts, functions, and script blocks only.

In action

I will take the same ping example and show you that in action. I will write a small function to check the ping status with the name MyInvCmd and of course the name doesn’t matter and I will never use it or I can’t use it anywhere with that name, and here’s my function…

Function MyInvCmd
{
    $ServerName = $MyInvocation.InvocationName
    Test-Connection -TargetName $ServerName -IPv4    # In PowerShell 7
}

Now, if I load this function and call it in the console, then it will throw me an error like this…

MyInvCmd error

But, as I said earlier I will never use this function with this name, so I will create an alias for this function with my server name. First to test this out in my local machine I will create an alias with localhost name…

New-Alias -Name localhost -Value MyInvCmd

That’s all, now load the MyInvCmd function and call with the alias name localhost in the console, and see the magic. You will see the ping results of the localhost as below…

localhost example

So, if you take a look at the function, $MyInvocation returns the output as below and the value of the InvocationName will be localhost, the actual function name is MyInvCmd though since it is being invoked by the alias name localhost, the value of the InvocationName is localhost and assigned it to $ServerName, and the same is used with -TargetName parameter in the Test-Connection cmdlet.

MyCommand             : MyInvCmd
BoundParameters       : {}
UnboundArguments      : {}
ScriptLineNumber      : 1
OffsetInLine          : 1
HistoryId             : 4
ScriptName            :
Line                  : localhost
PositionMessage       : At line:1 char:1
                        + localhost
                        + ~~~~~~~~~
PSScriptRoot          :
PSCommandPath         :
InvocationName        : localhost
PipelineLength        : 1
PipelinePosition      : 1
ExpectingInput        : False
CommandOrigin         : Runspace
DisplayScriptPosition :

So, whatever the alias name that you create for MyInvCmd will be used in the function as a server name, so what I will do is I will get the list of servers and create aliases with all of them.

$FilePath = "C:\Inventory\Servers.txt" 
Get-Content -Path $FilePath | Foreach-Object { New-Alias -Name $_ -Value MyInvCmd }

Now, all the server names are aliases to MyInvCmd but every alias invocation is unique and the function will work using the aliased server name.

This time I will add more functionality and parameters as well, so that we can change the behavior on every execution. Besides ping functionality, I will add the functionality to get the server info, enter into PSSession, RDP session, and still you can continue to add…

function MyInvCmd 
{
    <#
    .SYNOPSIS
    Replace MyInvCmd with the aliased server name
    #>

    [CmdLetBinding(DefaultParameterSetName = 'Help')]
    param 
    (
        [Parameter(Mandatory = $false)]
        [switch] $Ping,

        [Parameter(Mandatory = $false)]
        [switch] $ServerInfo,

        [Parameter(Mandatory = $false, ParameterSetName = 'PSSession')]
        [switch] $PSSession,

        [Parameter(Mandatory = $false, ParameterSetName = 'RDP')]
        [switch] $RDP
    )

    $ServerName = $MyInvocation.InvocationName
    
    if ($Ping) { Test-Connection -TargetName $ServerName -IPv4 } # In PS7

    if ($ServerInfo)
    {
        $OSInfo = Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ServerName
        $CSInfo = Get-CimInstance -ClassName CIM_ComputerSystem -ComputerName $ServerName

        [PSCustomObject]@{
            OperatingSystem = $OSInfo.Caption
            TotalMemory     = $CSInfo.TotalPhysicalMemory
            FreeMemory      = $OSInfo.FreePhysicalMemory
            NoOfCPUs        = $CSInfo.NumberOfProcessors
            NoOfLogicalCPUs = $CSInfo.NumberOfLogicalProcessors
            LastRebootTime  = $OSInfo.LastBootUpTime
        }
    }
    
    if ($PSSession) { Enter-PSSession -ComputerName $ServerName }

    if ($RDP) { Start-Process -FilePath mstsc.exe -ArgumentList /v:$ServerName }    

    if ($MyInvocation.BoundParameters.Count -eq 0) { Get-Help -Name $ServerName }
}

And now, I will get my list of servers and add them as aliases to this function. Since I am running this demo in my lab which is running in Hyper-V, I will get all the VM Names and add them as aliases…

Get-VM | ForEach-Object { New-Alias -Name $_.Name -Value MyInvCmd }

And here’s the list of aliases that I have created for MyInvCmd, and if you notice all the server names are pointing to the MyInvCmd itself.

aliases list

Now, let’s see how to use these aliases, you can use the server name as command and pass the parameters to it.

aliases in action

What I like the most in this concept is I can use this pretty much for anything, not only the servers but also I can convert my request numbers, incidents and etc., and you can add lot more functionality at your convince.


Share it on     |   |   |   | 
  Prev:  

Publish a static website on Azure Static WebApps (Preview)

  :Next  

Self Aliased Functions in PowerShell

comments powered by Disqus