0

I have a powershell script to collect folders and permissions on one system and another script to create the folder structure and apply permissions on a different system in a highly restricted environment.

I can't use robocopy to the new system. I have to ftp, so I'm using a csv file to collect and apply the permissions. Folders only - the files are transferred over ftp after the folders and permissions are in place (H/T to people who helped me with earlier problem on the "apply permissions" script (Powershell problem with Set-ACL from imported csv)).

When I test the "apply permissions" script on my local laptop, the folders are created with the permissions as expected. When I run the script with the same csv file on the secure system, it creates the folder structure, but it applies every explicit permission in the spreadsheet to every listed folder. The only difference in the scripts is the root folder I set as the starting point. Both systems are Windows 10 and have the same version of powershell.

I have full NTFS permissions where I create the directories, but I don't have permissions to run scripts in the secure environment so I have to select all and use the "run selection" option. I did the same thing on my laptop to rule out any potentially different behavior from that.

Here's an example of the folder/acl spreadsheet:

FolderPath IdentityReference FileSystemRights InheritanceFlags
Activities AD\User-ACTIVITIES.T Read None
Activities AD\user.adm FullControl ContainerInherit, ObjectInherit
Activities\BWOVC1 AD\User-BWOVC1 Modify ContainerInherit, ObjectInherit

Here is the script:

# Location Where your folders are to be created
$RootDir = "C:\Users\User1\Documents\ACL"
Set-Location "$RootDir" 

# Import CSV file from location
$Folders = Import-Csv "$RootDir\ACL-File.csv"

