locked
Help explaining (expanding) shorthand commands from code sample RRS feed

  • Question

  • I have an awesome piece of code for enumerating permissions that I found on stack. I'd like to adapt it for my own purposes:

    $accessMask = [ordered]@{
      [uint32]'0x80000000' = 'GenericRead'
      [uint32]'0x40000000' = 'GenericWrite'
      [uint32]'0x20000000' = 'GenericExecute'
      [uint32]'0x10000000' = 'GenericAll'
      [uint32]'0x02000000' = 'MaximumAllowed'
      [uint32]'0x01000000' = 'AccessSystemSecurity'
      [uint32]'0x00100000' = 'Synchronize'
      [uint32]'0x00080000' = 'WriteOwner'
      [uint32]'0x00040000' = 'WriteDAC'
      [uint32]'0x00020000' = 'ReadControl'
      [uint32]'0x00010000' = 'Delete'
      [uint32]'0x00000100' = 'WriteAttributes'
      [uint32]'0x00000080' = 'ReadAttributes'
      [uint32]'0x00000040' = 'DeleteChild'
      [uint32]'0x00000020' = 'Execute/Traverse'
      [uint32]'0x00000010' = 'WriteExtendedAttributes'
      [uint32]'0x00000008' = 'ReadExtendedAttributes'
      [uint32]'0x00000004' = 'AppendData/AddSubdirectory'
      [uint32]'0x00000002' = 'WriteData/AddFile'
      [uint32]'0x00000001' = 'ReadData/ListDirectory'
    }
    
    $fileSystemRights = Get-Acl -LiteralPath 'C:\some\folder_or_file' |
                        select -Expand Access |
                        select -Expand FileSystemRights -First 1
    
    $permissions = $accessMask.Keys |
                   ? { $fileSystemRights.value__ -band $_ } |
                   % { $accessMask[$_] }

    I am not overly familiar with PowerShell and I am having trouble translating the shorthand bits at the end:

    $permissions = $accessMask.Keys |
                   ? { $fileSystemRights.value__ -band $_ } |
                   % { $accessMask[$_] }
    I believe:

    ? = 'where' conditional

    band = bitwise and operator

    $_ = last variable used (??)

    % = foreach

    Clearly the individual who scripted this had a better grasp of PowerShell than I do. I was hopeful that someone might be able to recreate this code like I was learning it for the first time. 

    Additionally there is a -First 1 that returns only a single result from get-acl. I need to enumerate all the permissions in a directory and I'm not sure that this sample does that... Wondering how/if it could be adapted.

    Thanks!

    Thursday, June 16, 2016 6:58 PM

Answers

  • Hi Shrout,

    you are right, generic permissions don't work. Microsoft didn't add them to the enumeration (and there is no other), probably because all the generic permissions map to another set of permissions that are part of the enumeration.

    This however forces me to bloat my otherwise elegant function to parse enumerations (*sigh*):

    function Split-Enumeration
    {
    	[CmdletBinding()]
    	Param (
    		$Enumeration
    	)
    	
    	$keys = [System.Enum]::GetValues($Enumeration.GetType())
    	$keys = $keys | Where-Object { $Enumeration -band $_ } | Select-Object -Unique
    	
    	#region Exceptions
    	
    	#region FileSystem Permissions
    	if ($Enumeration.GetType().FullName -eq "System.Security.AccessControl.FileSystemRights")
    	{
    		# Generic Execute
    		if ($Enumeration -band 536870912)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::ExecuteFile
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		# Generic Read
    		if ($Enumeration -band -2147483648)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadData
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		# Generic  Write
    		if ($Enumeration -band 1073741824)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::AppendData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		# Generic All
    		if ($Enumeration -band 268435456)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::ExecuteFile
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadData
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::AppendData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		$keys = $keys | Select-Object -Unique
    	}
    	#endregion FileSystem Permissions
    	
    	#endregion Exceptions
    	
    	$keys
    }
    
    $acl = Get-Acl "C:\Program Files\Microsoft Office 15\"
    $permissions = $acl.Access
    foreach ($perm in $permissions)
    {
    	$perm | Add-Member -Value (Split-Enumeration -Enumeration $perm.FileSystemRights) -Name AccessRightsList -MemberType NoteProperty
    }

    Now I've added a check for filesystemrights, which will convert the generic permissions into the actual permissions they grant (so you can handle them the same way as other permissions, since it appears to be the effective access that's your concern).

    Also, please don't remove the function output when adding debugging-write-hosts. Keep the output and you may want to use Write-Verbose or Write-Debug instead of Write-Host. That way you can simply add a switch when testing it and can keep it, in case you need the debugging capability later.

    Cheers,
    Fred


    There's no place like 127.0.0.1


    • Edited by FWN Wednesday, June 22, 2016 1:34 PM
    • Marked as answer by Shrout1 Thursday, June 23, 2016 8:11 PM
    Saturday, June 18, 2016 8:09 AM

