Get-PSCallStack Demonstration Using Multiple Functions

I wrote a couple recent posts that used the Get-PSCallStack cmdlet. In my case, Get-PSCallStack helped one function determine what function invoked it, and used the information to further determine if that function was approved to invoke it, or not. In doing this, I wrote a series of small functions I’d like to share to help demonstrate this process.

In the below code example, I’ve defined a series of functions. The first function defined is called Get-Main, and it does a few things. It sets a variable $Index to zero, dumps the Get-PSCallStack commands into a global variable (that will exist after the function completes), and then loops through each of the command properties in the second variable, displaying its correctly assumed index, and the command name.

The following functions are in place to progressively call one another from the bottom up. Working downward, however, Get-OneUpOne invokes Get-Main (discussed above), Get-OneUpTwo invokes Get-OneUpOne, Get-OneUpThree invokes Get-OneUpTwo, etc. These multiple functions end up being listed in Get-PSCallStack, as we’ll soon see.

Function Get-Main {
    $Index = 0
    $Global:Var = (Get-PSCallStack).Command
    $Var | ForEach-Object {
        "Index $Index : $_"
        $Index ++
    }
}

Function Get-OneUpOne {
    Get-Main
}

Function Get-OneUpTwo {
    Get-OneUpOne
}

Function Get-OneUpThree {
    Get-OneUpTwo
}

Function Get-OneUpFour {
    Get-OneUpThree
}

Get-OneUpFour

The last line in the above example, starts the magic by invoking Get-OneUpFour, that invokes Get-OneUpThree, etc., moving upward. Here’s a gif that shows how these are invoked, working up from the bottom.

get-pscallstack-demonstration-using-multiple-functions01

When this chunk of code is dropped into the ISE and run, we get the results listed below. They indicate the index and value of Get-PSCallStack’s recorded commands, or steps, on the way to invoking the Get-Main function. Notice, that it’s backwards, and what I mean is this: The first index 0, is the last command recorded. The last index, 5, is the first command recorded.

Index 0 : Get-Main
Index 1 : Get-OneUpOne
Index 2 : Get-OneUpTwo
Index 3 : Get-OneUpThree
Index 4 : Get-OneUpFour
Index 5 : <ScriptBlock>

Since we created the global variable $Var, we can verify if the above results are correct — if our assumed index is accurate. We’ll check with a single index first, and then run $Var though a For loop to see each index.

$Var[2]

Get-OneUpTwo
For ($i = 0; $i -lt $Var.Length; $i++) {
    "Index $i -- $($Var[$i])"
}

Index 0 -- Get-Main
Index 1 -- Get-OneUpOne
Index 2 -- Get-OneUpTwo
Index 3 -- Get-OneUpThree
Index 4 -- Get-OneUpFour
Index 5 -- Get-PSCallStack_Example.ps1

Take a look at index 5 in the directly above example, and then the further above example. One says, “<ScriptBlock>,” and one says, “Get-PSCallStack_Example.ps1.” Any idea what changed between these two examples? If you guessed that I saved my code example, while writing this post, then you’d be correct.

The highest numbered index* — 5 in this example — is always going to be the starting point — whether it’s called from the prompt, or a saved file, and the lowest index — index 0 — will always be the currently invoked function. This means that index 1 will always be the calling function, of the last function that was invoked.

* The highest numbered index will also always be index -1, such as $Var[-1] would equal “<ScriptBlock>,” or “Get-PSCallStack_Example.ps1.” Knowing this, it’s possible to work backwards through these results, or any other array.

So it’s been said, the other time we see an array in reverse, is the $Error variable. $Error[0] is always the most recent error, not the first error. Thanks for reading, and we’ll do it again sometime soon.

Leave a Reply

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