I am curious if it's possible to pass arguments to a powershell module using object notation. Look at the following definition of a function:
function Get-Something
{
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string] $Name,
[Parameter(Mandatory=$true, Position=1)]
[int] $Id
)
}
can I call this function in the following notation:
Get-Something(Name "Vance Refrigeration", Id "9")
instead of doing:
Get-Something -Name "Vance Refrigeration" -Id 9
As far as I know, the only other way than to type every variable with '-Name "Vance Refrigeration" -Id 9' is to do the following:
function Get-Something
{
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string] $Name,
[Parameter(Mandatory=$true, Position=1)]
[int] $Id
)
Write-Host $name en $id
}
$object = #{Name = "Vance Refrigeration"; Id = "9"}
Get-Something #object
Or with the prettier styling for the object:
$object = #{
Name = "Vance Refrigeration"
Id = "9"
}
Get-Something #object
Related
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 #>
}
}
Currently I'm doing like that:
function Invoke-Service
{
Param(
[parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[int] $Id,
[parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string] $Name,
[parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[int] $Age
)
DoSomeWork |
New-Object PSObject -Property #{ Id = $Id; Name = $Name; Age = $Age }
}
This function can obtain it's parameters directly or from Import-Csv output, or select output.
But very often I want to continue processing down the pipeline with all the power of PSObject:
Import-Csv -Path "data.csv" |
Invoke-Service |
... #
And my question is: do I need to call New-Object enumerating all parameters or is there a keyword or other technique which I missed?
Use the $PSBoundParameters variable:
New-Object psobject -Property $PSBoundParameters
$PSBoundParameters is an automatic variable that contains a hashtable with an entry per named parameter, using the full parameter name as the key and the parameter argument as the value, so in your example it's already 100% aligned with the input you're trying to pass
I've been trying to write a utility function that will return initialized [System.Data.DataTable] object data type.
I am aware of the annoying PowerShell functions return behavior where it tries to "unroll" everything into a [System.Array] return type.
Previously I have always managed to get around that problem.
Usually using the "comma trick" to return your own array #(,$result) aways worked - however this time this does not seem to make a difference and I've run out of options...
Another trick I normally use is the $null assignments within the Process block (see my code bellow) - this way I fool the pipeline there is nothing to "unroll" on the output...
I am not taking impossible for an answer, so far based on my experience nothing is impossible in PowerShell :)
Here is my code:
function Get-SourceDataTable
{
[OutputType([System.Data.DataTable])]
[cmdletbinding()]
param(
[parameter(Mandatory=$true, Position=0)]
[System.Data.SqlClient.SqlBulkCopy] $Destination,
[parameter(Mandatory=$true, Position=1)]
[System.Collections.Specialized.OrderedDictionary] $ColumnDefinition,
[parameter(Mandatory=$false, Position=2)]
[int]$ColumnStartIndex = 0
)
BEGIN{
$datatable = New-Object System.Data.DataTable
$colIndex = $ColumnStartIndex
}
PROCESS{
$ColumnDefinition.Keys |
foreach {
$null = $datatable.Columns.Add($_, $ColumnDefinition[$_]) # define each column name and data type
$null = $Destination.ColumnMappings.Add($_, $colIndex) # map column to destination table
$colIndex++
}
}
END{
return ,$datatable
}
}
I hope someone can get this code working...
Rather than return use Write-Output -NoEnumerate. For example:
function New-DataTable {
$datatable = New-Object System.Data.DataTable
$null = $datatable.Columns.Add("x",[int])
$null = $datatable.Columns.Add("y",[int])
$null = $datatable.Rows.Add(#(1,2))
$null = $dataTable.Rows.Add(#(3,4))
Write-Output -NoEnumerate $datatable
}
New-DataTable | Get-Member
Note however that if you just type New-DataTable, it might look like enumberated rows, but Get-Member tells you the actual type returned.
I got the function from the question to return DataTable type when I used LoadWithPartialName to load the assembly containing the type and pipe it out with Out-Null.
Don't ask my why, but feel free to comment if you know the reason.
The working function code is below. Note the return statement is not necessary I only use it to improve code readability:
function Get-SourceDataTable
{
[OutputType([System.Data.DataTable])]
[cmdletbinding()]
param(
[parameter(Mandatory=$true, Position=0)]
[System.Data.SqlClient.SqlBulkCopy] $Destination,
[parameter(Mandatory=$true, Position=1)]
[System.Collections.Specialized.OrderedDictionary] $ColumnDefinition,
[parameter(Mandatory=$false, Position=2)]
[int]$ColumnStartIndex = 0
)
BEGIN{
[System.Reflection.Assembly]::LoadWithPartialName("System.Data") | Out-Null
$datatable = New-Object System.Data.DataTable
$colIndex = $ColumnStartIndex
}
PROCESS{
$ColumnDefinition.Keys |
foreach {
$null = $datatable.Columns.Add($_, $ColumnDefinition[$_]) # define each column name and data type
$null = $Destination.ColumnMappings.Add($_, $colIndex) # map column to destination table
$colIndex++
}
}
END{
return ,$datatable
}
}
To summarize all known possible solutions to the problem of forcing PowerShell function to return specific data type:
use $null assignments
use comma to return an array ,$variable
use LoadWithPartialName("Assembly.Name") | Out-Null
use Write-Output -NoEnumerate $variable to return the type - credit goes to Burt_Harris
Finally, after the input from Burt_Harris (THANKS Burt!) the final working version of the function from this question is this:
function Get-SourceDataTable
{
[OutputType([System.Data.DataTable])]
[cmdletbinding()]
param(
[parameter(Mandatory=$true, Position=0)]
[System.Data.SqlClient.SqlBulkCopy] $Destination,
[parameter(Mandatory=$true, Position=1)]
[System.Collections.Specialized.OrderedDictionary] $ColumnDefinition,
[parameter(Mandatory=$false, Position=2)]
[int]$ColumnStartIndex = 0
)
BEGIN{
#[System.Reflection.Assembly]::LoadWithPartialName("System.Data") | Out-Null
$datatable = New-Object System.Data.DataTable
$colIndex = $ColumnStartIndex
}
PROCESS{
$ColumnDefinition.Keys |
foreach {
$null = $datatable.Columns.Add($_, $ColumnDefinition[$_]) # define each column name and data type
$null = $Destination.ColumnMappings.Add($_, $colIndex) # map column to destination table
$colIndex++
}
}
END{
#return ,$datatable
Write-Output -NoEnumerate $datatable
}
}
I am trying to find the way to create values for parameters that can be auto completed or show all available options with tab, like the value "allsigned" in the cmdlet
Set-ExecutionPolicy -ExecutionPolicy AllSigned
¿Any ideas about how this is called in programation or how can I achieve this ?
yes its called validateset
Param
(
[parameter(Mandatory=$true)]
[ValidateSet("Low", "Average", "High")]
[String[]]
$Detail
)
advanced functions/cmdlets or just parameter validation, parameter sets, etc
snippet from the ISE:
function Verb-Noun
{
[CmdletBinding(DefaultParameterSetName='Parameter Set 1',
SupportsShouldProcess=$true,
PositionalBinding=$false,
HelpUri = 'http://www.microsoft.com/',
ConfirmImpact='Medium')]
[Alias()]
[OutputType([String])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=0,
ParameterSetName='Parameter Set 1')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateCount(0,5)]
[ValidateSet("sun", "moon", "earth")]
[Alias("p1")]
$Param1,
# Param2 help description
[Parameter(ParameterSetName='Parameter Set 1')]
[AllowNull()]
[AllowEmptyCollection()]
[AllowEmptyString()]
[ValidateScript({$true})]
[ValidateRange(0,5)]
[int]
$Param2,
# Param3 help description
[Parameter(ParameterSetName='Another Parameter Set')]
[ValidatePattern("[a-z]*")]
[ValidateLength(0,15)]
[String]
$Param3
)
Begin
{
}
Process
{
if ($pscmdlet.ShouldProcess("Target", "Operation"))
{
}
}
End
{
}
}
Say I have a Powershell script TestParameters.ps1 like this, with two mandatory, named parameters and two optional parameters:
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True)]
[string] $AFile = "C:\A\Path",
[Parameter(Mandatory=$True)]
[ValidateSet("A","B","C", "D")]
[string] $ALetter = "A",
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Optional1 = "Foo",
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Optional2 = "Bar"
)
echo "Hello World!"
$psboundparameters.keys | ForEach {
Write-Output "($_)=($($PSBoundParameters.$_))"
}
Say I call the script like this:
.\TestParameters.ps1 `
-AFile "C:\Another\Path" `
-ALetter "B"
which produces output:
Hello World!
(AFile)=(C:\Another\Path)
(ALetter)=(B)
Powershell set the variables $Optional1 and $Optional2 ... but how do I easily display them to the screen, like the way I use $PSBoundParameters?
I do not want to simply write the following every time I have a script:
Write-Host $AFile
Write-Host $ALetter
Write-Host $Optional1
Write-Host $Optional2
Notes:
$args only seems to include unbound parameters, not default
parameters
$MyInvocation seems to only include commands passed on the command lineEDIT: MyInvocation has member variable MyCommand.Parameters, which seems to have all the parameters, not just those passed on the command line...see the accepted answer, below.
Get-Variable seems to include the optional variables in the result list, but I do not know how to differentiate them from the other variables
The following seems to work on my box... probably not the best way to do it, but it seems to work in this case, at least...
[cmdletbinding()]
param([Parameter(Mandatory=$True)]
[string] $AFile = "C:\A\Path",
[Parameter(Mandatory=$True)]
[ValidateSet("A","B","C", "D")]
[string] $ALetter = "A",
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Optional1 = "Foo",
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Optional2 = "Bar"
)
echo "Hello World!"
($MyInvocation.MyCommand.Parameters ).Keys | %{
$val = (Get-Variable -Name $_ -EA SilentlyContinue).Value
if( $val.length -gt 0 ) {
"($($_)) = ($($val))"
}
}
Saved as allparams.ps1, and run it looks like:
.\allparams.ps1 -ALetter A -AFile "C:\Another\Path"
Hello World!
(AFile) = (C:\Another\Path)
(ALetter) = (A)
(Optional1) = (Foo)
(Optional2) = (Bar)
Using AST:
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True)]
[string] $AFile = "C:\A\Path",
[Parameter(Mandatory=$True)]
[ValidateSet("A","B","C", "D")]
[string] $ALetter = "A",
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Optional1 = "Foo",
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[string] $Optional2 = "Bar"
)
echo "Hello World!"
$psboundparameters.keys | ForEach {
Write-Output "($_)=($($PSBoundParameters.$_))"
}
$ast = [System.Management.Automation.Language.Parser]::
ParseFile($MyInvocation.InvocationName,[ref]$null,[ref]$Null)
$ast.ParamBlock.Parameters | select Name,DefaultValue