使用PowerShell框架进行Active Directory健康检查

这是关于Active Directory健康检查的两篇文章中的第二部分。虽然不是必需阅读,但如果你想了解本文中将要学习的PowerShell脚本是如何构建的,建议你查看构建Active Directory健康检查工具[深入解析]:第一部分

在第一部分中,你学到了许多不同的测试以及它们为何重要。现在让我们将它们汇总起来并构建一个工具。在这部分中,你将把第一部分中解释的所有Active Directory健康检查转换为一个测试框架。你还将学会如何将各种AD健康检查的结果输出到诸如Pester和一个名为PRTG的监控工具中。

要跟随操作或查看本文中将要学习的工具的已完成版本,请从GitHub下载ADHealthCheck-NoResult.ps1脚本。

定义输出

拥有一个共同的对象类型以及一种简单的生成方式将使将测试结果转换为所选工具变得更加容易。

为了为所有潜在的工具创建统一的输出,我选择使用PowerShell类。虽然不是必需的,但这是我选择采用的方法。主要目的是确保所有AD健康检查都返回相同类型的输出。

A PowerShell class is a schema that defines how a PowerShell object should look and what it should do. Each line you see below represents a property the objects return will have. You can see below I’m planning on each AD health check to return ten properties.

Class AdhcResult {
    [string]$Source
    [string]$TestName
    [bool]$Pass
    $Was
    $ShouldBe
    [string]$Category
    [string]$SubCategory
    [string]$Message
    $Data
    [string[]]$Tags
}

为了加快创建这个类的过程,我将使用一个名为New-AdhcResult的辅助函数。这个函数会创建类以及你需要跟进的一切内容。该函数将输出一个定制的[AdhcResult]类型对象。

运行AD健康检查工具

首先,下载并复制AD健康检查脚本到域控制器。使用PowerShell ISE打开它并运行。工具的这部分不会返回任何信息。

此脚本将运行并将每个检查的结果存储在$TestResults变量中,作为多个[AdhcResult]对象。稍后您将使用这些对象来生成报告或将其输出到各种工具中。像这样将健康检查结果保存在变量中,使您可以在选择创建另一个并使用New-AdHcResult命令时添加更多结果。

一旦脚本运行完成,您现在应该在$TestResults变量中有一组完整的AD健康检查对象。现在您可以从控制台运行$TestResults并查看原始结果。

在工具中显示AD健康检查结果

由于所有检查都是以常见的对象类型存在,您可以通过一些工具如Pester和PRTG更好地检查它们。

在这一部分,您将学习如何使用一个名为extent的工具创建HTML报告,并在PRTG中显示报告。

使用Pester创建一个nUnit XML文件

首先,您需要将PowerShell对象转换为您的工具可以理解的格式。大多数工具都可以理解XML格式,或者更具体地说,nUnit XML格式。这是一种您可以导入到各种工具以显示结果的格式。

由于您正在使用PowerShell,您将使用Pester测试框架来读取AD健康检查脚本的输出并生成一个nUnit XML文件

首先下载最新版本的Pester。您可以通过在提升权限的PowerShell控制台中运行Install-Module来下载Pester。下面的命令将强制安装最新的Pester版本。由于Pester签名的发布者证书随Windows 10一起提供,我们需要使用SkipPublisherCheck参数来安装它。

PS51> Install-Module Pester -Force -Verbose -SkipPublisherCheck

一旦Pester可用,您就可以运行脚本并动态创建一组Pester测试。

注意: 您甚至可以在不使用我提供的PowerShell脚本的情况下自己创建Pester测试。

下面的PowerShell脚本将使用Pester从$TestResults变量中定义的ADHealthCheck-NoResult.ps1脚本的输出生成一个nUnit XML文件。

将此文件保存为Pester.ps1,放在与AD健康检查脚本相同的文件夹中。

