Netwrix 1Secure 提供跨数据和身份的统一可见性——免费试用14天,享有完全访问权限。开始免费试用

资源中心博客

如何备份和还原组策略对象(GPO)

如何备份和还原组策略对象(GPO)

Dec 18, 2024

组策略对象对于管理 Windows Server 基础架构至关重要。为了避免严重的服务问题,管理员必须谨慎配置 GPO,并在修改之前先备份,以便必要时能够快速回滚任何更改。

Group Policy Refresher

在深入了解组策略对象的备份与恢复之前,让我们先回顾一些关于 GPO 如何创建和使用的重要细节。

GPO 组件

当创建新的组策略对象时,会为其分配一个称为 GUID 的唯一标识符。每个 GPO 都包含两个部分:

  • 组策略模板(GPT) — GPT 由 SYSVOL 文件共享中的一组文件夹组成(“C:\WindowsSYSVOLdomainPolicies{GUID}”)。这些文件夹用于存储组策略对象的大部分内容,包括模板、设置、脚本以及有关 MSI 包的详细信息。根据 Windows 的版本不同,GPT 会通过文件复制服务(FRS)或分布式文件系统复制(DFSR)复制到域中的每个 DC。事实上,由于 SYSVOL 只会在域内复制,因此 GPO 在实际层面上是特定于某个域的。
Restore GPO 1

  • 组策略容器(GPC) — GPC 是一个 groupPolicyContainer 对象,位于 CN=System,CN=Policies 下的域命名上下文中。该 AD 对象的属性用于存储与 GPO 相关的引用信息。值得注意的是,这包括 gPCFileSysPath 属性,其中包含指向 SYSVOL 中 GPO 的 GPT 的路径。与 GPT 不同,GPC 会根据配置的复制成本、计划和间隔,由 Active Directory Domain 服务进行复制。
Restore GPO 2

GPO 关联

创建 GPO 后,它可以与一个或多个 Active Directory 对象关联:组织单位(OU)、域以及站点。此关联并不是由 GPO 维护的,而是由每个关联的 AD 对象在其 gPLink 属性中维护。对象的 gPLink 属性的值是该对象已关联的每个 GPO 的 GPC 路径列表。当创建或删除某个 GPO 与对象的关联时,只会修改受影响对象的 gPLink 属性值。

需要特别注意的是,尽管 SYSVOL 的复制实际上会使 GPO 变为特定于域的,但由于 GPO 可以链接到站点对象,这意味着与 GPO 相关的信息并不一定被局限在某个域内。站点对象存储在 Active Directory 配置分区中,而该分区会复制到林中的所有域控制器。这会导致 gPLink 属性中包含的组策略对象的 GPC 路径在 Active Directory 复制过程中“溜出”域之外。

组策略处理顺序

Active Directory 按以下顺序应用 GPO:

  1. 本地 GPO
  2. 站点链接的 GPO
  3. 域链接的 GPO
  4. OU 链接的 GPO(从根开始处理,因此链接到嵌套 OU 的 GPO 会优先于链接到其父 OU 的 GPO)

最后应用的策略“生效”(除非使用“Enforce”选项,该选项会阻止策略被随后应用的策略覆盖)。

Restore GPO 3

用户和计算机配置

最后需要注意的是,GPO 同时包含“计算机配置”和“用户配置”。这些子组包含几乎相同的策略设置集合,但它们在何时应用 GPO 设置方面有所不同。

计算机配置设置在启动时应用到计算机,而用户配置设置在登录时应用。也就是说,计算机配置中的选项会始终对关联的计算机强制生效;而用户配置中的选项仅在关联的用户账户登录到某台计算机时才会强制生效。

如果用户登录到某台计算机,并且“计算机配置”设置与“用户配置”设置之间存在冲突,那么“计算机配置”设置将始终优先。

使用 PowerShell 的 GPO 备份与还原流程

在充分理解这些关键概念之后,我们就可以继续进行 GPO 备份和恢复。

我们将从 Microsoft 的 Windows 远程服务器管理工具中提供的两个相关 Group Policy PowerShell cmdlet 开始:

  • Backup-GPO — 该 cmdlet 可轻松对域中的所有 Group Policy 对象或单个指定的 GPO 进行快照。
  • Restore-GPO — 该 cmdlet 可将 Group Policy 对象还原到使用 Backup-GPO cmdlet 创建的备份中所捕获的状态。

创建所有 GPO 的备份

以下 PowerShell 脚本使用 Backup-GPO cmdlet 的 -All 参数,使用指定的 DC 创建指定域中所有 GPO 的备份:

      $BackupPath = '\HOSTNAMEGPOBackup'
$Domain = 'domain.local'
$DomainController = 'DC.domain.local'

$BackupFolder = New-Item -Path $BackupPath -Name (Get-Date -format yyyyMMddTHHmmss) -ItemType Directory
Backup-GPO -All -Domain $Domain -Server $DomainController -Path $BackupFolder
      

Backup-GPO cmdlet 的输出包括:每个 GPO 的备份信息对应一个独立的子文件夹,以及一个 manifest.xml 文件,用于包含将各个子文件夹关联到其各自 GPO 所需的信息:

