Author Posts

September 25, 2013 at 8:18 am

I have a script to create folders in a trusted domain, the script will create the folders properly and get-acl information for the folders however i am unable to change the ace/alc

When i get-acl it returns the sids instead of the friendly names for the groups.

I am running the script from Forest A attempting to get/set-acl on resources in Forest B

What is have so far is

###########################################

#region Apply Access Controls to $ftpFolder
$aclFtpRoot = {Get-Acl -Path $($ftpFolder)}

if
($aclFtpRoot.AreAccessRulesProtected) { $aclFtpRoot.Access | % {$aclFtpRoot.purgeaccessrules($_.IdentityReference)} }
else
{

$isProtected = $true
$preserveInheritance = $false
$aclFtpRoot.SetAccessRuleProtection($isProtected, $preserveInheritance)}

$rule1 = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators","FullControl","ContainerInherit,ObjectInherit","None","Allow")
$aclFtpRoot.AddAccessRule($rule1)
Set-Acl -aclobject $aclFtpRoot -Path $ftpFolder

$rule2 = New-Object System.Security.AccessControl.FileSystemAccessRule($filerDRwGroup,"FullControl","ContainerInherit,ObjectInherit","None","Allow")
$aclFtpRoot.AddAccessRule($rule2)
Set-Acl -aclobject $aclFtpRoot -Path $ftpFolder

$rule3 = New-Object System.Security.AccessControl.FileSystemAccessRule($divisionListGroup,"ListDirectory","None","None","Allow")
$aclFtpRoot.AddAccessRule($rule3)
Set-Acl -aclobject $aclFtpRoot -Path

$ftpFolder

#endregion

###########################################

If i perform

$ftpfolder = '\\Filer\test.com'
$aclFtpRoot = Get-Acl -Path $ftpFolder
$aclFtpRoot | Select-Object -ExpandProperty access

It returns

FileSystemRights : FullControl
AccessControlType: Allow
IdentityReference:BUILTIN\Administrators
IsInherited:False
InheritanceFlags: ContainerInherit,ObjectInherit
PropagationFlags:None

FileSystemRights: ReadAndExecute, Synchronize
AccessControlType:Allow
IdentityReference:S-1-5-11-1111111111-1111111111-1111111111-1111
IsInherited:False
InheritanceFlags:ContainerInherit
PropagationFlags: None

If i would like to add the rule i get error

$aclFtpRoot.AddAccessRule($rule3)

Exception calling "AddAccessRule" with "1" argument(s): "Some or all identity references could not be translated."

At line:1 char:1

+ $aclFtpRoot.AddAccessRule($rule3)

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException

+ FullyQualifiedErrorId : IdentityNotMappedException

Thank you

September 25, 2013 at 8:40 am

What are the contents of your $divisionListGroup variable? Make sure that it's a DOMAIN\GroupName format string (or [System.Security.Principal.NTAccount] object), and that the group actually exists; no typos in the name, etc.

If, for some reason, the .NET IdentityReference classes aren't translating names to SIDs across these forest trusts properly, it's always possible to code around them (either using the underlying Win32 API functions to translate names to SIDs, or fetching the SIDs with ADSI, etc.) However you accomplish it, all you should need to do is pass a [System.Security.Principal.SecurityIdentifier] object to the FileSystemAccessRule constructor in place of your current String or [System.Security.Principal.NTAccount] object.

September 25, 2013 at 10:32 am

The variable is IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False ADGroup Microsoft.ActiveDirectory.Management.ADPrincipal
It is the same group that appears with only the SID. I added the group to the resource (manually) and gave it read access to test this portion of the script.
It is aggravating because i am able to open explorer with my administrator creds then perform this one small portion of the script is stopping the entire thing.

September 25, 2013 at 10:37 am

Oh. There's no constructor for FileSystemAccessRule that would accept an ADPrincipal object; it either needs a subclass of IdentityReference (NTAccount or SecurityIdentifier), or a string (see http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemaccessrule.filesystemaccessrule.aspx). PowerShell is probably casting your ADPrincipal object to a string, to make it fit one of these constructors, but that string value might not quite match what the FileSystemAccessRule expects.