# 在ADHealthCheck文件中进行源代码引用
. $PSScriptRoot\ADHealthCheck-NoResult.ps1
$Grouped = $TestResults | Group-Object Category

Foreach($Category in $Grouped) {
    Describe -Name $Category.Name -Tags ($Category.Group.Tags | Select -Unique) {
        Foreach($Result in $Category.Group){
            Context "$($Result.Source) - $($Result.TestName)" {
                It -Name "Should've passed" {
                    $Result.Pass | Should -Be -ExpectedValue $True -Because $Result.data
                }
            }
        }
    }
}

最后,运行下面的Invoke-Pester以调用Pester.ps1文件,并以NUnitXml格式保存结果。

PS51 > Invoke-Pester -Script @{Path = '.\Pester.ps1'} -OutputFile .\NunitReport.xml -OutputFormat NUnitXml

使用Extent工具构建HTML报告

一旦您有了NUnit XML文件,您现在可以使用该文件传递给一个可以将其转换为HTML的工具。其中一个工具叫做extent。Extent是一个方便的工具,可以从Nunit XML文件创建HTML报告。

首先,下载extent到与之前创建的NunitReport.xml文件相同的目录中。然后在PowerShell会话中执行以下命令。这些命令将创建用于存储HTML文件的目录,然后执行extent.exe进行转换。

# 创建报告目录
PS51> mkdir .\HTMLReports

# 创建报告
PS51> .\extent.exe -i .\NunitReport.xml -o .\HTMLReports\

完成后,您将在HTMLReports目录中找到两个HTML文件。当您使用网络浏览器打开它们时,这些文件将看起来像下面的截图。

HTML report for Pester test output
HTML report for Pester test output

将AD健康检查结果导入PRTG

PRTG是由Paessler开发的一款流行的监控工具,您可以使用它来监视您的基础架构和服务。在本节中,您将学习如何在健康检查脚本运行后将健康检查结果推送到PRTG。

将结果推送到PRTG确实需要比让工具拉取信息更多的工作,但最终您会发现这样的设置是值得花费时间的。

先决条件

要成功地将PRTG设置为本文中构建的AD健康检查脚本的监控工具,请确保您已经:

  • 安装并配置了PRTG
  • 在PRTG中设置了所有域控制器
  • 从GitHub下载了Send-AdhcResultToPrtg.ps1 PowerShell脚本
  • 您的PRTG传感器的URL和端口

如果您已完成每个先决条件,然后您可以按照下面我建议的逐步说明来推送这些AD健康检查结果到PRTG。

  1. 在PRTG中创建一个名为Domain或您喜欢的任何名称的设备。
  2. 使用高级HTTP推送传感器创建一个IdentityTokendirectory-adhealthcheck的传感器。请注意这是区分大小写的!
  3. 对于PRTG中的每个域控制器设备,创建一个高级HTTP推送传感器。对于每个IdentityToken,请附加每个传感器的-adhealthcheck,例如dc01-adhealthcheck
  4. Send-AdhcResultToPrtg.ps1 PowerShell脚本的内容添加到我们已经涵盖的ADHealthCheck-NoResult.ps1 PowerShell脚本的末尾。
  5. 将变量$PRTGUrl更改为您的PRTG传感器的URL和端口。
  6. 执行该脚本。

一旦完成,AD健康检查脚本完成后,它现在应该像下面显示的那样将状态推送到您的PRTG传感器。

PRTG sensors showing AD health

安排执行Active Directory健康检查脚本

监视AD健康是一个持续的过程。您应该始终运行测试,而不是临时实例。让我们安排Active Directory健康检查脚本以在频繁的间隔运行。

自动化这些检查的最简单方法是将脚本添加到任务计划程序,并让其在AD用户帐户或组托管服务帐户下运行。

使用组托管服务帐户(gMSA)是执行定期任务的更安全方式,因为只有指定的计算机帐户可以从AD获取密码。但是一些组织可能没有这种奢侈。

创建AD用户帐户

让我们首先分解如何设置AD用户帐户来运行计划任务。

如果您要以用户帐户身份运行定期任务,请务必不要使用您自己的帐户运行!请始终为此目的创建一个单独的用户帐户。

为了节省时间,您将在下面看到一个 PowerShell 脚本。这是一个示例脚本,您可以使用它来创建一个属于Domain Admins组的 AD 用户帐户。然后,您可以使用此帐户来运行定期任务。

# 将此更改为您的服务帐户的 OU
$OU = "OU=Service Accounts,DC=contoso,DC=com"
# 将此更改为您要用于该帐户的密码
$Password = "JägareTvå"
$SecureString = $Password | ConvertTo-SecureString -AsPlainText -Force
New-ADUser -Enabled $True -Path $OU -Name svcADHealthCheck -AccountPassword $SecureString
# 限制帐户仅限于域控制器
$DomainControllers = (Get-ADDomainController -Filter *).Name

Set-ADAccount -Identity svcADHealthCheck -LogonWorkstations ($DomainControllers -Join ",")

# 将其设为域管理员(抱歉)
Add-ADGroupMember -Identity "Domain Admins" -Members svcADHealthCheck

创建组管理的服务帐户

如果您的环境中尚未使用 gMSA,使用 gMSA 运行健康检查会有些棘手,但安全性更高。

创建 KDS 根密钥

要创建一个用于运行 AD 健康检查脚本的 gMSA 帐户,请首先添加一个KDS 根密钥,如果您还没有的话。您可以通过在域控制器上运行 PowerShell 命令Get-KDSRootKey来检查是否有 KDS 根密钥。

如果您没有KDS根密钥,可以通过在2012R2或更高版本的域控制器上,以属于Domain Admins AD组的用户帐户身份运行Add-KDSRootKey -EffectiveImmediately来创建一个。

密钥需要复制到其他域控制器以充分生效。有关此过程的更多信息,请参阅Microsoft文档

创建gMSA

一旦创建了KDS根密钥,您就可以使用PowerShell创建gMSA帐户。下面是一个示例脚本,用于创建只允许从Domain Admins组的域控制器进行身份验证的gMSA帐户。

# 切换到您的域
$Domain = "contoso.com"

$AccountName = "svcadhealthcheck"

# 创建名为svcadhealthcheck(或实际上是svcadhealthcheck$)的GMSA,并允许只有域控制器可以获取密码
New-ADServiceAccount $AccountName -DNSHostName "$AccountName.$Domain" –PrincipalsAllowedToRetrieveManagedPassword "Domain Controllers"

# 将GMSA添加到Domain Admins
# 请注意,我们通过真实的SamAccountName 'svcadhealthcheck$'添加帐户
Add-ADGroupMember -Identity "Domain Admins" -Members "$AccountName`$"

安装和测试gMSA

现在已经创建了 gMSA,最后一步是在所有域控制器上安装和测试它。 一种方法是使用 Invoke-Command PowerShell 命令。 下面您可以看到一个 PowerShell 脚本,将在所有 DC 上安装 gMSA 并确保其正常工作。

# 这将在所有域控制器上运行
Invoke-Command -ComputerName (Get-ADDomainController -Filter *).Name -ScriptBlock {
    $Account = Get-ADServiceAccount -Filter { Name -eq 'svcadhealthcheck'}
    Install-ADServiceAccount $Account

    # 测试 GMSA 是否在计算机上正常工作
    # 如果测试正常,则返回 $True
    $Test = Test-ADServiceAccount -Identity $Account.Name
    if($Test){
        Write-Output "GMSA test OK on $env:computername"
    }
    else {
        Write-Output "GMSA test FAILED on $env:computername"
    }

}

给 gMSA 授予批处理作业权限

安装了 gMSA 后,现在您需要在 DC 上授予其批处理作业的运行权限。 由于它将在后台作为计划任务自主运行,因此该帐户需要此权限。