Restore GPO 4

查看其中一个子文件夹,我们发现每个备份都由一个文件夹和三个 XML 文件组成:

Restore GPO 5

该文件夹包含组策略对象的 GPT 副本;XML 文件则包含来自组策略 GPC 的数据、与备份执行相关的特定信息,以及描述 GPO 内容的报告。

Netwrix Identity Recovery

使用单个快照备份并恢复 AD 和组策略对象

备份版本控制

支持将组策略对象反复备份到同一位置,但此脚本的每次执行都会为其输出创建唯一的子文件夹。子文件夹的名称使用在 cmdlet 执行期间生成的、特定于备份的 GUID,从而几乎消除了在对同一位置进行重复备份时发生命名冲突的可能性。

将每次执行的输出分隔到各自的唯一文件夹中并非绝对必要,但 Restore-GPO cmdlet 的行为使得这样做会带来好处。Restore-GPO cmdlet 允许你一次性还原所有 GPO,但它将使用 manifest.xml 中所标识的每个组策略对象的最新备份。通过将每组备份分开放到自己的文件夹中,你可以确保每组备份都拥有自己的 manifest.xml。这样就可以在一次操作中还原这些备份集中任意一组里的全部 GPO。

使用 Backup-GPO cmdlet 的 -All 参数的一个缺点是:它会在每次执行时备份域中的所有组策略对象,而不是仅备份已更改的对象。解决这一问题的一种方式是使用类似下面的脚本。该脚本会强制 Backup-GPO cmdlet 使用脚本在“update”文件夹中创建的文件来维护脚本上一次运行的时间戳。如果该文件存在,脚本将备份自文件中的时间戳以来已被修改的域中的任何 GPO。 如果该文件不存在,脚本会创建该文件,设置时间戳,然后备份域中的所有 GPO。

      $BackupPath = '\DCGPOBackup'
$Domain = 'domain.local'
$DomainController = 'DC.domain.local'

$BackupPathTracker = "$BackupPathLastBackup.txt"

if((Test-Path "$BackupPathTracker")) {
    $LastBackup = Get-Date (Get-Content -Path "$BackupPathTracker")
    Set-Content -Path "$BackupPathTracker" -Value (Get-Date)
    $GPOs = Get-GPO -All -Domain $Domain -Server $DomainController
    $GPOs | Where-Object { $_.ModificationTime -gt $LastBackup } | ForEach-Object {
        Backup-GPO -Guid $_.Id -Domain $Domain -Server $DomainController -Path $BackupPath
    }
} else {
    Set-Content -Path "$BackupPathTracker" -Value (Get-Date)
    Backup-GPO -All -Domain $Domain -Server $DomainController -Path $BackupPath
}
      

这种做法通过限制不必要的备份来节省空间,但它所执行的备份最终都会放在同一个文件夹中,这会使得难以将组策略对象恢复到特定的时间点。

将这两个脚本中采用的思路结合起来就能解决我们所有的问题,对吗?

      BackupPath = '\HOSTNAMEGPOBackup'
$Domain = 'domain.local'
$DomainController = 'DC.domain.local'

$BackupPathTracker = "$BackupPathLastBackup.txt"
$BackupFolder = New-Item -Path $BackupPath -Name (Get-Date -format yyyyMMddTHHmmss) -ItemType Directory

if((Test-Path "$BackupPathTracker")) {
    $LastBackup = Get-Content -Path "$BackupPathTracker"
    Set-Content -Path "$BackupPathTracker" -Value (Get-Date)
    $GPOs = Get-GPO -All -Domain $Domain -Server $DomainController
    $GPOs | Where-Object { $_.ModificationTime -gt $LastBackup } | ForEach-Object {
        Backup-GPO -Guid $_.Id -Domain $Domain -Server $DomainController -Path $BackupFolder
    }
}
else {
    Set-Content -Path "$BackupPathTracker" -Value (Get-Date)
    Backup-GPO -All -Domain $Domain -Server $DomainController -Path $BackupFolder
}
      

事实证明,将每次差异备份的结果分别放在各自的文件夹里,反而会让一切变得更糟。这种做法还会把每个 manifest.xml 文件也隔离到各自的文件夹中:一方面,实际上消除了“一次性还原所有 GPO”的能力;另一方面,也会让查找与任何特定组策略对象相关的备份变得极其困难。

从备份还原 GPO

在实践中,这些做法会遇到 Restore-GPO cmdlet 的一个限制:Restore-GPO 可以从 manifest.xml 文件中引用的“最新备份”或“指定的备份”中还原特定的组策略对象——但要一次性还原某个域中的所有 GPO,则只能使用指定的 manifest.xml 文件中对应的最新备份。

      # Restore a single GPO from its most recent backup
Restore-GPO -Name 'GpoName' -Path '\HOSTNAMEGPOBackup' -Domain 'domain.local' -Server 'DC.domain.local'

# Restore a single GPO from a specific backup
Restore-GPO -BackupId 12345678-09ab-cdef-1234-567890abcdef -Path '\HOSTNAMEGPOBackup' -Domain 'domain.local' -Server 'DC.domain.local'

# Restore all of a domain’s GPOs from their most recent backup
Restore-GPO -All -Path '\HOSTNAMEGPOBackup' -Domain 'domain.local' -Server 'DC.domain.local'
      

当从备份还原组策略对象时,会在还原过程中将 GPO 的版本递增,以强制复制在比较时偏向于已还原的 GPO 副本。

Restore GPO 6

版本号为何会失去同步

Restore-GPO cmdlet 的输出包含两组版本号:一组用于计算机配置(Computer Configuration),另一组用于用户配置(User Configuration)。

GPT 和 GPC 各自负责维护自己的版本号;其中,单独的 UserVersion 和 ComputerVersion 值分别由“用户配置(User Configuration)”的版本号和“计算机配置(Computer Configuration)”的版本号计算得出。这之所以可行,是因为版本号的递增方式不同:当修改 GPO 的计算机配置时,版本号增加 1;当每次修改用户配置时,版本号增加 65536。

所有这些都是必需的,因为如上所述,一个组策略对象(Group Policy object)的 GPT 和 GPC 由不同的服务分别进行复制,这可能导致在任何特定域控制器上,某个 GPO 的 GPT 和 GPC 的版本号出现不同步——从而阻止其被处理。

恢复已删除的 GPO

Restore-GPO cmdlet 的一个有据可查的限制是:它不能用于恢复已删除的组策略对象(Group Policy object)。原因是它将无法在 Active Directory 中找到该 GPO 的 GPC 部分。尝试恢复已删除的 GPO 将导致如下所示的错误:

Restore GPO 7

但是,如果你能够先恢复 GPC,则可以使用 Restore-GPO 来恢复已删除的 GPO。为此,我们可以使用 Restore-ADObject cmdlet,从 Active Directory Recycle Bin 中完整恢复 GPO 的 Active Directory 部分,然后 Restore-GPO cmdlet 就可以恢复该 GPO:

Restore GPO 8

尽管此流程可以恢复已删除的 GPO,但它无法还原 GPO 删除之前存在的 gPLink 值。这是因为这些值只存在于被关联的对象(linked objects)上。要安全地绕过这一限制,唯一的方法是使用包含 gPLink 值的 Active Directory 外部备份。

使用 GPMC 和 AGPM 进行 GPO 备份与恢复

用于 GPO 备份与恢复的选择并不只有 Group Policy PowerShell cmdlets。Microsoft 还提供 Group Policy Management 控制台(GPMC),这是一种 MMC 管理单元(snap-in),可用于备份和恢复组策略对象(Group Policy Objects)。与 Backup-GPO cmdlet 类似,它可以备份单个指定的 GPO,也可以备份某个域的所有 GPO。与 Restore-GPO cmdlet 不同的是,它一次只能恢复一个 GPO。

GPMC 确实提供了从备份中还原已删除 GPO 的方法,但它实际上并不会恢复已删除的 GPO;它只是创建一个新的 GPO,并使用备份中的信息对其进行填充。

GPMC 的另一个好处是可以更清晰地查看 GPO 备份的内容,不过要将备份中的设置与当前正在使用的实时 GPO 的设置进行对比,依然比较困难。

作为 Microsoft Desktop Optimization Pack 的一部分提供的 Microsoft 的 Advanced Group Policy Management (AGPM) 工具,通过版本控制功能扩展了 GPMC,帮助你查看并理解备份的内容。然而,AGPM 的优势往往会被两个因素抵消:它似乎维护得并不完善,而且还因与其他系统“协同不佳”而有口碑(例如,在 AGPM 之外修改由 AGPM 管理的 GPO,可能会导致 AGPM 数据库损坏)。话虽如此,它未必是个坏工具;我只是建议你在将其部署到生产环境之前,先在实验室环境中多多摸索、充分测试。

Netwrix 如何提供帮助?

Netwrix Identity Recovery 提供统一的 Web 界面,使你能够在单个快照中备份 Active Directory 对象和组策略(Group Policy)对象,搜索并管理备份,将对活动(在线)对象的属性更改回滚,甚至恢复已删除的 GPO 及其关联的 gPLinks。

Active Directory 审计与报告

借助专家洞察,简化您 Active Directory 的安全管理

了解更多

常见问题

我可以使用什么命令从备份中还原 GPO?

Windows PowerShell cmdlet Restore-GPO 会将单个 GPO 或所有 GPO 恢复到原始域。

如何从文件导入 GPO?

如果您在 Advanced Group Policy Management (AGPM) 中以管理员身份拥有必要的权限,并已将 GPO 导出为 CAB 文件,那么您可以按照提供的步骤,将 GPO 设置导入到新的 GPO 或现有的 GPO 中 此处

分享到

了解更多

关于作者

Asset Not Found

Joe Dibley

安全研究员

Netwrix 的安全研究员,并且是 Netwrix Security Research Team 的成员。Joe 是 Active Directory、Windows 以及各类企业软件平台与技术方面的专家;他/她研究新的安全风险、复杂的攻击技术,以及相应的缓解措施与检测方法。