# Create Folders from FolderPath column in csv; set ACL
ForEach ($Folder in $Folders) 
{ 
if(Test-Path $RootDir\$Folder.FolderPath) {continue} #{Write-Verbose "Folder: $Path Already Exists"}
else{
 
New-Item $Folder.FolderPath -type directory
}

        $IdentityReference = $Folder.IdentityReference
        $FileSystemRights = $Folder.FileSystemRights
        $InheritanceFlag = "ContainerInherit, ObjectInherit"
        $PropagationFlag = "None"
        $AccessControlType = "Allow"

#From stackoverflow response;
# Define the access rule(s) to add to the ACL
$New_ACR = New-Object Security.AccessControl.FileSystemAccessRule $IdentityReference, $FileSystemRights, $InheritanceFlag, $PropagationFlag, $AccessControlType
# Get Access Control List from directory
$ACL = Get-Acl -Path ($RootDir + "\" + $Folder.FolderPath)
# Add new rule to ACL
$ACL.AddAccessRule($New_ACR)
# Apply updated ACL to directory
Set-Acl -Path ($RootDir + "\" + $Folder.FolderPath) -AclObject $ACL

}  

Any idea why I'm getting different behavior on the different systems?

mklement0
  • 382,024
  • 64
  • 607
  • 775
tr_cpc1
  • 1
  • 2
  • 1
    `$FolderPath = $Folder.FolderPath` should be within the `ForEach ($Folder in $Folders)` loop. – iRon Mar 11 '23 at 17:02
  • The version so the cmdlets could be different. I don't see the "run selection" option. If you are using a network drive the root is the server name where network drive is mounted. So you root path would be "\\servername\shared folder\path\filename". the issue could be the shared folder permission. If you are using a mounted drive like "p:\" is is a mounted drive which is mounted to a different server. If you are an admin you do not have admin privilege on remote machine unless you replace the colon with a dollar sign "p$\". – jdweng Mar 11 '23 at 21:22
  • Thanks for the comments. The network drive may be the issue, so I'll try that suggestion. – tr_cpc1 Mar 13 '23 at 14:15
  • BTW, the "run selection" tool is in the ISE, not the script itself. The default is to run any single line where you have the cursor, but any selected block can be run as well. – tr_cpc1 Mar 13 '23 at 14:28
  • As an aside: A more robust way to invoke a script file when the execution policy prevents direct execution is to use `powershell -ExecutionPolicy Bypass -NoProfile -File script.ps1`. If execution is prevented by GPOs, however, the bypass won't be effective and you'll need to use `powershell -ExecutionPolicy Bypass -NoProfile -Command "Invoke-Expression (Get-Content -Raw script.ps1)"`, which, however, will only work if your script doesn't call _other_ scripts. Assuming your script does _not_ call `exit`, you can even execute it in-session with `Invoke-Expression (Get-Content -Raw script.ps1)`. – mklement0 Mar 13 '23 at 14:58
  • Another aside: The PowerShell ISE is [no longer actively developed](https://docs.microsoft.com/en-us/powershell/scripting/components/ise/introducing-the-windows-powershell-ise#support) and [there are reasons not to use it](https://stackoverflow.com/a/57134096/45375) (bottom section), notably not being able to run PowerShell (Core) 6+. The actively developed, cross-platform editor that offers the best PowerShell development experience is [Visual Studio Code](https://code.visualstudio.com/) with its [PowerShell extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell). – mklement0 Mar 13 '23 at 15:00

2 Answers2

1

This may not be your only problem, but Test-Path $RootDir\$Folder.FolderPath does not work as you expect:

  • As a command argument, $RootDir\$Folder.FolderPath is implicitly treated as if it were enclosed in "...", i.e. as an expandable (double-quoted) string ("...")

  • You can not use expressions - such as trying to access a variable's property inside "..." - as-is - the .FolderPath substring would be used verbatim rather than a property access. You need to enclose expressions in $(...), the subexpression operator:

    # "..." isn't strictly necessary here, but advisable for conceptual clarity
    Test-Path "$RootDir\$($Folder.FolderPath)"
    
  • Alternatively, use string concatenation, as also used later in your code:

    Test-Path ($RootDir + "\" + $Folder.FolderPath)
    
  • Or, more simply call Join-Path

    Test-Path (Join-Path $RootDir $Folder.FolderPath)
    
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • "As iRon points out, your $FolderPath = $Folder.FolderPath statement should be inside your foreach loop. However, you're not even using this variable inside the loop, so this is a moot point;" LOL. Yes, I noticed that after I saw iRon's comment. Thanks for your insights. I'm not exactly sure what to make of them, given that the script works as it is on my local computer, but I'll work through them after I try jdweng's suggestion for the network drive. – tr_cpc1 Mar 13 '23 at 14:16
  • @tr_cpc1, yes. I didn't realize the moot aspect until later, but since the statement is still in the question, I left it for now. It would be better to eliminate this distraction: please remove it from your question, then I'll remove the relevant part from my answer. However, `Test-Path $RootDir\$Folder.FolderPath` is unequivocally broken. – mklement0 Mar 13 '23 at 14:21
  • @tr_cpc1, if the intent is to run the script against the _local_ file-system, how do _network_ shares come into play? – mklement0 Mar 13 '23 at 14:35
  • @tr_cpc1, you're _hard-coding_ the inheritance flags in `$InheritanceFlag = "ContainerInherit, ObjectInherit"` - shouldn't that be `$InheritanceFlag = $Folder.InheritanceFlag`? – mklement0 Mar 13 '23 at 14:39
  • Argh! Deleted my last 2 comments just in case anyone saw them. Stupid mistake on my part. Parent folder had those rights, so I've recreated. Now my folders are inheriting the parent folder rights, so I have to figure out how to get "move" behavior so the rights remain in place across drives. – tr_cpc1 Mar 13 '23 at 15:48
  • mklement0, good question about network. All rights are domain rights, but I guess the rights won't carry across systems. Still trying different suggestions. I'll check out your inheritance flag suggestion. – tr_cpc1 Mar 13 '23 at 15:58
0

mklement takes the gold.

"you're hard-coding the inheritance flags in $InheritanceFlag = "ContainerInherit, ObjectInherit" - shouldn't that be $InheritanceFlag = $Folder.InheritanceFlag?"

Yup. Changing my hardcoded inheritance flags to use the ones listed in the spreadsheet fixed the problem. It turns out that was the only thing that needed to be tweaked. However, I do appreciate all of the responses, and I'll keep the suggestions in mind for other problems that may come up.

avariant
  • 2,234
  • 5
  • 25
  • 33
tr_cpc1
  • 1
  • 2