This commit is contained in:
OCram85 2022-03-16 16:14:45 +01:00
parent 380fe07b8c
commit 26c348c41a
2 changed files with 62 additions and 52 deletions

View File

@ -1,10 +1,10 @@
--- ---
title: 'Parameter validation with PSTypeName' title: 'Parameter Validation with PSTypeName'
date: 2022-03-16T09:24:56+01:00 date: 2022-03-16T09:24:56+01:00
draft: true #draft: true
categories: ['PowerShell'] categories: ['PowerShell']
tags: ['parameter validation'] tags: ['parameter', 'validation']
# lastmod: 2022-03-16T09:24:56+01:00 # lastmod: 2022-03-16T09:24:56+01:00
# showDateUpdated: true # showDateUpdated: true
@ -19,10 +19,14 @@ tags: ['parameter validation']
# sharingLinks: [null] # sharingLinks: [null]
--- ---
If you're using `PSCustomObject`s with customized _TypeNames_ you could validate them with additional parameter ![ship](ship.jpg 'Photo by [Rod Long](https://unsplash.com/@rodlong) on [Unsplash](https://unsplash.com)')
attributes.
## Well-Known Workflow ## 🖼️ Intro
`PSCustomObject`s with customized _TypeNames_ can be validated with the parameter
attribute `[PSTypeName()]`.
## 🗑️ Well-Known Workflow
So let's start with a common object definition how it is used with a function: So let's start with a common object definition how it is used with a function:
@ -33,7 +37,8 @@ $Rocinante = [PSCustomObject]@{
Class = 'Corvette' Class = 'Corvette'
Registry = 'ECF-270' Registry = 'ECF-270'
HullNumber = '158' HullNumber = '158'
Length = '46' LengthInMeter = 46
Name = 'Rocinante'
} }
``` ```
@ -44,18 +49,19 @@ As you can see, a `PSCustomObject` has still the the same class type and just di
TypeName: System.Management.Automation.PSCustomObject TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition Name MemberType Definition
---- ---------- ---------- ---- ---------- ----------
Equals Method bool Equals(System.Object obj) Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode() GetHashCode Method int GetHashCode()
GetType Method type GetType() GetType Method type GetType()
ToString Method string ToString() ToString Method string ToString()
Class NoteProperty string Class=Corvette Class NoteProperty string Class=Corvette
HullNumber NoteProperty string HullNumber=158 HullNumber NoteProperty string HullNumber=158
Length NoteProperty string Length=46 LengthInMeter NoteProperty int LengthInMeter=46
Owner NoteProperty string Owner=Martian Congressional Republic Navy Name NoteProperty string Name=Rocinante
Registry NoteProperty string Registry=ECF-270 Owner NoteProperty string Owner=Martian Congressional Republic Navy
Type NoteProperty string Type=Light Frigate Registry NoteProperty string Registry=ECF-270
Type NoteProperty string Type=Light Frigate
> $Rocinante.PSObject.TypeNames > $Rocinante.PSObject.TypeNames
System.Management.Automation.PSCustomObject System.Management.Automation.PSCustomObject
@ -79,8 +85,8 @@ function Invoke-Launch {
# Manual input validation for $Ship # Manual input validation for $Ship
# test if all needed properties are present. # test if all needed properties are present.
$DockLength = '50' $DockLength = 50
if ($Ship.Length > $DockLength) { if ($Ship.LengthInMeter -gt $DockLength) {
Write-Error -Message "Ship doesn't fit in the docking station." -ErrorAction 'Stop' Write-Error -Message "Ship doesn't fit in the docking station." -ErrorAction 'Stop'
} }
# ... # ...
@ -91,7 +97,7 @@ function Invoke-Launch {
} }
``` ```
This common pattern could fail whenever someone changes your object properties. If the _Length_ property is missing you ran into an error. E.g.: This common pattern could fail whenever someone changes your object properties. If the _LengthInMeter_ property is missing you ran into an error. E.g.:
```console ```console
> $Rocinante = [PSCustomObject]@{ foo = 'bar' } > $Rocinante = [PSCustomObject]@{ foo = 'bar' }
@ -104,7 +110,7 @@ Keep in mind - Because we are using here custom objects and not class instances,
To fix this we can use the `[PSTypeName()]` parameter attribute, to ensure an object with the correct type name is used. This doesn't verify your parameters but minimize the risk for using invalid parameter objects. To fix this we can use the `[PSTypeName()]` parameter attribute, to ensure an object with the correct type name is used. This doesn't verify your parameters but minimize the risk for using invalid parameter objects.
## PSTypeName Usage ## 🛡️ PSTypeName Usage
Let's first modify the object creation and use a custom type name. Let's first modify the object creation and use a custom type name.
@ -112,38 +118,40 @@ Let's first modify the object creation and use a custom type name.
$Rocinante = [PSCustomObject]@{ $Rocinante = [PSCustomObject]@{
# You can use special property 'PSTypeName' # You can use special property 'PSTypeName'
# to set it implicit within the creation. # to set it implicit within the creation.
PSTypeName = 'Rocinante' PSTypeName = 'Ship.Corvette.LightFrigate'
Owner = 'Martian Congressional Republic Navy' Owner = 'Martian Congressional Republic Navy'
Type = 'Light Frigate' Type = 'Light Frigate'
Class = 'Corvette' Class = 'Corvette'
Registry = 'ECF-270' Registry = 'ECF-270'
HullNumber = '158' HullNumber = '158'
Length = '46' LengthInMeter = 46
Name = 'Rocinante'
} }
# Legacy syntax for injection a custom type name # Legacy syntax for injection a custom type name
# $Rocinante.PSObject.TypeNames.insert(0,'Rocinante') # $Rocinante.PSObject.TypeNames.insert(0,'Ship.Corvette.LightFrigate')
``` ```
```bash ```bash
> $Rocinante | Get-Member > $Rocinante | Get-Member
TypeName: Rocinante TypeName: Ship.Corvette.LightFrigate
Name MemberType Definition Name MemberType Definition
---- ---------- ---------- ---- ---------- ----------
Equals Method bool Equals(System.Object obj) Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode() GetHashCode Method int GetHashCode()
GetType Method type GetType() GetType Method type GetType()
ToString Method string ToString() ToString Method string ToString()
Class NoteProperty string Class=Corvette Class NoteProperty string Class=Corvette
HullNumber NoteProperty string HullNumber=158 HullNumber NoteProperty string HullNumber=158
Length NoteProperty string Length=46 LengthInMeter NoteProperty int LengthInMeter=46
Owner NoteProperty string Owner=Martian Congressional Republic Navy Name NoteProperty string Name=Rocinante
Registry NoteProperty string Registry=ECF-270 Owner NoteProperty string Owner=Martian Congressional Republic Navy
Type NoteProperty string Type=Light Frigate Registry NoteProperty string Registry=ECF-270
Type NoteProperty string Type=Light Frigate
> $Rocinante.PSObject.TypeNames > $Rocinante.PSObject.TypeNames
Rocinante Ship.Corvette.LightFrigate
System.Management.Automation.PSCustomObject System.Management.Automation.PSCustomObject
System.Object System.Object
``` ```
@ -156,14 +164,14 @@ function Invoke-Launch {
param ( param (
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[PSTypeName('Rocinante')]$Ship [PSTypeName('Ship.Corvette.LightFrigate')]$Ship
) )
begin {} begin {}
process { process {
$DockLength = '50' $DockLength = 50
if ($Ship.Length > $DockLength) { if ($Ship.LengthInMeter -gt $DockLength) {
Write-Error -Message "Ship doesn't fit in the docking station." -ErrorAction 'Stop' Write-Error -Message "Ship doesn't fit in the docking station." -ErrorAction 'Stop'
} }
# ... # ...
@ -174,7 +182,7 @@ function Invoke-Launch {
} }
``` ```
## Final Thoughts ## 💭 Final Thoughts
Over time, your PowerShell functions become more and more complex. You will reach a point where you start using Over time, your PowerShell functions become more and more complex. You will reach a point where you start using
objects as parameters. This is where the PSTypeName parameter attribute shown can help you. objects as parameters. This is where the PSTypeName parameter attribute shown can help you.
@ -192,31 +200,33 @@ You should consider creating a your objects within a wrapper function to mimic a
```powershell ```powershell
function New-LightFrigate { function New-LightFrigate {
[CmdletBinding()] [CmdletBinding()]
[OutputType('Ship.Corvette.LightFrigate')]
param ( param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Name,
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$Registry, [string]$Registry,
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$HullNumber [string]$HullNumber,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Name
) )
begin {} begin {}
process { process {
$Ship = [PSCustomObject]@{ $Ship = [PSCustomObject]@{
PSTypeName = $Name PSTypeName = 'Ship.Corvette.LightFrigate'
Owner = 'Martian Congressional Republic Navy' Owner = 'Martian Congressional Republic Navy'
Type = 'Light Frigate' Type = 'Light Frigate'
Class = 'Corvette' Class = 'Corvette'
Registry = $Registry Registry = $Registry
HullNumber = $HullNumber HullNumber = $HullNumber
Length = '46' LengthInMeter = 46
Name = $Name
} }
Write-Output $Ship Write-Output $Ship
} }
@ -224,5 +234,5 @@ function New-LightFrigate {
end {} end {}
} }
$Rocinante = New-LightFrigate -Name 'Rocintante' -Registry 'DE-MB2' -HullNumber '158' $Rocinante = New-LightFrigate -Name 'Rocinante' -Registry 'DE-MB2' -HullNumber '158'
``` ```

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB