AD 已删除对象查询与还原
AD 已删除对象查询与还原
删除调用成功,随后按 GUID 查询却返回零条,并不矛盾。普通搜索默认不显示已删除对象;而能查到删除对象,也不自动意味着它还可以完整恢复。
下面使用 Windows 域 corp.test.checo.cc 的 LAB-DC1 演示精确 GUID 查询和回收站还原。查询与还原采用不同测试对象;还原示例的前提是林回收站已启用,对象是在启用之后删除的。
1. 查询已删除对象
查询实验先确认服务器声明支持 ShowDeletedControl:OID 1.2.840.113556.1.4.417,该次 RootDSE supportedControl 共 40 项。随后为目标和存活对照各做一次 GUID 查询,再删除目标。
| 查询 | ShowDeletedControl | 结果码 | 条数 |
|---|---|---|---|
| 删除前查目标 | 无 | Success | 1 |
| 删除后查目标 | 无 | Success | 0 |
| 删除后同连接、同过滤器查目标 | 有 | Success | 1 |
| 再移除控制查目标 | 无 | Success | 0 |
| 同期查未删除对照 | 无 | Success | 1 |
返回 0 条不是本例的 NoSuchObject。这里的 Base 是仍存在的域命名上下文,搜索正常完成,只是已删除目标不在默认可见结果中。查询一个已经不存在的原 DN 则属于另一个入口,不能把两者的错误文本混用。
在安装 AD 模块的管理机上,使用准确的历史 GUID、固定一台 DC,先做只读检查:
Import-Module ActiveDirectory
$dc = 'LAB-DC1.corp.test.checo.cc'
$id = [guid]'a0e653e5-1aa7-41b6-9d85-78a9ea66d038'
Get-ADObject -Identity $id -Server $dc -IncludeDeletedObjects -Properties `
isDeleted,isRecycled,lastKnownParent,msDS-lastKnownRDN,objectGUID,objectSID,whenChanged本次目标 GUID 是 a0e653e5-1aa7-41b6-9d85-78a9ea66d038。删除后 uSNChanged 从 167986 变为 167990,isDeleted=True,lastKnownParent 指向原 OU。DN 中保留旧 RDN 并附加 \0ADEL:<GUID>,随后位于 Deleted Objects 容器;不是对象重建成了同名用户。
2. ShowDeleted 控制
原始应用修复只给同一个 SearchRequest 增加 ShowDeletedControl,Base、Scope、Filter 和连接不变。GUID 过滤器需要使用二进制值的 LDAP 转义字节,而不是把 GUID 文本随意拼成 DN。
下面是当前 Windows 身份、Negotiate 签名/密封的整理示例。需要读取删除容器及目标的权限;控制不会绕过 DACL。它是查询代码,不会恢复、重新删除或修改对象:
Add-Type -AssemblyName System.DirectoryServices.Protocols
$dc = 'LAB-DC1.corp.test.checo.cc'
$guid = [guid]'a0e653e5-1aa7-41b6-9d85-78a9ea66d038'
$filter = '(objectGUID=' + (($guid.ToByteArray() | ForEach-Object {
'\' + $_.ToString('x2')
}) -join '') + ')'
$c = New-Object System.DirectoryServices.Protocols.LdapConnection($dc)
$c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Negotiate
$c.SessionOptions.Signing = $true
$c.SessionOptions.Sealing = $true
try {
$c.Bind()
$base = (Get-ADRootDSE -Server $dc).defaultNamingContext
$q = New-Object System.DirectoryServices.Protocols.SearchRequest(
$base,$filter,[System.DirectoryServices.Protocols.SearchScope]::Subtree,
[string[]]@('objectGUID','isDeleted','lastKnownParent'))
$q.SizeLimit = 3
$q.TimeLimit = [TimeSpan]::FromSeconds(15)
$control = New-Object System.DirectoryServices.Protocols.ShowDeletedControl
$control.IsCritical = $true
[void]$q.Controls.Add($control)
$r = $c.SendRequest($q)
[pscustomobject]@{ ResultCode=$r.ResultCode; Count=$r.Entries.Count }
foreach ($entry in $r.Entries) { $entry.DistinguishedName }
} finally {
$c.Dispose()
}原实验增加控制后 Count=1、GuidMatch=True;去掉控制回到 0,存活对照仍为 1。若服务器不支持 critical 控制,应保留 unavailableCriticalExtension 等错误,而不是把失败当成空结果。枚举大量删除对象还需要对象分页,本例用精确 GUID 和最多 3 条的限制只查一个目标。检索删除对象
3. 回收站状态
11 月的还原实验读到 EnabledScopes 有两条:一条是林配置分区的 Partitions,另一条是测试 DC 的 NTDS Settings。这个数字属于该环境,不是所有林都必须等于 2 的判据。
下面检查当前林作用域是否在列表里,并把实际列表留下:
Import-Module ActiveDirectory
$dc = 'LAB-DC1.corp.test.checo.cc'
$root = Get-ADRootDSE -Server $dc
$feature = Get-ADOptionalFeature -Identity 'Recycle Bin Feature' -Server $dc
$forestScope = 'CN=Partitions,' + $root.configurationNamingContext
[pscustomobject]@{
Feature = $feature.DistinguishedName
ForestScope = $forestScope
EnabledForForest = @($feature.EnabledScopes) -contains $forestScope
}
$feature.EnabledScopes重复启用测试返回 The specified value already exists,尝试关闭返回 The specified method is not supported,前后作用域不变。这证明该实验域原已启用且调用未改变状态;不能把任意“值已存在”异常都一律吞掉作为部署成功。
回收站启用是不可逆的林功能决策,不是让搜索多返回一条记录的修复。它也不能把启用之前删除的对象追溯变成完整可恢复对象。微软启用与使用说明
4. 还原与验证
还原实验建立用户并加入两个测试组。关键记录如下:
| 状态 | GUID | SID 末段 | pwdLastSet | 组关系 |
|---|---|---|---|---|
| 删除前 | 7fc14e90-009a-4d73-97d4-91665471b05a | 4985 | 134383295727825939 | G1、G2 |
| 删除后 | 不变 | 不变 | 记录保留 | 组的活动成员列表不再显示该删除用户 |
| 还原后 | 不变 | 不变 | 134383295727825939 | G1、G2 恢复 |
删除前 uSNChanged=243928,删除后为 243935,还原后为 243940;它随目录变化推进,不应该要求恢复到旧值。还原后的 DN 回到 CN=kb-t41-restore,OU=KB-T4,...,Description、Department、Title、Office 的测试值也相同。
对象身份未变与“删除后重建同名账号”有本质差别;同名不保证 SID 相同。这里 pwdLastSet 不变是属性证据,不等于本次另做过旧密码的实际登录测试。实际业务仍要用适当的认证与访问验收核对,而不能靠时间戳推断所有系统已经恢复。
在确认对象仍处于可还原的删除阶段、原父容器存在、没有名称冲突后执行:
$dc = 'LAB-DC1.corp.test.checo.cc'
$id = [guid]'7fc14e90-009a-4d73-97d4-91665471b05a'
$deleted = Get-ADObject -Identity $id -Server $dc -IncludeDeletedObjects -Properties `
isDeleted,isRecycled,lastKnownParent,msDS-lastKnownRDN,objectSID
$deleted | Format-List DistinguishedName,ObjectGUID,ObjectSID,isDeleted,isRecycled,lastKnownParent
Get-ADObject -Identity $deleted.lastKnownParent -Server $dc
Restore-ADObject -Identity $id -Server $dc -WhatIf
# After verifying the exact object, state, and destination:
Restore-ADObject -Identity $id -Server $dc
Get-ADUser -Identity $id -Server $dc -Properties memberOf,pwdLastSet,Description,Department,Title,Office |
Select-Object DistinguishedName,ObjectGUID,SID,Enabled,pwdLastSet,memberOf,Description,Department,Title,OfficeWhatIf 只帮助核对拟执行目标,不替代对象状态、权限或还原后的验收。代码中的 GUID 是历史实验对象,实际处理换成已确认目标。保持同一 DC 读取便于比较;多 DC 环境另行确认复制及客户端实际使用的节点。
5. 父 OU 与目标路径
另一个测试先删除用户及父 OU,先还原用户得到 parent 相关错误;先还原父 OU 再还原用户成功。对于恢复原结构,这是有意义的顺序要求,不应概括成所有恢复场景唯一可行的顺序。
若原 OU 已不再需要,也可明确选择一个现存 TargetPath:
$target = Read-Host 'Existing destination OU DN'
Get-ADOrganizationalUnit -Identity $target -Server $dc
Restore-ADObject -Identity $id -Server $dc -TargetPath $target -WhatIf
# Execute only after reviewing the new policy/permission scope.指定其他路径会改变 DN,也可能改变继承的权限、GPO 范围与依赖路径的应用行为。不能因为 SID 保持就说所有访问不受影响。还原 OU 后也应回读其删除保护状态,而不是假定自动启用保护。Restore-ADObject 的 TargetPath 与默认父路径
6. 恢复限制
需要区分回收站保留的 deleted 阶段、后续 recycled 阶段及未启用回收站的墓碑行为。它们保留的属性和可恢复方式不同,不能统一写成“目录里还在就能捞回来”,也不能把所有林的期限固定写成 180 天。
本次测试覆盖启用回收站后的短时间删除/还原,没有做保留期耗尽或 recycled 阶段恢复。查询不到时先核对 DC、权限、控制和对象状态;不要通过同名重建来证明原身份已恢复。查询到时则核对可恢复状态、身份、目标容器、组链接、属性与当前 ACL。
还原不是无副作用的探测,也不是可以自动“回滚”的读取。执行前导出当前目标对象及受影响组状态;如果还原后发现目标选错,应先停止后续业务变更并核对,不再盲目删除这个刚恢复的对象。
