PowerShell Mail Encoding - email

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
}
}
}

Related

Attribute PasswordNeverExpires & UserMayNotChangePassword in New-LocalUser function not working in PowerShell

Attribute PasswordNeverExpires & UserMayNotChangePassword in New-LocalUser function not working.
These attributes where working until, I restarted the machine. After restarting, I found these functionalities were not working.
I had certain network issues in my computer so, I manually installed the LocalAccount module.
Then, I checked the LocalAccount.psm1 file where these attributes where missing. I myself added these attributes but, I am not sure whether its syntactically correct or not.
Initial LocalAccount.psm1 file
function New-LocalUser
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]$Name,
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=1)]
[string[]]$Computername = "$Env:computername",
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=2)]
[ValidateScript({$_.GetType().Name -eq 'SecureString'})]
[array][system.security.securestring]$Password,
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=3)]
[string[]]$Description=' ',
)
Begin
{
}
Process
{
$cred=New-Object -TypeName System.management.automation.pscredential -ArgumentList "null",$Password[0]
$Plaintextpassword=$cred.GetNetworkCredential().password
$computer = [ADSI]"WinNT://$($ComputerName[0]),computer"
$user = $computer.Create("User", "$($Name[0])")
$user.setpassword("$PlainTextPassword")
$user.put("Description",$($Description[0]))
$user.SetInfo()
}
End
{
}
}
After modification
function New-LocalUser
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]$Name,
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=1)]
[string[]]$Computername = "$Env:computername",
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=2)]
[ValidateScript({$_.GetType().Name -eq 'SecureString'})]
[array][system.security.securestring]$Password,
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=3)]
[string[]]$Description=' ',
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=4)]
[bool]$PasswordNeverExpires,
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=5)]
[bool]$UserMayNotChangePassword
)
Begin
{
}
Process
{
$cred=New-Object -TypeName System.management.automation.pscredential -ArgumentList "null",$Password[0]
$Plaintextpassword=$cred.GetNetworkCredential().password
$computer = [ADSI]"WinNT://$($ComputerName[0]),computer"
$user = $computer.Create("User", "$($Name[0])")
$user.setpassword("$PlainTextPassword")
$user.put("Description",$($Description[0]))
$user.set("PasswordNeverExpires",$($PasswordNeverExpires))
$user.set("UserMayNotChangePassword",$($UserMayNotChangePassword))
$user.SetInfo()
}
End
{
}
}
Can anyone please check your localaccount.psm1 and suggest me where I went wrong.
Thanks in advance.
You need to alter certain bits in the UserFlags property of the user:
if ($UserMayNotChangePassword) {
# ADS_UF_PASSWD_CANT_CHANGE --> user cannot change password
$user.UserFlags.value = $user.UserFlags.value -bor 0x40
$user.SetInfo()
}
if ($PasswordNeverExpires) {
# ADS_UF_DONT_EXPIRE_PASSWD --> password never expires
$user.UserFlags.value = $user.UserFlags.value -bor 0x10000
$user.SetInfo()
}
alternative for the above
$flags = 0
if ($UserMayNotChangePassword) {
# ADS_UF_PASSWD_CANT_CHANGE --> user cannot change password
$flags += 0x40
}
if ($PasswordNeverExpires) {
# ADS_UF_DONT_EXPIRE_PASSWD --> password never expires
$flags += 0x10000
}
# if one or both properties are set
if ($flags) {
$user.UserFlags.value = $user.UserFlags.value -bor $flags
$user.SetInfo()
}
These are the values for the ADS_USER_FLAG_ENUM
ADS_UF_SCRIPT = 0x1
ADS_UF_ACCOUNTDISABLE = 0x2
ADS_UF_HOMEDIR_REQUIRED = 0x8
ADS_UF_LOCKOUT = 0x10
ADS_UF_PASSWD_NOTREQD = 0x20
ADS_UF_PASSWD_CANT_CHANGE = 0x40
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80
ADS_UF_TEMP_DUPLICATE_ACCOUNT = 0x100
ADS_UF_NORMAL_ACCOUNT = 0x200
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 0x800
ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x1000
ADS_UF_SERVER_TRUST_ACCOUNT = 0x2000
ADS_UF_DONT_EXPIRE_PASSWD = 0x10000
ADS_UF_MNS_LOGON_ACCOUNT = 0x20000
ADS_UF_SMARTCARD_REQUIRED = 0x40000
ADS_UF_TRUSTED_FOR_DELEGATION = 0x80000
ADS_UF_NOT_DELEGATED = 0x100000
ADS_UF_USE_DES_KEY_ONLY = 0x200000
ADS_UF_DONT_REQUIRE_PREAUTH = 0x400000
ADS_UF_PASSWORD_EXPIRED = 0x800000
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000
BTW. I would make both parameters [switch] instead of [bool] like this:
[switch]$PasswordNeverExpires,
[switch]$UserMayNotChangePassword
so you can either leave them out or add to the New-LocalUser function call

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
}

Pipelining internally from one function to another

