要管理 AD,用網域管理者群組(Domain Admins)成員執行是無腦又簡單做法,但 Domain Admins 權限過大,減少使用頻率有助降低風險。對於頻繁發生的日常作業,另外授權指定群組或人員,只給與必要的最小權限,是更安全的做法,「解鎖 AD 帳號或重設密碼」便是其中一例。

實務上常見的做法是在 AD OU 新增委派控制(Delegate Control),指定使用者或群組跟可執行的作業,不同 OU 可指派不同人員負責。例如,假設我們要委派 jeffrey 有權解鎖及重設 Marketing OU 人員密碼:

  1. 在 Active Directory Users and Computers 主控台找到 Marketing OU 新增委派
  2. 選取指定帳號或群組
  3. 選擇作業項目:Reset user passwords and force password change at next logon

    註:如果只想開放解鎖不含重設密碼,請選「Create a custom task to delegate」,選取「User objects」、「Property-specific」、「Read lockoutTime / Write lockoutTime」參考

不過,比較麻煩的是同一介面沒法查詢目前的委派,需改用 ADSI Edit 查詢。

開啟 ADSI Edit 找到 OU,由屬性 Security 頁籤查詢,管理密碼作業屬於 Special permissions:

若要查詢 Special permissions 細節要從 Advacned 進去,會看到兩筆,一筆 Access 為 Reset password,另一筆空白,也不怎麼明確:

GUI 操作複雜加上語意不明,若想快速盤點或產生報表,又得招喚 PowerShell 登場了。

微軟部落格有篇解說詳細的好文,拿香跟著拜,我寫出以下報表工具:

Import-Module ActiveDirectory

#REF: https://devblogs.microsoft.com/powershell-community/understanding-get-acl-and-ad-drive-output/
$ObjectTypeGUID = @{}
$GetADObjectParameter = @{
    SearchBase = (Get-ADRootDSE).SchemaNamingContext
    LDAPFilter = '(SchemaIDGUID=*)'
    Properties = @("Name", "SchemaIDGUID")
}
Get-ADObject @GetADObjectParameter | ForEach-Object {
    $ObjectTypeGUID.Add([GUID]$_.SchemaIDGUID, $_.Name)
}
$ADObjExtPar = @{
    SearchBase = "CN=Extended-Rights,$((Get-ADRootDSE).ConfigurationNamingContext)"
    LDAPFilter = '(ObjectClass=ControlAccessRight)'
    Properties = @("Name", "RightsGUID")
}
Get-ADObject @ADObjExtPar | ForEach-Object {
    if (!$ObjectTypeGUID.ContainsKey([Guid]$_.RightsGUID)) {	
        $ObjectTypeGUID.Add([GUID]$_.RightsGUID, $_.Name)
    }
}
$ObjectTypeGUID.Add([Guid]::Empty, 'All');

Get-ADOrganizationalUnit -Filter * -SearchScope OneLevel | ForEach-Object {
    $dn = $_.DistinguishedName 
    $name = $_.Name
    (Get-Acl -Path "AD:\$dn").Access | Where-Object {
        return $_.IsInherited -eq $false
    } | ForEach-Object {
        #$ObjectTypeGUID[[Guid]$_.ObjectType.Guid]
        [PSCustomObject][Ordered]@{
            OU = $name  
            Access = $_.AccessControlType
            Id = $_.IdentityReference.Value
            Rights = $_.ActiveDirectoryRights
            Object = $ObjectTypeGUID[[Guid]$_.ObjectType.Guid]
            Target = $ObjectTypeGUID[[Guid]$_.InheritedObjectType.Guid],
			ObjId = $_.ObjectType.Guid
			InhObjId = $_.InheritedObjectType.Guid
        }        
    }
}

報表可以看出 jeffrey 對 Marketing 的 Users 具有 Pwd-Last-Set 讀寫及 User-Force-Change-Password 延伸權限,比 UI 快速且清楚。

產生報表沒問題,那可以用 PowerShell 設定委派權限嗎?參考網路文章,我寫成簡單工具,輸入 OU 跟帳號即可授權該帳號管理該 OU 的密碼(其實就是賦與 Pwd-Last-Set 讀寫及 User-Force-Change-Password 延伸權限):

param (
    [Parameter(Mandatory=$true)][string]$ouName, 
    [Parameter(Mandatory=$true)][string]$acntName
)
$ErrorActionPreference = 'STOP'
$dn = "AD:\OU=$ouName,DC=utopia,DC=com"
$acls = Get-ACL -Path $dn
[System.Security.Principal.IdentityReference]$id = (Get-AdUser -Identity $acntName).SID
$pwdLastSet = [Guid]'bf967a0a-0de6-11d0-a285-00aa003049e2'
$forceChgPwd = [Guid]'00299570-246d-11d0-a768-00aa006e0529'
$users = [Guid]'bf967aba-0de6-11d0-a285-00aa003049e2'
$aclPwdLastSet = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($id, "ReadProperty, WriteProperty", "Allow", $pwdLastSet, 'Descendents', $users)
$aclForceChgPwd = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($id, "ExtendedRight", "Allow", $forceChgPwd, 'Descendents', $users)
$acls.AddAccessRule($aclPwdLastSet)
$acls.AddAccessRule($aclForceChgPwd)
Set-Acl -Path $dn -AclObject $acls

程式裡比較神奇部分應該是 bf967a0a-0de6-11d0-a285-00aa003049e2、00299570-246d-11d0-a768-00aa006e0529 這些 GUID 是怎麼來的?我採用逆推法,先用 UI 做完想要的設定,再反查 ObjId 及 InhObjId,原則上就能複刻所有 UI 操作的結果囉。

再累積一些 PowerShell 管理 Windows 技巧。

【參考資料】

Example of how to list and add ACL of AD OU with PowerShell.


Comments

# by 挨踢cc

版主您好,請問ActiveDirectoryRights Enum中有和「複製」物件有關的權限嗎,我嚐試過用duplicate一詞找,但沒找到, 因為想讓管理員有複製權限, 謝謝~

# by Jeffrey

to 挨踢cc, 我是用逆推法,先用 UI 做完想要的設定,再反查 ObjId 及 InhObjId (參考文章最後一張圖)

# by 挨踢cc

版主好,後來我有在內容安全性裡找到想到的權限,加上ObjId 及 InhObjId方式確實能比較快速地復刻想要的結果,感謝版主的指點!

Post a comment