非常随机的PowerShell错误,信任关系失败

时间:2018-07-13 07:07:09

标签: powershell active-directory acl powershell-v5.0

我制作了一个脚本,该脚本必须遍历数千个AD用户主目录    一一一一完成,基本上每个人都要执行以下步骤:

  • 取得文件夹的所有权
  • 为Domain Admin组添加访问规则
  • 返回文件夹的所有权
  • 遍历所有子文件夹和文件,启用继承并删除 所有显式权限

经过过多的测试和问题解决后,脚本可以正常工作,除了一个使我的头撞在墙上的问题。

该脚本成功循环了大约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
    }
}

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

感谢所有帮助!

1 个答案:

答案 0 :(得分:0)

调用AddAccessRule时,如果身份引用的类型为System.Security.Principal.SecuriyIdentifier,则不会遇到此问题。从NTAccountSecurityIdentifier的转换似乎出现了问题;可以通过调用$ntAccount.Translate([System.Security.Principal.SecuriyIdentifier])或在接收到AddAccessRule以外的任何类型的身份证明后,将其留给SecurityIdentifier进行后台处理。

好消息是,从保留为string的SID转换为类型SecurityIdentifier不会出现此问题;因此,简单的转换就足够了;例如[System.Security.Principal.SecurityIdentifier]'S-1-1-0'

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

或者,Dave Wyatt提供了一个很好的解决方案,可通过Windows API在其Get-Sid function中获取用户的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'

NB:在我的ADSI示例中,我使用了HexSIDToDec函数将字节数组转换为SID字符串。可以{/ {3}} /复制到下面的代码。

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
}

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