Specify Which Functions Can Invoke Another Function Part II

It wasn’t long after I wrote and posted Part I of this topic — which I didn’t know was a Part I at the time — that I had another idea.

For those that didn’t read the post linked above, I have a function called Send-ErrorMessage. Its purpose is to send an email if the catch block of a try-catch, that doesn’t include an error exception, executes. It’s a safety net, just in case I haven’t properly coded for an unknown error condition. Because I had to export the function, so that it’s available to other functions in other modules, I needed a way to ensure that only specific functions were able to invoke Send-ErrorMessage. I didn’t want a user using the function; I only wanted the functions I wrote, that were approved, to be able to use the Send-ErrorMessage function.

Well today, I took the conditional logic that was taking up space in my Begin block and moved it to the [ValidateScript()] attribute. This also helped clean up some of the Process block, too. The only thing that I’m giving up, is that now the message to someone trying to invoke this function with an unapproved function, or at the prompt, is that it’ll show an error using red text, instead of a warning using orange text (in the ISE).

I may have mentioned this before, but I believe it’s okay to display standard, red text error messages in your tools, depending on the audience. If I’m writing tools for my team, they should be comfortable enough to decipher PowerShell errors. If the tool is being written for end-users, or lower tier employees, then it may be wise to better control the errors and warnings.

The complete, modified code is below. It begins by defining the Send-ErrorMessage function. Then it creates two additional functions — one that is approved to invoke Send-ErrorMessage, and one that’s not. Then, it invokes those two functions — Test-Allowed and Test-NotAllowed, respectively — and displays the results one right after the other. The first function will be permitted and will write the verbose statements in the Process block, but the second function will fail inside the [ValidateScript()] attribute, as it’s not approved to invoke Send-ErrorMessage.

Copy, paste, and take it for a run. After you’ve run the code, enter Send-ErrorMessage manually. It still won’t let you run it from the command line, even if you provide an approved function name, as the value of the -FunctionName parameter. That’s right, you can’t fake it out, as it’s not taking your word, it’s using the value returned by Get-PSCallStack. Thanks for reading!

Function Send-ErrorMessage {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [ValidateScript({
            $AcceptableFunctions = 'Get-AJob','Test-Allowed','Get-Real'
            $CallingFunction = (Get-PSCallStack)[1].Command
            If (-Not ($CallingFunction -in $AcceptableFunctions)) {
                Throw "The `"$_`" function or command, does not have approval to invoke this function. Send-ErrorMessage will not work if you use the function manually and enter an approved function name."
            } Else {
                $true
            }
        })]
        [string]$FunctionName
    )

    Begin {
    } # End Begin.

    Process {
        Write-Verbose -Message "The calling function ($($PSBoundParameters.FunctionName)) is approved." -Verbose
        Write-Verbose -Message 'Beginning to send error message ...' -Verbose
    } # End Process.
    
    End {
    } # End End.
} # End Function: Send-ErrorMessage.


Function Test-Allowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage -FunctionName $MyInvocation.MyCommand.Name
    }
} # End Function: Test-Allowed.


Function Test-NotAllowed {
    # Divide by 0 to force error/catch block.
    try {
        10 / 0
    } catch {
        Send-ErrorMessage -FunctionName $MyInvocation.MyCommand.Name
    }
} # End Function: Test-NotAllowed.


# Invoke Function.
Test-Allowed

# Invoke Function.
Test-NotAllowed

Leave a Reply

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