我制作了一个脚本,该脚本必须遍历数千个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 :(得分:0)
调用AddAccessRule
时,如果身份引用的类型为System.Security.Principal.SecuriyIdentifier
,则不会遇到此问题。从NTAccount
到SecurityIdentifier
的转换似乎出现了问题;可以通过调用$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
}
我已从源代码中逐字复制了这些代码示例。