Question
Is there a simple way to add exception handling to a proxy cmd's process functionality?
As you can see below, I did find a way to do this, but I suspect there's a far cleaner solution.
Full Story
I want to create a proxy function for Get-ADUser which resolves the current issue with exceptions being thrown where an account cannot by found by identity. With the standard function the -ErrorAction
parameter has no effect; my plan was simply to prevent such exceptions from being thrown.
I created a Proxy for Get-ADUser
via the following: [System.Management.Automation.ProxyCommand]::Create((New-Object System.Management.Automation.CommandMetaData (Get-Command Get-ADUser)))
.
I then pasted the result into a function Get-AdUserNullIfNotExist { <# output of the above pasted here #> }
.
If I alter the $ScriptCmd
as below I get Exception calling "GetSteppablePipeliine
... Only a script block that contains exactly one pipeline or command can be converted
. The same is try if I attempt to use trap
instead of try-catch
.
#$scriptCmd = {& $wrappedCmd @PSBoundParameters}
$scriptCmd = {
try {
& $wrappedCmd @PSBoundParameters
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
write-output $null
} catch {
throw
}
}
If instead I try to add this logic in the process block (as below) I get: Object reference not set to an instance of an object
from $steppablePipeline.End()
; presumably because the output from the proxy's pipeline isn't available to the $steppablePipeline
's stream.
process
{
try {
$steppablePipeline.Process($_)
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
$null #I've tried both with and without this line
} catch {
throw
}
}
In the end I got this to work by copying the param block into a second $wrappedCmd
variable and calling one from the other... but this feels horredously hacky.
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-ADUser', [System.Management.Automation.CommandTypes]::Cmdlet)
$wrappedCmd2 = {
[CmdletBinding(DefaultParameterSetName='Filter')]
param(
[Parameter(ParameterSetName='Filter', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
${Filter}
,
[Parameter(ParameterSetName='LdapFilter', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
${LDAPFilter}
,
[Alias('Property')]
[ValidateNotNullOrEmpty()]
[string[]]
${Properties}
,
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateRange(0, 2147483647)]
[ValidateNotNullOrEmpty()]
[int]
${ResultPageSize}
,
[Parameter(ParameterSetName='LdapFilter')]
[Parameter(ParameterSetName='Filter')]
[System.Nullable[int]]
${ResultSetSize}
,
[Parameter(ParameterSetName='LdapFilter')]
[Parameter(ParameterSetName='Filter')]
[ValidateNotNull()]
[string]
${SearchBase}
,
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateNotNullOrEmpty()]
[Microsoft.ActiveDirectory.Management.ADSearchScope]
${SearchScope}
,
[Parameter(ParameterSetName='Identity', Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[ValidateNotNull()]
[Microsoft.ActiveDirectory.Management.ADUser]
${Identity}
,
[Parameter(ParameterSetName='Identity')]
[ValidateNotNullOrEmpty()]
[string]
${Partition}
,
[ValidateNotNullOrEmpty()]
[string]
${Server}
,
[ValidateNotNullOrEmpty()]
[pscredential]
[System.Management.Automation.CredentialAttribute()]
${Credential}
,
[Microsoft.ActiveDirectory.Management.ADAuthType]
${AuthType}
)
try {
& $wrappedCmd @PSBoundParameters
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
$null
}
}
$scriptCmd = {& $wrappedCmd2 @PSBoundParameters}
Full Code for Proxy Function
function Get-AdUserNullIfNotExist {
[CmdletBinding(DefaultParameterSetName='Filter')]
param(
[Parameter(ParameterSetName='Filter', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
${Filter}
,
[Parameter(ParameterSetName='LdapFilter', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
${LDAPFilter}
,
[Alias('Property')]
[ValidateNotNullOrEmpty()]
[string[]]
${Properties}
,
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateRange(0, 2147483647)]
[ValidateNotNullOrEmpty()]
[int]
${ResultPageSize}
,
[Parameter(ParameterSetName='LdapFilter')]
[Parameter(ParameterSetName='Filter')]
[System.Nullable[int]]
${ResultSetSize}
,
[Parameter(ParameterSetName='LdapFilter')]
[Parameter(ParameterSetName='Filter')]
[ValidateNotNull()]
[string]
${SearchBase}
,
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateNotNullOrEmpty()]
[Microsoft.ActiveDirectory.Management.ADSearchScope]
${SearchScope}
,
[Parameter(ParameterSetName='Identity', Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[ValidateNotNull()]
[Microsoft.ActiveDirectory.Management.ADUser]
${Identity}
,
[Parameter(ParameterSetName='Identity')]
[ValidateNotNullOrEmpty()]
[string]
${Partition}
,
[ValidateNotNullOrEmpty()]
[string]
${Server}
,
[ValidateNotNullOrEmpty()]
[pscredential]
[System.Management.Automation.CredentialAttribute()]
${Credential}
,
[Microsoft.ActiveDirectory.Management.ADAuthType]
${AuthType}
)
begin
{
try
{
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-ADUser', [System.Management.Automation.CommandTypes]::Cmdlet)
$wrappedCmd2 = {
[CmdletBinding(DefaultParameterSetName='Filter')]
param(
[Parameter(ParameterSetName='Filter', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
${Filter}
,
[Parameter(ParameterSetName='LdapFilter', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
${LDAPFilter}
,
[Alias('Property')]
[ValidateNotNullOrEmpty()]
[string[]]
${Properties}
,
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateRange(0, 2147483647)]
[ValidateNotNullOrEmpty()]
[int]
${ResultPageSize}
,
[Parameter(ParameterSetName='LdapFilter')]
[Parameter(ParameterSetName='Filter')]
[System.Nullable[int]]
${ResultSetSize}
,
[Parameter(ParameterSetName='LdapFilter')]
[Parameter(ParameterSetName='Filter')]
[ValidateNotNull()]
[string]
${SearchBase}
,
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateNotNullOrEmpty()]
[Microsoft.ActiveDirectory.Management.ADSearchScope]
${SearchScope}
,
[Parameter(ParameterSetName='Identity', Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[ValidateNotNull()]
[Microsoft.ActiveDirectory.Management.ADUser]
${Identity}
,
[Parameter(ParameterSetName='Identity')]
[ValidateNotNullOrEmpty()]
[string]
${Partition}
,
[ValidateNotNullOrEmpty()]
[string]
${Server}
,
[ValidateNotNullOrEmpty()]
[pscredential]
[System.Management.Automation.CredentialAttribute()]
${Credential}
,
[Microsoft.ActiveDirectory.Management.ADAuthType]
${AuthType}
)
try {
& $wrappedCmd @PSBoundParameters
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
$null
}
}
$scriptCmd = {& $wrappedCmd2 @PSBoundParameters}
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
$CopyOfSP = $steppablePipeline
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
<#
.ForwardHelpTargetName Get-ADUser
.ForwardHelpCategory Cmdlet
#>
}
Wow, this was a 'fun' one. Thanks OP for your post and base code.
I did some work from your original process, and after generating the code from the [System.Management.Automation.ProxyCommand]::Create((New-Object System.Management.Automation.CommandMetaData (Get-Command Get-ADUser)))
command, I had the same problem as you when trying to change the $ScriptCmd - so I moved on to changing the Catch
in the Process {...}
block. It took a while, but I pulled on a few threads, and got it working.
Originally it was successfully processing whatever code I put in the catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
- but still throwing the ADIdentityNotFoundException
exception, until I added a second catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
in the end {...}
block.
It now successfully returns $null if the user doesn't exist, without throwing an exception, and so far has worked perfectly in an IF statement.
My full code is
# Proxy Function for custom ADIdentityNotFound exception handling
# Based on https://stackoverflow.com/questions/37205808/powershell-proxy-function-adding-exception-handling
function Get-ADUserWrapped {
[CmdletBinding(DefaultParameterSetName='Filter', HelpUri='http://go.microsoft.com/fwlink/?LinkId=301397')]
param()
dynamicparam
{
try {
$targetCmd = $ExecutionContext.InvokeCommand.GetCommand('ActiveDirectory\Get-ADUser', [System.Management.Automation.CommandTypes]::Cmdlet, $PSBoundParameters)
$dynamicParams = @($targetCmd.Parameters.GetEnumerator() | Microsoft.PowerShell.Core\Where-Object { $_.Value.IsDynamic })
if ($dynamicParams.Length -gt 0)
{
$paramDictionary = [Management.Automation.RuntimeDefinedParameterDictionary]::new()
foreach ($param in $dynamicParams)
{
$param = $param.Value
if(-not $MyInvocation.MyCommand.Parameters.ContainsKey($param.Name))
{
$dynParam = [Management.Automation.RuntimeDefinedParameter]::new($param.Name, $param.ParameterType, $param.Attributes)
$paramDictionary.Add($param.Name, $dynParam)
}
}
return $paramDictionary
}
} catch {
throw
}
}
begin
{
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('ActiveDirectory\Get-ADUser', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = { & $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
} catch {
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
} catch { throw }
}
end
{
try {
$steppablePipeline.End()
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
} catch { throw }
}
<#
.ForwardHelpTargetName ActiveDirectory\Get-ADUser
.ForwardHelpCategory Cmdlet
#>
}
Since it was successful, thought I should make my first StackOverflow post to help others. It's definitely been added to my personal collection of snippets.
And since it's my first post, please forgive any breaches of ettiquette.