All replies

  • Hi Shrout,

    you pretty much got most of it:

    • ? Is an Alias of Where-Object (Get-Alias "?" will show you)
    • % Is an Alias of ForEach-Object (Get-Alias "%" will show you)
    • -band is indeed a bitwise AND operator
    • In a scriptblock, such as is passed to Where-Object or ForEach-Object, $_ refers to the item that is currently being processed. If you pass three objects to Where-Object, it will for each of these 3 objects test, whether the scriptblock results in $true or $false and only pass those along where it is $true. During each of these three tests, $_ refers to the object currently being tested.

    You can drop the -First 1 parameter, however you will have to make sure to iterate over each result. This snippet so far assumes you only have a single result.

    What this script does is ...

    • First create a hashtable that maps the numeric value of each permission setting to its string representation. This means, that when you give it the numeric value of a single file access permission, it will return its text.
    • Then it retrieves a single permission as configured on a file or folder (the first entry)
    • Then comes the final part, which is a bit tricky:
      - First it takes all numeric value of each possible permission, by retrieving the keys from the hashtable
      - Then it filters out all that are not in the permission on the file
      - Then for each number that passed through the filter, it retrieves the text representation of the permission

    While it looks fairly elegant, its downside lies in the list you need to define. It fails to detect any unknown values (A new OS revision might add new possible permissions ...). Moreover - it ignores them, so you won't get notified.

    Also, this is a static example that will only work on FileSystemSecurity. You will need to research a new list if you want to apply it to any other kind of enumeration.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    • Edited by FWN Thursday, June 16, 2016 7:22 PM
    Thursday, June 16, 2016 7:12 PM
  • I got a bit into the mood, so here is a "simpler" version:

    function Split-Enumeration
    {
    	[CmdletBinding()]
    	Param (
    		$Enumeration
    	)
    	
    	$keys = [System.Enum]::GetValues($Enumeration.GetType())
    	$keys | Where-Object { $Enumeration -band $_ } | Select-Object -Unique
    }
    
    $acl = Get-Acl "C:\folder\file"
    $permissions = $acl.Access
    foreach ($perm in $permissions)
    {
        Split-Enumeration -Enumeration $perm.FileSystemRights
    }

    You can use the function above for any kind of enumeration, it will always be up-to-date and work :)

    Cheers,
    Fred


    There's no place like 127.0.0.1

    • Proposed as answer by _P_M_ Friday, June 17, 2016 2:43 PM
    Thursday, June 16, 2016 7:31 PM
  • So if I took this:

    $permissions = $accessMask.Keys |
                   ? { $fileSystemRights.value__ -band $_ } |
                   % { $accessMask[$_] }

    And recoded it, it would look like:

    $permissions = $accessMask.Keys | where { $filesystemRights.value__ -band $accessMask } |
           foreach {$permission in $accessMask}

    I'm lost here... :D Forgive my lack of knowledge

    Also I read that the $variable.value__ with double underscores allows easy access to the "enum" value? I'm confused about that as well.
    • Edited by Shrout1 Thursday, June 16, 2016 8:09 PM
    Thursday, June 16, 2016 8:08 PM
  • Awesome! Thanks a lot - I'm going to play with it and see if I can integrate it into my code :)
    Thursday, June 16, 2016 8:24 PM
  • Ok one more question as I'm still figuring this out... Let's say I want to pair the "IdentityReference" field from the "$permissions" object (array?) with the particular permission. I'm trying to scan for invalid user permissions :)
    Thursday, June 16, 2016 8:29 PM
  • So if I took this:

    $permissions = $accessMask.Keys |
                   ? { $fileSystemRights.value__ -band $_ } |
                   % { $accessMask[$_] }

    And recoded it, it would look like:

    $permissions = $accessMask.Keys | where { $filesystemRights.value__ -band $accessMask } |
           foreach {$permission in $accessMask}

    I'm lost here... :D Forgive my lack of knowledge

    Also I read that the $variable.value__ with double underscores allows easy access to the "enum" value? I'm confused about that as well.

    Indeed, that is the same code. An Alias is another name for the same command. A shorthand so you don't have to type the full thing all the time.

    To add the split list of permissions to the other properties already present, you can do this:

    foreach ($perm in $permissions)
    {
    	$perm | Add-Member -Value (Split-Enumeration -Enumeration $perm.FileSystemRights) -Name AccessRightsList -MemberType NoteProperty
    }
    $permissions

    This will add the correct list as a new property to each object in $permissions. However, how do you determine, whether a permission is wrong? It might just be, that this splitting of individual permissions isn't what you really need ...

    Also make sure to differentiate between allow and deny acess rules.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, June 16, 2016 8:53 PM
  • So I'm actually scanning current user permissions to be certain that the baseline config on the system I'm building doesn't have any incorrect configurations in a list of directories I specify. :)

    And thanks so much for your help!

    Friday, June 17, 2016 12:44 PM
  • Fred,

    Thanks for your help! I'm running into an issue with generic permissions still:

    function Split-Enumeration
    {
    	[CmdletBinding()]
    	Param (
    		$Enumeration
    	)
    	
    	$keys = [System.Enum]::GetValues($Enumeration.GetType())
    	$thang = $keys | Where-Object { $Enumeration -band $_ } | Select-Object -Unique
        Write-Host "Permission / Enumeration Value: " $Enumeration
        Write-Host "Key Comparison: " $thang
    }
    
    $acl = Get-Acl "C:\Program Files\Microsoft Office 15\"
    $permissions = $acl.Access
    foreach ($perm in $permissions)
    {
    
    	$perm | Add-Member -Value (Split-Enumeration -Enumeration $perm.FileSystemRights) -Name AccessRightsList -MemberType NoteProperty
    
    }
    
    Seems like generic permissions, say where $Enumeration=268435456 don't find a match. Is there another type that should be enumerated in order to get matching generic permissions?


    Friday, June 17, 2016 7:05 PM
  • Hi Shrout,

    you are right, generic permissions don't work. Microsoft didn't add them to the enumeration (and there is no other), probably because all the generic permissions map to another set of permissions that are part of the enumeration.

    This however forces me to bloat my otherwise elegant function to parse enumerations (*sigh*):

    function Split-Enumeration
    {
    	[CmdletBinding()]
    	Param (
    		$Enumeration
    	)
    	
    	$keys = [System.Enum]::GetValues($Enumeration.GetType())
    	$keys = $keys | Where-Object { $Enumeration -band $_ } | Select-Object -Unique
    	
    	#region Exceptions
    	
    	#region FileSystem Permissions
    	if ($Enumeration.GetType().FullName -eq "System.Security.AccessControl.FileSystemRights")
    	{
    		# Generic Execute
    		if ($Enumeration -band 536870912)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::ExecuteFile
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		# Generic Read
    		if ($Enumeration -band -2147483648)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadData
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		# Generic  Write
    		if ($Enumeration -band 1073741824)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::AppendData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		# Generic All
    		if ($Enumeration -band 268435456)
    		{
    			$keys += [System.Security.AccessControl.FileSystemRights]::ExecuteFile
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadData
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::AppendData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteData
    			$keys += [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes
    			$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    			$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    		}
    		
    		$keys = $keys | Select-Object -Unique
    	}
    	#endregion FileSystem Permissions
    	
    	#endregion Exceptions
    	
    	$keys
    }
    
    $acl = Get-Acl "C:\Program Files\Microsoft Office 15\"
    $permissions = $acl.Access
    foreach ($perm in $permissions)
    {
    	$perm | Add-Member -Value (Split-Enumeration -Enumeration $perm.FileSystemRights) -Name AccessRightsList -MemberType NoteProperty
    }

    Now I've added a check for filesystemrights, which will convert the generic permissions into the actual permissions they grant (so you can handle them the same way as other permissions, since it appears to be the effective access that's your concern).

    Also, please don't remove the function output when adding debugging-write-hosts. Keep the output and you may want to use Write-Verbose or Write-Debug instead of Write-Host. That way you can simply add a switch when testing it and can keep it, in case you need the debugging capability later.

    Cheers,
    Fred


    There's no place like 127.0.0.1


    • Edited by FWN Wednesday, June 22, 2016 1:34 PM
    • Marked as answer by Shrout1 Thursday, June 23, 2016 8:11 PM
    Saturday, June 18, 2016 8:09 AM
  • Fred,

    Thanks so much for your help! You have a formidable knowledge of PowerShell and I really appreciate the time you've spent assisting me. I will play with this today!

    Monday, June 20, 2016 12:28 PM
  • Fred,

    Another question -

    It appears that generic permissions are simply bit masks applied to a binary version of a 32bit unsigned integer (unsigned?). The mask is used to check the value of particular bits in certain locations of the 32 bit binary word, thereby determining what permission is granted to that particular user.

    I'm confused because the binary values of the generic permission values come back with 29, 28, 27 and 26 bits. Does this need to be padded?

    10000000000000000000000000000 – “Generic Read” – 268435456 - 29 bits

    1000000000000000000000000000 – “Generic Write” – 134217728 - 28 bits

    100000000000000000000000000 – “Generic Execute” – 67108864 - 27 bits

    10000000000000000000000000 – “Generic All” – 33554432 - 26 bits

    Also, take a look at this diagram from technet:

    Why are these integers less than 32 bits? Should I pad them on the most or least significant bits (left or right?)

    Also take a look at this stackexchange post - it states that the generic permissions are as follows:

    268435456 - FullControl

    -536805376 - Modify, Synchronize

    -1610612736 - ReadAndExecute, Synchronize

    Since I can't figure out how to apply the access mask I'm not sure how to verify the access rights.


    This is my first exploration of this topic, so forgive my ignorance. Thanks again!

    • Edited by Shrout1 Wednesday, June 22, 2016 1:49 PM
    Wednesday, June 22, 2016 1:03 PM
  • Hi Shrout,

    it appears I slipped by a few digits, sorry. You are of course right, that GR should be on the highest bit and then count down from there. I'll fix it in the function above.

    Cheers and thanks for pointing this out,
    Fred


    There's no place like 127.0.0.1

    Wednesday, June 22, 2016 1:33 PM
  • Fred,

    Hate to keep bugging you! I'm trying to figure out how to interpret the varying bit sizes of the generic permissions. If a permission has only 29 or 28 bits in it, should it be padded to equal 32 bits? If so, what side of the permission should be padded? I can't get the alignment of the permission to the access mask down...

    I'm trying to locate some additional resources to understand exactly how the OS uses generic permissions, but I'm having difficulty. The "access rights and access masks" section of this post is somewhat helpful, but I still can't figure out proper alignment of the binary word in relation to the access mask.

    This appears to be the patent with the U.S. Patent office that outlines the methods used in this type of security architecture.
    • Edited by Shrout1 Thursday, June 23, 2016 1:45 PM
    Thursday, June 23, 2016 1:41 PM
  • Hi Shrout,

    well, first of all, you pad it on the high side with zero, as otherwise you'd inflate the numeric value and shift the set flags to the left. The model Microsoft patented is used for various permission scenarios, not only file system. Which is why it has some generic permissions, so you can sometimes use abstract tools for a task, instead of having to use separate tools for each individual type of secured object.

    A few examples of objects, the access of which Windows protects using this system:

    • Files
    • Registry Keys
    • Processes
    • Named Pipes

    If you only look at it from the filesystem perspective, you can't really help scratching your head and wondering why :)

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, June 23, 2016 2:01 PM
  • Ok! Your values look spot on!

    Unpadded "268435456" "Generic All" 29 bits - 10000000000000000000000000000
    Unpadded "536870912" "Generic Execute" 30 bits - 100000000000000000000000000000
    Unpadded "1073741824" "Generic Write" 31 bits - 1000000000000000000000000000000


    Padded "268435456" "Generic All" 32 bits - 00010000000000000000000000000000
    Padded "536870912" "Generic Execute" 32 bits - 00100000000000000000000000000000
    Padded "1073741824" "Generic Write" 32 bits - 01000000000000000000000000000000

    And check me on this one (the online tool is having some trouble so I'm just doing the complements myself)

    Unpadded "2147483648" "Generic Read" 32 Bits - 10000000000000000000000000000000
    1's complement of "2147483648" = 01111111111111111111111111111111
    SIGNED 2s complement of "2147483648" = 10000000000000000000000000000000 = "-2147483648"

    Also, if I lived *anywhere* near you I would owe you about two good drinks by now. Thanks again!

    Thursday, June 23, 2016 6:25 PM
  • Hey Shrout,

    what online tool are you talking about? I thought you wanted to PowerShell ...

    # Convert binary string to int
    $bit32 = ("1" + "0" * 30).PadLeft(32, "0")
    [Convert]::ToInt32($bit32, 2)
    
    # Convert int to binary string
    $int = -2147483648
    [Convert]::ToString($int, 2)

    Note that the PadLeft was not necessary, but showed how to fill up missing digits on the left side.

    As for the drinks ... well, I live in southern Germany (near Stuttgart), so feel to come over for a visit - I sure don't mind free drinks and the beer here is awesome!

    On the other hand, you may need to drink a few drinks yourself if you come over, seeing how I happen to be a barkeeper ^^

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, June 23, 2016 7:28 PM
  • Oh lol I was just using some silly online converter to do a quick check on the binary :)

    If I'm ever in the neighborhood I'll certainly let you know! I love a good beer and I know Germany has a quite a few great ones - nothing better than swapping drunken scripting stories with the bartender!

    My job does have the occasional project in Germany, but I haven't had the luck of being selected just yet...

    Thanks again!!

    Thursday, June 23, 2016 8:10 PM
  • Hi Shrout,

    glad to have been of assistance :)
    I'm hanging out here and help people for quite a few reasons, not the least of which being that while teaching, you are also being taught. Whether it's just framing a thought in way that allows you to convey it to others, or that while doing just that you gain new insights. Then there's the people you meet, albeit only digitally. All things considered, I consider myself well rewarded from this thread :)

    That said, some shameless advertisement incoming: It's quite possible to rent me for the usual range of consulting tasks. Whether it's powershell tutoring, tailored-to-purpose pieces of script, recommendations on corporate script management or hosting a cocktail-bar for a company event ^^

    Oh lol I was just using some silly online converter to do a quick check on the binary :)

    Don't worry, I was just twisting your tail a little for using an online tool when PowerShell can do all those nifty things for you itself :). I've found myself using PowerShell to support me in most of my mundane every-day tasks. Always got at least two consoles open at any given time, just in case. You never know when you need your next header or convert a hundred sql insert statements into a single insert statement, ...

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, June 23, 2016 9:24 PM
  • Haha well thanks again :) If I ever have need to a powershell consultant or developer I will come find you! And if I'm ever near Stuttgart I will definitely find your bar!
    Friday, June 24, 2016 2:11 PM
  • Fred, you thought you were done with me, but it should appear that I'm back... If you still have a moment to spare I could use some additional help. 

    It seems that when I get to a certain point in the execution of the script that you've crafted I run into a problem... My "$keys" permissions object changes from an object array (I think that's what this is...) to an integer - or perhaps it's a character array. Here is more specific information on my quandry:

    # Generic All
    if ($Enumeration -band 268435456)
    {
    	$keys += [System.Security.AccessControl.FileSystemRights]::ExecuteFile
    	$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    	$keys += [System.Security.AccessControl.FileSystemRights]::ReadData
    	$keys += [System.Security.AccessControl.FileSystemRights]::ReadAttributes
    	$keys += [System.Security.AccessControl.FileSystemRights]::ReadExtendedAttributes
    	$keys += [System.Security.AccessControl.FileSystemRights]::AppendData
    	$keys += [System.Security.AccessControl.FileSystemRights]::WriteAttributes #<------ Here is the problem
    	$keys += [System.Security.AccessControl.FileSystemRights]::WriteData
    	$keys += [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes
    	$keys += [System.Security.AccessControl.FileSystemRights]::Synchronize
    	$keys += [System.Security.AccessControl.FileSystemRights]::ReadPermissions
    }



    $keys changes from "ReadData, AppendData, ReadExtendedAttributes, ExecuteFile, WriteAttributes" to "557" when the "WriteAttributes" string/object is appended to it. The value continues to change, with "WriteData" changing it to "559", "WriteExtendedAttributes" changing it to "575", "Synchronize" changing it to "1049151" and finally "ReadPermissions" changing it to "1180223"

    If the problem line is commented out then the "$keys" object seems to remain as an array. Can't say I know enough to determine the root cause and my research efforts have been pretty fruitless.

    Can't thank you enough - do these forums have a PM function by chance?
    Monday, June 27, 2016 12:58 PM
  • Hi Shrout,

    can't reproduce it on my end, but this may well be a difference in PowerShell version. Very well, let's see what this yields you:

    # Old Lines
    $keys = [System.Enum]::GetValues($Enumeration.GetType())
    $keys = $keys | Where-Object { $Enumeration -band $_ } | Select-Object -Unique
    
    # New Lines
    $keys = @()
    [System.Enum]::GetValues($Enumeration.GetType()) | Where-Object { $Enumeration -band $_ } | Select-Object -Unique | ForEach-Object { $keys += $_ }
    

    Some Versions can have trouble with switching between arrays and individual entries.

    Does this work for you?

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Monday, June 27, 2016 1:07 PM
  • do these forums have a PM function by chance?
    Nope, we don't have any messaging functions here. We've been asking for years, but no joy.

    Monday, June 27, 2016 1:18 PM
  • Yeah that did the trick! I need to look at this black magic you've performed so I can understand it :D lol. Thanks again!
    Monday, June 27, 2016 2:42 PM
  • That gets the job done! Thanks again! Perhaps I can finally leave you in peace...
    Monday, June 27, 2016 2:47 PM
  • Ah well. I'm sure it would be riddled with spam, so that is probably why they've never implemented it. Too bad though!
    Monday, June 27, 2016 2:48 PM
  • Yeah that did the trick! I need to look at this black magic you've performed so I can understand it :D lol. Thanks again!<input id="spamFlag" type="hidden" />

    In some (but not all) versions of PowerShell, it will treat the result of a pipeline as an array no matter what. In others it treats the result not as an array when the result of a pipeline is a single object.

    This changes the addition operation on the object down the road and can thus cause issues.

    What the new lines do is force it to always be an array, even when only a single value is present. I do this by ...

    1. Explicitely declaring an array
    2. All results of the pipeline (one more many) are then added to the array

    Thus no matter what value is passed, $keys will always be an array and behave predictably.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Monday, June 27, 2016 3:21 PM