首页 > 解决方案 > PowerShell错误,主域和可信域之间的信任关系失败

问题描述

我制作了一个脚本,必须一个一个地遍历数千个 AD 用户主目录,基本上对每个目录执行以下步骤:

在过度测试和解决问题之后,脚本完美运行,除了一个让我把头撞到墙上的问题。

该脚本成功循环了大约 50-150 个文件夹(非常随机),然后导致以下错误:"the trust relationship between the primary domain and the trusted domain failed"

我构建了一个额外的循环,当此错误发生时将重试 30 次(每 30 秒)。但是,这无济于事,因为只要脚本运行,信任关系就会丢失。

最有趣的部分是,一旦我再次运行脚本,(从问题文件夹开始)该文件夹将被处理而没有进一步的错误。该脚本再也不会卡在同一个文件夹中。但随后又发生了这种情况,比如 50 个文件夹之后。

这是一个巨大的不便,因为我需要处理至少 15,000 个用户文件夹,并且当 1 失败时,我总是需要编译一个新的“待处理文件夹”列表。

这是基本的代码功能,我去掉了所有不必要的错误处理和重试循环以提高可读性:

foreach ($folder in $homeFoldersFound) {
    $accessControl = Get-Acl -LiteralPath $folder.FullName -ErrorAction Stop

    #Current owner
    $folderOwner = $accessControl.Owner

    #Take ownership for the user running the script
    $accessControl.SetOwner([System.Security.Principal.NTAccount]$currentUser)

    #Access rule to add
    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($groupToAdd,"FullControl","ContainerInherit,ObjectInherit", "None", "Allow")
    $accessControl.AddAccessRule($accessRule)

    #Purge current explicit permissions
    $accessControl.SetAccessRuleProtection($true, $false)

    #Apply ownership and access rules
    set-acl -AclObject $accessControl -LiteralPath $folder.FullName -ErrorAction Stop | Out-Null


    #Return the previous ownership and apply
    $accessControl.SetOwner([System.Security.Principal.NTAccount]$folderOwner)
    $accessControl.SetAccessRuleProtection($false, $false)
    set-acl -AclObject $accessControl -LiteralPath $folderItem -ErrorAction Stop | Out-Null


    #Loop through child items, enable inheritance & remove explicit permissions
    foreach ($item in (Get-ChildItem -LiteralPath $folder.FullName -Recurse -ErrorAction Stop)) {
        #More code
    }
}

同样,代码不应该有任何问题,因为错误是如此随机发生并在再次运行脚本时通过。关于可能导致此问题/如何解决此问题的任何想法?

感谢所有帮助!

标签: powershellactive-directoryaclpowershell-5.0

解决方案


当您调用 时AddAccessRule,如果身份引用是类型,System.Security.Principal.SecuriyIdentifier那么您不会遇到此问题。该问题似乎发生在从转换NTAccountSecurityIdentifier; 由您调用$ntAccount.Translate([System.Security.Principal.SecuriyIdentifier]),或者AddAccessRule在收到除SecurityIdentifier.

好消息是,从作为stringto 类型保存的 SID 进行翻译SecurityIdentifier没有这个问题。所以一个简单的演员就足够了;例如[System.Security.Principal.SecurityIdentifier]'S-1-1-0'

要在不使用该Translate选项的情况下获取 SID,您可以从 AD 中提取它((Get-AdUser 'myUsername').SID如果您安装了 AD 模块,HexSIDToDec(([ADSI]("WinNT://$myDomain/$myUsername,user")).objectSID)如果没有)。

或者,Dave Wyatt 在他的Get-Sid 函数中提供了一个很好的解决方案,用于通过 Windows API 获取用户的SID 。他的代码复制如下:

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'

注意:在我的 ADSI 示例中,我使用了一个HexSIDToDec函数将字节数组转换为 SID 字符串。可以在这里找到/复制下面的代码。

Function HexSIDToDec($HexSID)
{
    # Convert into normal array of bytes.
    $strSID = "S-" + $HexSID[0]
    $arrSID = $strSID.Split(" ")
    $Max = $arrSID.Count
    $DecSID = $arrSID[0] + "-" + $arrSID[1] + "-" + $arrSID[8]
    If ($Max -eq 11)
    {
      Return $DecSID
    }
    $Temp1 = [Int64]$arrSID[12] + (256 * ([Int64]$arrSID[13] + (256 * ([Int64]$arrSID[14] + (256 * ([Int64]$arrSID[15]))))))
    $DecSID = $DecSID + "-" + $($Temp1)
    If ($Max -eq 15)
    {
      Return $DecSID
    }
    $Temp2 = [Int64]$arrSID[16] + (256 * ([Int64]$arrSID[17] + (256 * ([Int64]$arrSID[18] + (256 * ([Int64]$arrSID[19]))))))
    $DecSID = $DecSID + "-" + $($Temp2)
    $Temp3 = [Int64]$arrSID[20] + (256 * ([Int64]$arrSID[21] + (256 * ([Int64]$arrSID[22] + (256 * ([Int64]$arrSID[23]))))))
    $DecSID = $DecSID + "-" + $($Temp3)
    If ($Max -lt 24)
    {
      Return $DecSID
    }
    $Temp4 = [Int64]$arrSID[24] + (256 * ([Int64]$arrSID[25] + (256 * ([Int64]$arrSID[26] + (256 * ([Int64]$arrSID[27]))))))
    $DecSID = $DecSID + "-" + $($Temp4)
    Return $DecSID
}

我已经从它们的源代码中逐字复制了这些代码示例。


推荐阅读