ОЧЕНЬ случайная ошибка 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
    }
}

Опять же, в коде не должно быть ничего плохого, так как ошибка происходит случайно и проходит при повторном запуске сценария. Любые идеи о том, что может вызвать это / как обойти это?

Вся помощь приветствуется!

0 ответов

Когда ты звонишь AddAccessRule, если идентификационная ссылка имеет тип System.Security.Principal.SecuriyIdentifierтогда вы не столкнетесь с этой проблемой. Проблема возникает при преобразовании изNTAccount к SecurityIdentifier; будь это твоим звонком$ntAccount.Translate([System.Security.Principal.SecuriyIdentifier]), или оставив это на AddAccessRule делать в фоновом режиме при получении идентификационной ссылки любого типа, кроме SecurityIdentifier.

Хорошая новость заключается в том, что перевод с идентификатора SID, который хранится как string печатать SecurityIdentifierнет этой проблемы; поэтому достаточно простого приведения; например[System.Security.Principal.SecurityIdentifier]'S-1-1-0'.

Чтобы получить SID без использования Translate вариант, возможно, вы сможете вытащить его из AD ((Get-AdUser 'myUsername').SID если у вас установлен модуль AD, HexSIDToDec(([ADSI]("WinNT://$myDomain/$myUsername,user")).objectSID) если не).

В качестве альтернативы Дэйв Вятт предлагает отличное решение для получения SID пользователя через Windows API в своей функции Get-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. Код для этого можно найти здесь / скопировать ниже.

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
}

Я скопировал эти образцы кода дословно из их источников.

Другие вопросы по тегам