So I've been watching the PowerShell Toolmaking Fundamentals course on Pluralsight authored by Adam Bertram. You may be surprised that I watch other PowerShell related courses, but I always pick up something I didn't know about, find a new teaching technique or something else that makes me say, "that was cool." I have found a few of these in Adam's course so far.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
One of the tricks he demonstrated was using a hashtable as a parameter value for an advanced function that could then be splatted to Set-ADuser. For the sake of what I want to demonstrate here's my simplified version of such a function.
Function Update-MyUser { [cmdletbinding(SupportsShouldProcess)] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "Enter a user name")] [ValidateNotNullorEmpty()] [Microsoft.ActiveDirectory.Management.ADUser]$Identity, [Parameter(Position = 1, Mandatory, HelpMessage = "Enter a hashtable of parameter values for Set-ADUser")] [ValidateNotNullorEmpty()] [hashtable]$Settings ) Write-Verbose "Updating $Identity" Write-Verbose ($Settings | Out-String) Try { $user = Get-ADuser $Identity -ErrorAction stop Write-Verbose $user.distinguishedname } Catch { Throw $_ } #splat the settings hashtable to Set-ADuUser $user| Set-ADUser @settings }
The function gets the specified user and then updates the user with hashtable of parameters from Set-ADUser. If you know all the parameter names this works just fine.
I've easily updated the user account.
But what if I make a mistake with the hashtable of settings?
There is no parameter called FirstName for Set-ADUser. It should be GivenName. One thing you could do, and this is the point of this article, is to validate the hashtable keys against parameter names from Set-ADuser.
You can use Get-Command to list all of the parameter names.
get-command Set-ADuser | Select -expand Parameters
What we need to do is make sure that all of the keys in the settings hashtable are in this list. Here's a quick test.
$s = @{Title="T";Description="d"} $p = get-command set-aduser | select -expand parameters
I can use code like this to test if the keys from $s are also in $p:
$s.keys | where {$p.keys -contains $_}
Although I may be more interested in the cases where they don't match.
$s.keys | where {$p.keys -notcontains $_}
This won't give any results because nothing matches the filter. But if I modify the hashtable with a bogus entry it will.
With this concept in mind I can revise the function.
Function Update-MyUser { [cmdletbinding(SupportsShouldProcess)] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "Enter a user name")] [ValidateNotNullorEmpty()] [Microsoft.ActiveDirectory.Management.ADUser]$Identity, [Parameter(Position = 1, Mandatory, HelpMessage = "Enter a hashtable of parameter values for Set-ADUser")] [ValidateNotNullorEmpty()] [hashtable]$Settings ) #get parameters for Set-ADUser $setparams = Get-Command -Name Set-ADuser | Select -ExpandProperty Parameters #get keys from Settings that aren't in $SetParams. $bad = $settings.Keys | where {$setparams.keys -notcontains $_ } if (-Not $Bad) { Write-Verbose "Updating $Identity" Write-Verbose ($Settings | Out-String) Try { $user = Get-ADuser $Identity -ErrorAction stop Write-Verbose $user.distinguishedname } Catch { Throw $_ } $user| Set-ADUser @settings } else { Write-Warning "The Settings hashtable contains these bad keys: $($bad -join ",")" } }
Certainly you could add other code to list the available parameters, suggest corrections or whatever. But now the function won't attempt to run and gracefully handles bad keys.
Once corrected, the function works as expected.
And please don't take any of this as an indication that Adam missed something in his course. Far from it. No course can teach you absolutely everything you need to know to build effective PowerShell tools. You need to build what works for you and add error handling that you feel is appropriate. In this case I thought this would offer a nice learning opportunity for you to learn about hashtable keys and a few operators.
Sounds like a great test case for Pester!
On one hand yes, but in my scenario it is probably more about parameter validation. In fact, now that I write that I have another thought on the subject.
Another option, since the whole point here is parameter validation is to do just that. I moved the test code into a [ValidateScript()] test. If there is a failure then I throw a custom error message.
Function Update-MyUser {
[cmdletbinding(SupportsShouldProcess)]
Param(
[Parameter(Position = 0, Mandatory, HelpMessage = "Enter a user name")]
[ValidateNotNullorEmpty()]
[Microsoft.ActiveDirectory.Management.ADUser]$Identity,
[Parameter(Position = 1, Mandatory, HelpMessage = "Enter a hashtable of parameter values for Set-ADUser")]
[ValidateScript({
$setparams = Get-Command -Name Set-ADuser | Select -ExpandProperty Parameters
$bad = $_.Keys | where {$setparams.keys -notcontains $_ }
if ($Bad) {
Throw "The Settings hashtable contains these bad keys: $($bad -join ",")"
#$False
}
else {
$True
}
})]
[hashtable]$Settings
)
Write-Verbose "Updating $Identity"
Write-Verbose ($Settings | Out-String)
Try {
$user = Get-ADuser $Identity -ErrorAction stop
Write-Verbose $user.distinguishedname
}
Catch {
Throw $_
}
$user| Set-ADUser @settings
}
Awesome! Thanks for sharing Jeff!