Author Posts

May 25, 2018 at 7:12 pm

I am working on a script to create A.D. groups and assign permissions to network shares. Group creation works just fine, and most of the time setting the ACLs works. But every once in a while it will set the ACL on the final directory but not on one of the parents. The methodology would be something like this:

function _setNTFS {
    Param (
        [String]$sDir,
        [String]$sGroup,
        [String]$sPerm,
        [Bool]$inherit
    )

    $ACL = Get-Acl -Path $sDir

    if ($inherit -eq $true) {
        $newACL = $sGroup, $sPerm, "ContainerInherit,ObjectInherit", "None", "Allow"
    } else {
        $newACL = $sGroup, $sPerm, "Allow"
    }

    $newRule = New-Object System.Security.AccessControl.FileSystemAccessRule $newACL
    $ACL.AddAccessRule($newRule)
    try {
        $ACL | Set-Acl $sDir -ErrorAction Stop
        "   Successfully Added $($sGroup) to directory $($sDir) with $($sPerm) permissions" | Out-File $sLogFile -Append
    } catch {
        "   $($Error[0].ToString()) + $($Error[0].InvocationInfo.PositionMessage)" | Out-File $sLogFile -Append
        "   RECOVERABLE ERROR = Failed to add $($sGroup) to directory $($sDir) with error above, please add manually" | Out-File $sLogFile -Append
    }
}

I have a $Path variable which is the full path (e.g. P:\ACC\Collections\EDI\820\Variance Reports\UAT10).

I create an $aPath variable which is $Path split on "\".

There is already a group that sets Read and Execute at root folders of P (ACC), so I would begin applying permissions at Collections. I have a For loop that begins at $aPath[2] (Collections) and loops through all folders except the last, setting Read and Execute:

for ($a = 2; $a -lt ($aPath.Count -1); $a++) {
 $sStartDir += "\$($aPath[$a])"
 "   Attempting to set Permissions at $($sStartDir)" | Out-File $sLogFile -Append
 _setNTFS -sDir $sStartDir -sGroup $groupName -sPerm "ReadAndExecute" -inherit $false
}

I then have a single call to set either RO or Modify on the final directory (UAT10):

if ($bPerm -eq "M") {
   _setNTFS -sDir $inpPath.Text -sGroup $groupName -sPerm "Modify" -inherit $true
} else {
   _setNTFS -sDir $inpPath.Text -sGroup $groupName -sPerm "ReadAndExecute" -inherit $true
}

As I said, 90% of the time this works. However, the times it does not, my try/catch statement in the function will not capture the error:

try {
   $ACL | Set-Acl $sDir -ErrorAction Stop
   "   Successfully Added $($sGroup) to directory $($sDir) with $($sPerm) permissions" | Out-File $sLogFile -Append
} catch {
   "   $($Error[0].ToString()) + $($Error[0].InvocationInfo.PositionMessage)" | Out-File $sLogFile -Append
   "   RECOVERABLE ERROR = Failed to add $($sGroup) to directory $($sDir) with error above, please add manually" | Out-File $sLogFile -Append
}

Succeed or fail, it outputs that it successfully created. I am not sure if I should do something more specific on my catch block, or if there is something else I am missing. For some reason, if it is going to fail, it will do so on the first folder it tries (Collections). It always works on the final folder, and always works on the upstream folders up to that first one. Hoping I am missing something stupid simple.

May 25, 2018 at 8:14 pm

Are you running this interactively, or is this a script that runs without anyone watching the output? Also, how soon after the group is created is this run? If you're running this right after creating a new group, it's possible that the name to SID translation isn't working.

First, you've got to figure out if that's the problem. Try wrapping the call to .AddAccessRule() in a try/catch block (that's where this specific error would manifest itself). You could also just move that call down into the existing try/catch block.

If that ends up being the problem, you could create the ACE with the SID of the group. Basically, when the group is created, grab the resulting object (New-AdGroup has a -PassThru parameter), and use its SID to create the new ACE (don't forget to cast it to a [SecurityIdentifier] so New-Object knows it's a SID).

May 25, 2018 at 10:50 pm

Thanks, Rohn, I was thinking about it and thinking along the same lines. I already had to slow down a bit as I was creating the group and then adding it to another group – which failed sometimes if I tried it too quickly. I will play around with it this weekend.

May 26, 2018 at 4:30 am

As a follow on to Rohn, by any chance are you running the group creation script on one machine and then applying the permissions on another? You could be running into problems as AD replication hasn't occurred and the two machines are hitting different DCs. When it does work they are hitting the same DC and when it fails they aren't.

You could use something like $env:LOGONSERVER to capture the authenticating DC on both servers and cross check, or possibly do a loop to wait until the server with the folder structure sees the AD group before continuing?

May 29, 2018 at 12:13 pm

Thanks, script is running from the same server, and using the LogonServer variable already.

May 29, 2018 at 2:25 pm

If you're using the ActiveDirectory cmdlets, you should be able to use the same server to create, then modify the groups by using the -Server parameter. You might call 'Get-AdDomainController' if you want to always get a valid DC instead of hard coding one.

As far as modifying ACLs goes, I'm not sure if you get to pick the DC you use there. You could try to use the current $env:LOGONSERVER and/or insert some time delays, but I've always found using SIDs to be easier (and faster since you don't have to wait for replication). Behind the scenes, the SID is what's stored in the ACE, and that's actually the part I think is failing here (the group name being translated to the SID).

So, if you're using New-AdGroup, I'd just add the -PassThru parameter to it, then use the SID when creating the ACE. Something like this:

$Group = New-AdGroup @OtherParameters -PassThru

# You might wrap this in a try block in case $Group.SID is invalid
$GroupSID = [System.Security.Principal.SecurityIdentifier] $Group.SID

$sPerm = 'Modify'
$InheritanceFlags = 'ContainerInherit, ObjectInherit'
$PropagationFlags = 'None'
$newRule = New-Object System.Security.AccessControl.FileSystemAccessRule (
    $GroupSID,
    $sPerm,
    $InheritanceFlags,
    $PropagationFlags,
    'Allow'
)