Try running ([string]$divisionListGroup) and ($divisionListGroup.ToString()), and see what the output is.

September 25, 2013 at 11:16 am

Instead of that i removed the variable and input domain\groupname to try to simplify it.
I attempted
New-Object System.Security.AccessControl.FileSystemAccessRule("Domain\Groupname","ListDirectory","None","None","Allow")
$aclFtpRoot.AddAccessRule($rule3)
Exception calling "AddAccessRule" with "1" argument(s): "The trust relationship between the primary domain and the trusted domain failed.
"
At line:1 char:1
+ $aclFtpRoot.AddAccessRule($rule3)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SystemException
When i show getType
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True String System.Object

September 25, 2013 at 11:38 am

Presumably that means you either really have a problem with your trust relationship (which I doubt, or you'd have known about it already), or we're back to my original statement, that you might need to work around a .NET Framework limitation here, if it's not behaving well.

I'd test this myself, but I don't have a multi-forest test environment set up at the moment. If I have time later, I'll build a new domain controller VM and see if I get the same results.

September 25, 2013 at 12:30 pm

THank you, just Fyi
for giggles i ran the following command from my elevated powershell console in forest A
PS C:\Sysutil\bin> $rule3 = New-Object System.Security.AccessControl.FileSystemAccessRule("domain\groupname","ListDirectory","None","None","Allow")

PS C:\Sysutil\bin> $aclFtpRoot.AddAccessRule($rule3)
Exception calling "AddAccessRule" with "1" argument(s): "The trust relationship between the primary domain and the trusted domain failed.
"
At line:1 char:1
+ $aclFtpRoot.AddAccessRule($rule3)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SystemException

Then i ran the exact same command from a server in forset B
PS C:\sysUtil\bin> $rule3 = New-Object System.Security.AccessControl.FileSystemAccessRule("domain\groupname","ListDirectory","None","None","Allow")

__________________________________________________________________________________________________
PS C:\sysUtil\bin> $aclFtpRoot.AddAccessRule($rule3)

__________________________________________________________________________________________________
PS C:\sysUtil\bin> Set-Acl -aclobject $aclFtpRoot -Path $ftpFolder1
It added the rule and set perms as it should have.
The only thing that sticks out for me is in forest A the get-acl returns SID of list group
When run get-acl in forest B it returns friendly name domain\groupname

Thank you very much for any help.

September 25, 2013 at 6:03 pm

I'm not sure how soon I'll have a second AD forest set up for my own testing, but in the meantime, try this as an experiment:

$account = [System.Security.Principal.NTAccount]'DOMAIN\GroupName'
$account.Translate([System.Security.Principal.SecurityIdentifier])

I expect this to fail; it's basically doing exactly the same thing that AddAccessRule is doing behind the scenes.

Then try this, and see if it succeeds (or if it fails with a similar error message):

# Based on sample code at http://www.pinvoke.net/default.aspx/advapi32.LookupAccountName

function Get-Sid
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [System.String]
        $Account,

        [Parameter(Mandatory = $false, Position = 1)]
        [System.String]
        $Domain = $null
    )

    Add-Type -TypeDefinition @'
        using System;
        using System.Runtime.InteropServices;
        using System.Text;

        public enum SID_NAME_USE 
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }
    
        public class NativeMethods
        {
            [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError = true)]
            public static extern bool LookupAccountName (
                string lpSystemName,
                string lpAccountName,
                [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
                ref uint cbSid,
                StringBuilder ReferencedDomainName,
                ref uint cchReferencedDomainName,
                out SID_NAME_USE peUse);
        }
