Powershell [ValidateSet()] Between Separate Parameter Sets Using Same Parameter Name - powershell

OK, so I'm trying to write an advanced function that uses two different parameter set names, one is Default the other is TestAccountsOnly.
Most of this works fine, however, here's my problem:
The output of Get-Help New-SecondaryAccount gives me this in the SYNTAX section:
SYNTAX
New-SecondaryAccount [-Name] <String> [-AccountType] <String> [-Password] <String> [-Description] <String> [-OwnerEmployeeID] <String>
[[-AdditionalDescription]] [<CommonParameters>]
New-SecondaryAccount [-Name] <String> [-AccountType] <String> [-Password] <String> [-CoreOrReserved] <String> [-Description] <String>
[-OwnerEmployeeID] <String> [[-AdditionalDescription]] [<CommonParameters>]
From the looks of it, this is exactly what I want - one parameter set where I can validate a list of a handful of different -AccountTypes and move along where I have passwords, descriptions, etc., and the other where I validate just one value for AccountType and have a CoreOrReserve parameter that only belongs to the TestAccountsOnly parameter set.
Unfortunately, when trying to test this in the ISE, if I type:
New-SecondaryAccount -Name mehSomeAccount -AccountType, the only suggestion I get from IntelliSense is Test.
Can you not use [ValidateSet()] the way I'm trying to, or am I just doing it wrong?
Would really appreciate it if someone could point this out!
Function New-SecondaryAccount(DefaultParameterSetName="Default")
{
<#
.Synopsis
Creates a new secondary account based on the parameters
.DESCRIPTION
Creates a secondary AD user account based on parameters
specified. This includes several different types of accounts,
and determines the employeeType, OU, and description values
of the account created.
The CoreOrReserved parameter can only be used for accounts
where AccountType is set to Test
.INPUTS
[String]
.OUTPUTS
[ADObject]
.NOTES
.COMPONENT
MyModule_Part1
.FUNCTIONALITY
Active Directory Things
#>
[cmdletBinding(DefaultParameterSetName="Default")]
param(
[Parameter(Mandatory=$True,
Position=0,
ParameterSetName="Default",
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$True,
Position=0,
ParameterSetName="TestAccountsOnly",
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$Name,
[Parameter(Mandatory=$True,
Position=1,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateSet('ADAdmin','ServerAdmin','ServiceAccount','ChuckNorris')]
[Parameter(Mandatory=$True,
Position=1,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateSet("Test")]
[String]$AccountType,
[Parameter(Mandatory=$True,
Position=2,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_.Length -ge 12)
{
$True
}
else
{
throw "Password must be at least 12 characters"
$False
}
})]
[Parameter(Mandatory=$True,
Position=3,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_.Length -ge 12)
{
$True
}
else
{
throw "Password must be at least 12 characters"
$False
}
})]
[String]$Password,
[Parameter(Mandatory=$True,
Position=2,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateSet("Core","Reserved")]
[String]$CoreOrReserved,
[Parameter(Mandatory=$True,
Position=3,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_ -match "^TASK\d{7}\b")
{
$True
}
else
{
throw "Description must be a TASK number only`nEx. TASK1234567"
$False
}
})]
[Parameter(Mandatory=$True,
Position=4,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_ -match "^TASK\d{7}\b")
{
$True
}
else
{
throw "Description must be a TASK number only`nEx. TASK1234567"
$False
}
})]
[String]$Description,
[Parameter(Mandatory=$True,
Position=4,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($(Get-ADUser -Filter {EmployeeID -eq $_ -and EmployeeType -eq "E"}) -ne $NULL)
{
$True
}
else
{
throw "$_ must correspond to a valid FTE user's employeeID number"
$False
}
})]
[Parameter(Mandatory=$True,
Position=5,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($(Get-ADUser -Filter {EmployeeID -eq $_ -and EmployeeType -eq "E"}) -ne $NULL)
{
$True
}
else
{
throw "$_ must correspond to a valid FTE user's employeeID number"
$False
}
})]
[String]$OwnerEmployeeID,
[Parameter(Mandatory=$False,
ParameterSetName="Default",
Position=5)]
[Parameter(Mandatory=$False,
ParameterSetName="TestAccountsOnly",
Position=6)]
[Switch]$AdditionalDescription
)
BEGIN{}
PROCESS{# implementation doing all the things here}
END{}

Unfortunately, you cannot declare more than one validate set attribute per parameter, which is one reason why its designation is separate.
You might be able to play around with dynamic parameters to get what you want. I stripped out a lot of stuff for clarity.
function New-SecondaryAccount() {
[cmdletBinding()]
param (
[Parameter(Mandatory,
Position = 0,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string] $Name,
[Parameter(Mandatory, Position = 1)]
[string] $Password,
[Parameter(Position = 2)]
[switch] $TestAccount
)
DynamicParam {
$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.Mandatory = $true
$collection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$collection.Add($attribute)
if ($TestAccount) {
$validationSet = #("Test")
} else {
$validationSet = #("ADAdmin", "ServerAdmin", "ServiceAccount", "ChuckNorris")
}
$collection.Add((New-Object System.Management.Automation.ValidateSetAttribute($validationSet)))
$param = New-Object System.Management.Automation.RuntimeDefinedParameter('AccountType', [string], $collection)
$dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$dictionary.Add('AccountType', $param)
return $dictionary
}
PROCESS {
<# implementation doing all the things here #>
}
}

Related

How to set the description of a local group to blank

Consider the following code:
# create test group
New-LocalGroup -Name 'Group1' -Description 'xxx'
# update test group description to blank
Set-LocalGroup -Name 'Group1' -Description '' # fails
Set-LocalGroup -Name 'Group1' -Description $null # fails
On the contrary it is possible to create a group without description:
New-LocalGroup -Name 'Group2'
How is it possible to update the group description of a local group to blank without removing the group first? This happens on PowerShell 5.1.
Although there are some AD attributes that requires the Putex method, that doesn't count for the Description attribute. Meaning my assumption in the initial comment to the question is wrong, and it is possible to clear the Description attribute with just the Put method`:
$Name = 'Group1'
New-LocalGroup -Name $Name -Description 'xxx'
$Group = [ADSI]"WinNT://./$Name,group"
$Group.Put('Description', '')
$Group.SetInfo()
Get-LocalGroup -Name $Name
Name Description
---- -----------
Group1
The issue lays purely in the cmdlet parameter definitions. Without going into the C# programming, you might just pull this from the proxy command:
$Command = Get-Command Set-LocalGroup
$MetaData = [System.Management.Automation.CommandMetadata]$Command
$ProxyCommand = [System.Management.Automation.ProxyCommand]::Create($MetaData)
$ProxyCommand
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='https://go.microsoft.com/fwlink/?LinkId=717979')]
param(
[Parameter(Mandatory=$true)]
[ValidateNotNull()] # <-- Here is the issue
[ValidateLength(0, 48)]
[string]
${Description},
...
In other words, to quick and dirty workaround this with a proxy command:
function SetLocalGroup {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='https://go.microsoft.com/fwlink/?LinkId=717979')]
param(
[Parameter(Mandatory=$true)]
[AllowEmptyString()] # <-- Modified
[ValidateLength(0, 48)]
[string]
${Description},
[Parameter(ParameterSetName='InputObject', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
[Microsoft.PowerShell.Commands.LocalGroup]
${InputObject},
[Parameter(ParameterSetName='Default', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
${Name},
[Parameter(ParameterSetName='SecurityIdentifier', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
[System.Security.Principal.SecurityIdentifier]
${SID})
end {
if ($Description) { Set-LocalGroup #PSBoundParameters }
elseif ($Name) {
$Group = [ADSI]"WinNT://./$Name,group"
$Group.Put('Description', '')
$Group.SetInfo()
}
}
}
SetLocalGroup -Name 'Group1' -Description ''
Related bug report: #16049 AllowEmptyString()] for -Description in Set-LocalGroup/SetLocalUser
As Set-LocalGroup fails on that, the only other way I can think of is using ADSI:
$group = [ADSI]"WinNT://$env:COMPUTERNAME/Group1,group"
$group.Description.Value = [string]::Empty
$group.CommitChanges()
It's a workaround of course and I agree with iRon you should do a bug report on this.

Powershell functions, parameters and arg

I write own powershell func for debug like:
function StartDebug {
param (
[PARAMETER(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
$FunctionName,
[PARAMETER(Mandatory = $false)]
$OtherArg
)
try {& $FunctionName $OtherArg} catch {...} finally {...}
and use it everyway, but i need more arg after $FunctionName. is it realistic to pass many arguments in this case bec use from 0 to 10 arg. do I have to list all the arguments that can be in the parameters of the function? like:
function StartDebug {
param (
[PARAMETER(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
$FunctionName,
[PARAMETER(Mandatory = $false)]
$OtherArg,
[PARAMETER(Mandatory = $false)]
$OtherArg1,
[PARAMETER(Mandatory = $false)]
$OtherArg2,
[PARAMETER(Mandatory = $false)]
$OtherArg3
)
try {& $FunctionName $OtherArg OtherArg1 OtherArg2 OtherArg3 } catch {...} finally {...}
but i dont use positional parameters in code and too many named parameters in code (~100)
Interested in any ideas about this. tnx!
The magic word is Splatting. You can provide an array or a hashtable containing your arguments to a function. The splatter is written with an #VariableName instead of the $:
function StartDebug {
param (
[PARAMETER(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
$FunctionName,
[PARAMETER(Mandatory = $false)]
$OtherArg
)
try {& $FunctionName #OtherArg # Watch out for the # in the OtherArg
} catch {$_} finally {}
}
$FunctionName = 'Get-ChildItem'
$Splatter = #{
Path = 'C:\'
Filter = 'Users'
Directory = $true
}
$Splatter2 = #('c:\')
StartDebug $FunctionName $Splatter
StartDebug $FunctionName $Splatter2
However if you want to use single items as $OtherArg you will have to provide them as single element array as can be seen with $Splatter2. Or extend your function to transform single arguments in arrays automatically, but thats up to you.
I think you better run it using scriptblock:
$result = Invoke-DeepDebug { Get-ChildItem -Path 'C:\' ; Get-Service -InformationAction Continue}
And in Invoke-DeepDebug you can work with $Command.AST as deep and detailed as you want.
Function Invoke-DeepDebug {
param(
[Parameter(Mandatory=$true, Position=0)]
[Scriptblock]$Command
)
Write-Host -f Cyan "Executing " -n
Write-Host -f Magenta $Command.Ast.Extent.Text -n
Write-Host -f Yellow " ... " -n
$result = $null
try {
$result = Invoke-Command $Command -ErrorAction Stop
Write-Host -f Green "OK!"
} catch {
Write-Host -f Red "Error"
Write-Host -f Red "`t$($_.Exception.Message)"
}
return $result
}

Make parameter mandatory based on switch - PowerShell

I was struggling in a situation where I need to make some parameters mandatory ONLY if a switch or combination of switches is used. Below is the example of what I am trying to do:
[CmdletBinding(DefaultParameterSetName='DefaultConfiguration')]
Param
(
[Parameter(Mandatory=$true)][String]$Location,
[Parameter(Mandatory=$true)][String]$DPMServername,
[Parameter(Mandatory=$False, ParameterSetName='CustomConfiguration')]
[Switch]$CustomizeDPMSubscriptionSettings,
[Parameter(Mandatory=$True, ParameterSetName='CustomConfiguration')]
[String]$StagingAreaPath,
[Parameter(Mandatory=$False, ParameterSetName='EncryptionSettings')]
[Parameter(ParameterSetName='CustomConfiguration')]
[Switch]$SetEncryption,
[Parameter(Mandatory=$true, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$False, ParameterSetName='CustomConfiguration')]
[String]$EncryptionPassPhrase,
[Parameter(Mandatory=$False, ParameterSetName='ProxyEnabled')]
[Parameter(ParameterSetName='CustomConfiguration')]
[Switch]$SetProxy,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(ParameterSetName='CustomConfiguration')]
[String]$ProxyServerAddress,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$False, ParameterSetName='CustomConfiguration')]
[String]$ProxyServerPort
)
Here, I need to follow below conditions:
If -CustomizeDPMSubscriptionSettings (Switch) parameter is used,
it must ask for the Staging AreaPath ---- This is Working fine
ONLY when -CustomizeDPMSubscriptionSettings (Switch) parameter is
used with -SetEncryption, it must ask for -EncryptionPassPhrase
And ONLY when -CustomizeDPMSubscriptionSettings (Switch) parameter is
used with -SetProxy, it must ask for -ProxyServerAddress and -ProxyServerPort
Sorry if this sounds like a repeated question but other posts I found here are not helping me solve my issue. I am confused :-(
NOTE: Above code is part of what I was trying with different combinations. Please correct as necessary.
Here is a solution that seems to do what you expect.
What I did was create a parameter set for each possible combination.
- CustomConfiguration
- EncryptionSettings
- ProxyEnabled
- EncryptionAndProxy
One limitation is that it will not prompt for specific missing parameters unless using EncryptionAndProxy, but will instead state that it cannot resolve the parameter set.
[CmdletBinding(DefaultParameterSetName='DefaultConfiguration')]
Param
(
[Parameter(Mandatory=$true)][String]$Location,
[Parameter(Mandatory=$true)][String]$DPMServername,
[Parameter(Mandatory=$True, ParameterSetName='CustomConfiguration')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[Switch]$CustomizeDPMSubscriptionSettings,
[Parameter(Mandatory=$True, ParameterSetName='CustomConfiguration')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$StagingAreaPath,
[Parameter(Mandatory=$True, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[Switch]$SetEncryption,
[Parameter(Mandatory=$true, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$EncryptionPassPhrase,
[Parameter(Mandatory=$True, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[Switch]$SetProxy,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$ProxyServerAddress,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$ProxyServerPort
)
I'm looking into a second potential solution based on dynamic parameters.
Edit: As promised, here's a solution based on dynamic parameters
[CmdletBinding(DefaultParameterSetName='DefaultConfiguration')]
Param
(
[Parameter(Mandatory=$true)][String]$Location,
[Parameter(Mandatory=$true)][String]$DPMServername,
[Switch]$CustomizeDPMSubscriptionSettings,
[Switch]$SetEncryption,
[Switch]$SetProxy
)
DynamicParam
{
$paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$attributes = New-Object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $true
$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
# If "-SetEncryption" is used, then add the "EncryptionPassPhrase" parameter
if($SetEncryption)
{
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("EncryptionPassPhrase", [String], $attributeCollection)
$paramDictionary.Add("EncryptionPassPhrase", $dynParam1)
}
# If "-SetProxy" is used, then add the "ProxyServerAddress" "ProxyServerPort" and parameters
if($SetProxy)
{
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("ProxyServerAddress", [String], $attributeCollection)
$paramDictionary.Add("ProxyServerAddress", $dynParam1)
$dynParam2 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("ProxyServerPort", [String], $attributeCollection)
$paramDictionary.Add("ProxyServerPort", $dynParam2)
}
# If "-CustomizeDPMSubscriptionSettings" is used, then add the "StagingAreaPath" parameter
if($CustomizeDPMSubscriptionSettings)
{
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("StagingAreaPath", [String], $attributeCollection)
$paramDictionary.Add("StagingAreaPath", $dynParam1)
}
return $paramDictionary
}
Process{
foreach($key in $PSBoundParameters.keys)
{
Set-Variable -Name $key -Value $PSBoundParameters."$key" -Scope 0
}
}
What this one does is dynamically add parameters to your function based on the presence of each switch.
This supports autocompletion, and has better support for missing parameters. It will explicitly ask for missing parameters if the corresponding switch is used.
Second edit: I added the Process section that's mandatory with this construct, as well as the variable creation bit, which makes things much easier.

Powershell gwmi for other users

I am trying to collect user profile information for users on a machine and I was wondering if I could get it with gwmi. Here is how I get printers for the current user:Get-WmiObject win32_printer. How can I get the same info for the user "Test" on the same machine?
As it happens, I can't sleep, so I came up with these 2 functions:
function Get-UserSid {
[CmdletBinding()]
param(
[Parameter(
ParameterSetName='NTAccount',
Mandatory=$true,
ValueFromPipeline=$true,
Position=0
)]
[System.Security.Principal.NTAccount]
$Identity ,
[Parameter(
ParameterSetName='DomainAndUser',
Mandatory=$true
)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('^[^\\]+$')]
[String]
$Domain ,
[Parameter(
ParameterSetName='DomainAndUser',
Mandatory=$true
)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('^[^\\]+$')]
[String]
$User
)
Begin {
if ($PSCmdlet.ParameterSetName -eq 'DomainAndUser') {
$Identity = New-Object System.Security.Principal.NTAccount -ArgumentList $Domain,$User
}
}
Process {
$Identity.Translate([System.Security.Principal.SecurityIdentifier])
}
}
function Get-PrinterNameByUser {
[CmdletBinding(DefaultParameterSetName='Ambiguous')]
param(
[Parameter(
ParameterSetName='ByAccount',
Mandatory=$true
)]
[System.Security.Principal.NTAccount]
$Account ,
[Parameter(
ParameterSetName='BySID',
Mandatory=$true
)]
[System.Security.Principal.SecurityIdentifier]
$SID ,
[Parameter(
ParameterSetName='Ambiguous',
Mandatory=$true,
Position=0,
ValueFromPipeline=$true
)]
[ValidateNotNullOrEmpty()]
[String]
$Identity
)
Begin {
Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"
if ($PSCmdlet.ParameterSetName -eq 'ByAccount') {
$SID = $Account | Get-UserSid
}
}
Process {
if ($PSCmdlet.ParameterSetName -eq 'Ambiguous') {
try {
$SID = [System.Security.Principal.SecurityIdentifier]$Identity
} catch [System.InvalidCastException] {
$Account = [System.Security.Principal.NTAccount]$Identity
$SID = $Account | Get-UserSid
}
}
Get-ChildItem -Path "Registry::\HKEY_Users\$($SID.Value)\Printers" | Select-Object -ExpandProperty Property -Unique
}
}
Usage
Get-PrinterNameByUser Test
Get-PrinterNameByUser 'domain\test'
Get-PrinterNameByUser 'S-1-S-21-65454546-516413534-4444'
All of those could be piped as well:
'Test' | Get-PrinterNameByUser
'domain\test' | Get-PrinterNameByUser
'S-1-S-21-65454546-516413534-4444' | Get-PrinterNameByUser
'S-1-S-21-65454546-516413534-4444','user1','machine\user2','domain\user3' | Get-PrinterNameByUser
Explanation
In the registry at HKU\S-ID-HERE\Printers there are some keys with properties. The property names are the printers. I wasn't able to test this on enough machines, so I wasn't certain which key(s) I should check, and whether they would be different depending on whether it was a local or network printer, etc., so I'm just getting the properties from all the keys and returning the unique ones.
The helper function Get-UserSid just provides a convenient way to get a SID from a user name.
Most of Get-PrinterNameByUser is just code to figure out what you've given it and translate it at needed. The meat of it that returns what you want is just the one line:
Get-ChildItem -Path "Registry::\HKEY_Users\$($SID.Value)\Printers" | Select-Object -ExpandProperty Property -Unique

PowerShell Mail Encoding

I'm trying to send an e-mail with a powershell-function using the cmdlet send-mailmessage.
I need to modify the encoding of the text.
The cmdlet send-mailmessage has a parameter "encoding" which uses the class System.Text.Encoding.
So I have to use something like this:
Send-Mailmessage -Encoding ([System.Text.Encoding]::UTF8)
I'd like to use -Encoding UTF8 instead. The Out-File cmdlet works like this. How can I reproduce the behaviour from the Out-File cmdlet?
That's my idea but I find it a bit circumstantial:
[parameter()][ValidateSet("UTF8","Unicode","ASCII")][String]$Encoding
With this I would create the encoding accordingly.
[System.Text.Encoding]::$Encoding
You can create a proxy function, change the type of the Encoding parameter to System.String and manipulate it in the Begin block. I included this example in the PowerShell Proxy Extensions module.
function Send-MailMessage
{
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
[Alias('PsPath')]
[ValidateNotNullOrEmpty()]
[System.String[]]
${Attachments},
[ValidateNotNullOrEmpty()]
[System.String[]]
${Bcc},
[Parameter(Position=2)]
[ValidateNotNullOrEmpty()]
[System.String]
${Body},
[Alias('BAH')]
[Switch]
${BodyAsHtml},
[Parameter()]
[ValidateSet('ASCII','UTF8','UTF7','UTF32','Unicode','BigEndianUnicode','Default','OEM')]
[ValidateNotNullOrEmpty()]
[Alias('BE')]
[System.String]
$Encoding,
[ValidateNotNullOrEmpty()]
[System.String[]]
${Cc},
[Alias('DNO')]
[ValidateNotNullOrEmpty()]
[System.Net.Mail.DeliveryNotificationOptions]
${DeliveryNotificationOption},
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[System.String]
${From},
[Parameter(Position=3)]
[Alias('ComputerName')]
[ValidateNotNullOrEmpty()]
[System.String]
${SmtpServer},
[ValidateNotNullOrEmpty()]
[System.Net.Mail.MailPriority]
${Priority},
[Parameter(Mandatory=$true, Position=1)]
[Alias('sub')]
[ValidateNotNullOrEmpty()]
[System.String]
${Subject},
[Parameter(Mandatory=$true, Position=0)]
[ValidateNotNullOrEmpty()]
[System.String[]]
${To},
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
${Credential},
[Switch]
${UseSsl}
)
begin
{
try
{
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Encoding'))
{
$null = $PSCmdlet.MyInvocation.BoundParameters.Remove('Encoding')
$newValue = & {
if ($Encoding -eq 'OEM')
{
[System.Text.Encoding]::GetEncoding($Host.CurrentCulture.TextInfo.OEMCodePage)
}
else
{
[System.Text.Encoding]::$Encoding
}
}
$null = $PSCmdlet.MyInvocation.BoundParameters.Add('Encoding',$newValue)
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Send-MailMessage', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = {& $wrappedCmd #PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
catch
{
throw
}
}
process
{
try
{
$steppablePipeline.Process($_)
}
catch
{
throw
}
}
end
{
try
{
$steppablePipeline.End()
}
catch
{
throw
}
}
}