I have a module which has the following two functions, which are almost identical:
<#
.SYNOPSIS
Retrieves a VApp from VCloud.
#>
Function Get-VApp
{
[CmdletBinding()]
[OutputType([System.Xml.XmlElement])]
Param(
[Parameter(Mandatory = $true)]
[System.Xml.XmlElement] $Session,
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string[]] $VAppName
)
Begin {
[System.Xml.XmlElement] $queryList = $Session.GetQueryList();
[System.Xml.XmlElement[]] $vAppRecords = $queryList.GetVAppsByRecords().VAppRecord;
}
Process {
ForEach ($VAN in $VAppName)
{
$vAppRecords |
Where-Object { $_.name -eq $VAN } |
ForEach-Object { $_.Get(); }
}
}
End
{
#
}
}
and
<#
.SYNOPSIS
Retrieves a VAppRecord from VCloud.
#>
Function Get-VAppRecord
{
[CmdletBinding()]
[OutputType([System.Xml.XmlElement])]
Param(
[Parameter(Mandatory = $true)]
[System.Xml.XmlElement] $Session,
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string[]] $VAppName
)
Begin {
[System.Xml.XmlElement] $queryList = $Session.GetQueryList();
[System.Xml.XmlElement[]] $vAppRecords = $queryList.GetVAppsByRecords().VAppRecord;
}
Process {
ForEach ($VAN in $VAppName)
{
$vAppRecords |
Where-Object { $_.name -eq $VAN } |
ForEach-Object { $_; }
}
}
End
{
#
}
}
Essentially, Get-VApp is like Get-VAppRecord, except that the former calls a Get() method on the returned object. This seems wasteful. If I wasn't bothering with pipelines, it would be easy:
Function Get-VApp
{
[CmdletBinding()]
[OutputType([System.Xml.XmlElement])]
Param(
[Parameter(Mandatory = $true)]
[System.Xml.XmlElement] $Session,
[Parameter(Mandatory = $true)]
[string[]] $VAppName
)
Get-VAppRecord $Session $VAppName |
ForEach-Object {
$_.Get();
}
}
But obviously the pipeline messes things up. I don't call the code in the Begin block multiple times for efficiency, and I would like to find a way to "play nice" with the pipeline without having to batch up records.
The SteppablePipeline class is designed for wrapping pipeline-enabled commands without messing up their pipeline support.
You don't even need to know how to set it up, ProxyCommand.Create() will generate the scaffolding for it!
So let's start out by creating a proxy function for Get-VAppRecord:
$GetVAppRecordCommand = Get-Command Get-VAppRecord
$GetVAppRecordCommandMetadata = [System.Management.Automation.CommandMetadata]::new($GetVAppRecordCommand)
# returns the body of the new proxy functions
[System.Management.Automation.ProxyCommand]::Create($GetVAppRecordCommandMetadata)
... and then we just need to add the Get() call in the process block of it:
function Get-VApp {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)]
[System.Xml.XmlElement]
${Session},
[Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
[string[]]
${VAppName})
begin
{
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-VAppRecord', [System.Management.Automation.CommandTypes]::Function)
$scriptCmd = {& $wrappedCmd #PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline()
$steppablePipeline.Begin($MyInvocation.ExpectingInput) # Many examples use $PSCmdlet; however setting this ensures that $steppablePipeline.Process() returns the output of the inner function.
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_) |ForEach-Object {
# call Get() on the record
$_.Get()
}
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
}

Trying to code a function which takes a scriptblock with params and argument list, edits that scriptblock and then runs invoke-command

I am editing a function which it will invoke a command directly on the VM. The issue I keep running into is if someone passes a function declaration as a scriptblock, I get and error when calling create, because params() is not at the top of the scriptblock.
Trying to figure out how I can still set-fulllanguage first then execute a function with params.
function Invoke-DirectOnVM
{
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[CloudEngine.Configurations.EceInterfaceParameters]
$Parameters,
[Parameter(Mandatory = $true)]
[String[]]$VMNames,
[Parameter(Mandatory = $true)]
[Object]$VMCredential,
[Parameter(Mandatory = $true)]
[ScriptBlock]$ScriptBlock,
[Object[]]$ArgumentList = $null
)
{
Invoke-Command -VMName $localVMs -Credential $using:VMCredential -ScriptBlock ([ScriptBlock]::Create($("Import-Module OpenUpSession; Set-FullLanguage; `r`n" + $using:ScriptBlock)))
}
Remove the $using: from the scriptblock and it should work properly. I took the liberty of cleaning up the code a bit. The result looks like:
function Invoke-DirectOnVM
{
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[CloudEngine.Configurations.EceInterfaceParameters]
$Parameters,
[Parameter(Mandatory)]
[String[]]
$VMNames,
[Parameter(Mandatory)]
$VMCredential,
[Parameter(Mandatory)]
[ScriptBlock]
$ScriptBlock,
[Parameter()]
[Object[]]
$ArgumentList = $null
)
$PSBoundParameters.Remove("ScriptBlock")
Invoke-Command #PSBoundParameters -ScriptBlock ([ScriptBlock]::Create( "Import-Module OpenUpSession; Set-FullLanguage; `r`n" + $ScriptBlock ))
}

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

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 #>
}
}