'@

    $NO_ERROR = 0
    $ERROR_INSUFFICIENT_BUFFER = 122
    $ERROR_INVALID_FLAGS = 1004

    $sidBytes = $null
    $sidByteCount = 0
    $referencedDomainName = New-Object System.Text.StringBuilder
    $referencedDomainNameCharCount = [System.UInt32]$referencedDomainName.Capacity
    [SID_NAME_USE]$sidNameUse = [SID_NAME_USE]::SidTypeUnknown

    $errorCode = $NO_ERROR

    if (-not [NativeMethods]::LookupAccountName($Domain, $Account, $sidBytes, [ref]$sidByteCount, $referencedDomainName, [ref] $referencedDomainNameCharCount, [ref] $sidNameUse))
    {
        $errorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
        if ($errorCode -eq $ERROR_INSUFFICIENT_BUFFER -or $errorCode -eq $ERROR_INVALID_FLAGS)
        {
            $sidBytes = New-Object Byte[]($sidByteCount)
            $null = $referencedDomainName.EnsureCapacity([int]$referencedDomainNameCharCount)
            $errorCode = $NO_ERROR

            if (-not [NativeMethods]::LookupAccountName($Domain, $Account, $sidBytes, [ref]$sidByteCount, $referencedDomainName, [ref] $referencedDomainNameCharCount, [ref] $sidNameUse))
            {
                $errorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
            }
        }
    }
    else
    {
        $displayAccount = ""
        
        if (-not [string]::IsNullOrEmpty($Domain))
        {
            $displayAccount += "$Domain\"
        }

        $displayAccount += $Account

        throw "Account '$displayAccount' could not be translated to a SID."
    }

    if ($errorCode -eq $NO_ERROR)
    {
        $sid = New-Object System.Security.Principal.SecurityIdentifier($sidBytes,0)
        Write-Output $sid
    }
    else
    {
        throw (New-Object System.ComponentModel.Win32Exception($errorCode))
    }
}

Get-Sid -Domain 'DOMAIN' -Account 'GroupName'

This code uses the Win32 API; if anything's going to work, it should work. If this does work, you can use it to get around the .NET problem. The SecurityIdentifier object returned by Get-Sid (unless it throws an exception) can be passed to the constructor of FileSystemAccessRule objects.

September 26, 2013 at 4:48 am

You were right,
PS C:\Sysutil\bin> $account = [System.Security.Principal.NTAccount]'Domain\Groupname'

PS C:\Sysutil\bin> $account.Translate([System.Security.Principal.SecurityIdentifier])
Exception calling "Translate" with "1" argument(s): "The trust relationship between the primary domain and the trusted domain failed.
"
At line:1 char:1
+ $account.Translate([System.Security.Principal.SecurityIdentifier])
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SystemException

I then added the function and ran Get-sid which returned:
BinaryLength AccountDomainSid Value
———— —————- —–
28 S-1-1-11-1111111111-1111111111-1111111111 S-1-1-11-1111111111-1111111111-1111111111-1111

October 7, 2013 at 5:57 am

Good morning Just wanted to follow up with this thread to see if there is any more info that may help.
Thank you very much for you time.

October 7, 2013 at 7:31 am

Sorry, I thought you said that the Get-Sid function I posted was working. I did build a second AD forest in my lab, but wasn't able to reproduce the problem.

The intended use of the Get-Sid function is like this:

# Instead of this:

$rule3 = New-Object System.Security.AccessControl.FileSystemAccessRule("Domain\Groupname","ListDirectory","None","None","Allow")
$aclFtpRoot.AddAccessRule($rule3)

# Try this:

try
{
    $sid = Get-Sid -Domain "Domain" -Account "Groupname"
    $rule3 = New-Object System.Security.AccessControl.FileSystemAccessRule($sid, "ListDirectory", "None", "None", "Allow")
    $aclFtpRoot.AddAccessRule($rule3)
}
catch
{
    # If an error occurred, handle it however you like.
    
    Write-Error -ErrorRecord $_
}

November 4, 2013 at 10:09 am

I am sorry i did not respond sooner, Thank you for your help.
This was the correct method and i can set the rules as expected.
I really appreciate all the time and effort you gave on this issue.