2

I'm having some issues with defining the position of parameters. For example, when I pipe a $Folder to my function, the positions of the other parameters change.

Normally the paramter $Folder has position=0, but when something is piped to the function it's not taking into account that position 0 is before the pipeline symbol an no longer behind it.

The function can be used in all cases with or without the switch $Recurse and the $Inheritance set to On/Off. The main purpose is to set permissions or to set the inheritance on a folder. A combination of both is also possible.

[CmdletBinding(SupportsShouldProcess=$True,DefaultParametersetName="SetPermissions")]
Param(
    [parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({Test-Path $_ -PathType Container})]
    [String[]]$Folder,

    [parameter(Mandatory=$true,Position=1,ParameterSetName='SetPermissions')]
    [parameter(Mandatory=$false,ParameterSetName='SetInheritance')]
    [ValidateNotNullOrEmpty()]
    [String]$SAMaccountName,

    [parameter(Mandatory=$true,Position=2,ParameterSetName='SetPermissions')]
    [parameter(Mandatory=$false,ParameterSetName='SetInheritance')]
    [ValidateNotNullOrEmpty()]
    [ValidateSet('ReadAndExecute','Modify','FullControl','ListFolderContents')]
    [String]$Grant,

    [parameter(Mandatory=$false)]
    [ValidateNotNullOrEmpty()]
    [String]$Domain = 'MyDomain',

    [parameter(Mandatory=$false,Position=3,ParameterSetName='SetPermissions')]
    [parameter(Mandatory=$true,Position=1,ParameterSetName='SetInheritance')]
    [ValidateNotNullOrEmpty()]
    [ValidateSet('On','Off')]
    [String]$Inheritance,

    [Switch]$Recurse
)

All the options that are possible:

# SetPermissions
Set-ACLspecial -Folder 'C:\Folder' -SAMaccountName 'Bob' -Grant ReadAndExecute
Set-ACLspecial -Folder 'C:\Folder' -SAMaccountName 'Bob' -Grant ReadAndExecute -Recurse
Set-ACLspecial -Folder 'C:\Folder' -SAMaccountName 'Bob' -Grant ReadAndExecute -Inheritance On
Set-ACLspecial -Folder 'C:\Folder' -SAMaccountName 'Bob' -Grant ReadAndExecute -Inheritance On -Recurse

Set-ACLspecial 'C:\Folder' 'Bob' ReadAndExecute
Set-ACLspecial 'C:\Folder' 'Bob' ReadAndExecute -Recurse
Set-ACLspecial 'C:\Folder' 'Bob' ReadAndExecute On
Set-ACLspecial 'C:\Folder' 'Bob' ReadAndExecute On -Recurse

'C:\Folder' | Set-ACLspecial 'Bob' ReadAndExecute
'C:\Folder' | Set-ACLspecial 'Bob' ReadAndExecute -Recurse
'C:\Folder' | Set-ACLspecial 'Bob' ReadAndExecute On
'C:\Folder' | Set-ACLspecial 'Bob' ReadAndExecute On -Recurse

# SetInheritance
Set-ACLspecial 'C:\Folder' -Inheritance Off
Set-ACLspecial 'C:\Folder' -Inheritance Off -Recurse
'C:\Folder' | Set-ACLspecial -Inheritance Off
'C:\Folder' | Set-ACLspecial -Inheritance Off -Recurse

The current syntax:

Set-HCaclTEST [-Folder] <String[]> [-SAMaccountName] <String> [-Grant] <String> [-Domain <String>] [[-Inheritance] <String>] [-Recurse] [-WhatIf] [-Confirm] [<Common
Parameters>]

Set-HCaclTEST [-Folder] <String[]> [-SAMaccountName <String>] [-Grant <String>] [-Domain <String>] [-Inheritance] <String> [-Recurse] [-WhatIf] [-Confirm] [<CommonPa
rameters>]

The error:

'C:\Folder' | Set-ACLspecial 'Bob' ReadAndExecute
# Test-Path on 'Bob' is not working... PowerShell sees Bob as the variable `$Folder`, but it's not.
DarkLite1
  • 13,637
  • 40
  • 117
  • 214
  • Are you getting an error? Or just not expected behavior? If you're getting an error, can you add it to the question? – Aaron Jensen Aug 28 '14 at 14:30
  • OP updated with the error. – DarkLite1 Sep 01 '14 at 07:42
  • Shameless promotion: check out the [Grant-Permission](http://get-carbon.org/help/Grant-Permission.html) function, part of the [Carbon module](http://get-carbon.org/). It will grant permissions on files, directories, registry keys, and (soon) certificates. – Aaron Jensen Sep 01 '14 at 15:28

2 Answers2

6

The Position value is only used to match unnamed parameters. Parameters passed via a pipeline are named, so are ignored when doing this calculation. In your example, the first unnamed parameter is Bob, so that gets assigned to parameter $Folder, because that parameter's position is 0. Best practice is to always use parameter names in your scripts. It makes them easier to understand. Use positional parameters when on the console to save yourself keystrokes.

From what I've seen in the PowerShell base cmdlets, parameters that take pipeline input usually aren't also positional. (Probably for just this reason.) Parameters that can be piped in must be passed by name when not passed via the pipeline. Remove Position=0 from the $Folder parameter.

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({Test-Path $_ -PathType Container})]
    [String[]]
    $Folder,
Community
  • 1
  • 1
Aaron Jensen
  • 25,861
  • 15
  • 82
  • 91
2

Hopefull you can use this to follow the logic used in parameter binding. Its an excerpt from Windows Powershell in Action By Bruce Payette.

  1. Bind all named parameters - Find all unquoted tokens on the command line that start with a dash. If the token ends with a colon, an argument is required. If there’s no colon, look at the type of the parameter and see if an argument is required. Convert the type of actual argument to the type required by the parameter, and bind the parameter.
  2. Bind all positional parameters -If there are any arguments on the command line that haven’t been used, look for unbound parameters that take positional parameters and try to bind them.
  3. Bind from the pipeline by value with exact match- If the command is not the first command in the pipeline and there are still unbound parameters that take pipeline input, try to bind to a parameter that matches the type exactly.
  4. If not bound, then bind from the pipe by value with conversion. - If the previous step failed, try to bind using a type conversion.
  5. If not bound, then bind from the pipeline by name with exact match - If the previous step failed, look for a property on the input object that matches the name of the parameter. If the types exactly match, bind the parameter.
  6. If not bound, then bind from the pipeline by name with conversion. If the input object has a property whose name matches the name of a parameter, and the type of the property is convertible to the type of the parameter, bind the parameter.

Following this I hope you can determine what is happening, and in what order, with your routine.

In the example 'C:\Folder' | Set-ACLspecial 'Bob' ReadAndExecute 'Bob' should be assigned to $Folder since positional is bound before pipeline.

oɔɯǝɹ
  • 7,219
  • 7
  • 58
  • 69
Matt
  • 45,022
  • 8
  • 78
  • 119
  • Yes Matt, that's exactly my problem. Do I need to use an extra `ParameterSetName` for piped content? So that it sees `Bob` as `SAMaccountName`. – DarkLite1 Sep 01 '14 at 07:44
  • You could just move the parameter for SamAccountName before the pipeline parameter. – Matt Sep 01 '14 at 11:34
  • Thank you Matt, how can I do this? `Position=0` always starts after the pipeline parameter. Is it something like `Position=-1`? – DarkLite1 Sep 01 '14 at 12:42