您可以通过现有的 GPO 或创建一个新的 GPO 并将其链接到 域控制器 OU 来设置此权限。 如果您还没有要使用的 GPO,下面是一些创建一个的步骤。

  1. 在 DC 上启动 Group Policy Editor
  2. 右键单击 Domain Controllers OU,然后选择 在此域中创建 GPO 并将其链接到此处
  3. 将其命名为 DC – Logon as batch 或您喜欢的其他名称
  4. 右键单击 GPO,然后单击 编辑
  5. 转到 计算机配置 –> Windows 设置 –> 安全设置 –> 用户权限分配
  6. 左键单击登录为批处理作业,然后单击属性
  7. 点击添加用户或组。
  8. 点击对象类型,仅选择服务帐户,然后点击确定
  9. 搜索之前创建的svcADHealthCheck服务帐户,选择它,然后点击确定

现在您应该在下面所示的AD对象列表中看到gMSA。

gMSA given rights to logon as a batch job

创建计划任务

现在您已经创建了一个用于运行计划任务的帐户,您现在可以在您选择的域加入服务器上创建计划任务本身。

您可以通过图形用户界面创建计划任务,但那样太费事了!相反,我建议使用PowerShell来创建它。为什么?因为您可以简单地复制下面看到的代码并完成。

下面您将找到两个脚本;两个脚本都相似,但一个假设是AD用户帐户,另一个假设是gMSA。请务必根据您使用的帐户使用适当的脚本。

# 替换为您脚本的路径
$ScriptPath = "C:\Scripts\ADHealthCheck.ps1"
# 替换为您创建用于运行任务的帐户的用户名
$UserName = "svdADHealthCheck"

# 替换为上述帐户设置的密码
$Password = "JägareTvå!"
# 创建启动脚本的操作
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy bypass -File '$ScriptPath'"
# 创建启动任务的触发器
$Trigger = New-ScheduledTaskTrigger -Once -At "12:00" -RepetitionInterval (New-TimeSpan -Hours 12)
# 创建调度任务的设置
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd
# 使用分隔符创建计划任务以提高可读性
$Splat = @{
    User = "$env:USERDOMAIN\$UserName"
    Password = $Password
    TaskName = "ADHealthCheck"
    Action = $Action
    Trigger = $Trigger
    RunLevel = "Highest"
    Settings = $Settings
}
Register-ScheduledTask @Splat
# 替换为您脚本的路径
$ScriptPath = "C:\Scripts\ADHealthCheck.ps1"
# 替换为您创建用于运行任务的帐户的用户名
$UserName = "svdADHealthCheck$"
# 创建启动脚本的操作
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy bypass -File '$ScriptPath'"
# 创建启动任务的触发器
$Trigger = New-ScheduledTaskTrigger -Once -At "12:00" -RepetitionInterval (New-TimeSpan -Hours 12)
# 创建调度任务的设置
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd

# 创建定义GMSA的主体
$Principal = New-ScheduledTaskPrincipal -UserID "$env:USERDOMAIN\$UserName" -LogonType Password -RunLevel Highest
# 使用分隔符创建计划任务以提高可读性
$Splat = @{
    Principal = $Principal
    TaskName = "ADHealthCheck"
    Action = $Action
    Trigger = $Trigger
    RunLevel = "Highest"
    Settings = $Settings
}
Register-ScheduledTask @Splat

完成!此时,计划任务将按照上述其中一个脚本中提供的间隔执行。

总结

哇!如果你从第一部分一直跟着走,现在应该知道 AD 健康是一个深入的主题。这个话题有很多内容,甚至两篇长篇博文都无法涵盖。

但是,现在你应该已经掌握了足够的知识和一个预先构建的 PowerShell 框架,以便在需要时插入其他 Active Directory 健康检查。

进一步阅读

Source:
https://adamtheautomator.com/active-directory-health-check-2/