当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 构建用于 Active Directory 更改的 PowerShell 工具

作者:精品下载站 日期:2024-12-14 08:01:33 浏览:19 分类:玩电脑

构建用于 Active Directory 更改的 PowerShell 工具


[玩转系统] 构建用于 Active Directory 更改的 PowerShell 工具

几天前,我发布了一个我编写的 PowerShell 脚本,该脚本创建一个格式化的 HTML 报告,其中包含可折叠区域,其中显示了 Active Directory 域中对象的最新更改。包括已删除的对象,假设您启用了 Active Directory RecycleBin 功能。我对结果很满意,你们中的许多人发现它很有用,至少值得一看。然而,我现在意识到我的过程倒退了。确实,HTML 生成脚本基于 PowerShell 代码,我可以交互运行该代码来显示 Active Directory 更改。但我应该走得更远。我应该构建一个 PowerShell 工具,它可以通过 PowerShell 控制台提示符向我显示相同的更改。我在原来的文章中暗示过这一点。我编写的脚本很有用,但它只能完成其设计目的——创建 HTML 报告。以下是我如何纠正我的疏忽。

从 PowerShell Cmdlet 开始

与任何 PowerShell 工具制作项目一样,您希望从一个 cmdlet 和一个 PowerShell 表达式或一组表达式开始,您可以交互运行它们以获得所需的结果。或者至少有足够的结果可以修饰。我使用上一个脚本中的 Get-ADObject 获得了该代码。但是 HTML 报告脚本只能做一件事,我希望我的命令行工具更加灵活。

除了支持一些 Get-ADObject 参数(例如 Credential、Server 和 SearchBase)之外,我还希望用户能够指定对象的类型。也许我现在只需要查看组对象,但明天我想查看用户和计算机。我需要一个这样的参数:

[Parameter(HelpMessage = "Specify the types of objects to query.")]
[ValidateSet("User","Group","Computer","OU")]
[ValidateNotNullOrEmpty()]
[string[]]$Category = "User",

Active Directory 对象有多种类型,但这些是我想要定期查看的对象。请注意 [ValidateSet()] 的使用。这些是唯一可能的值,PowerShell 将以制表符完成它们。请记住,如果您指定默认值,它将包含在验证集中。

我还考虑了我想看什么房产。您不限于原始对象。我有一个我想要从 Get-ADObject 命令获得的内容的列表。但我还想包含域控制器的名称。根据域控制器复制的不同,您可能会得到不同的结果,因此我想捕获域控制器。我在输出中找不到向我显示这一点的任何地方,因此我决定为服务器设置一个默认参数值,以便我始终有可用的东西。

[Parameter(HelpMessage = "Specifies the Active Directory Domain Services domain controller to query. The default is your Logon server.")]
[alias("DC")]
[ValidateNotNullorEmpty()]
[string]$Server = $env:LOGONSERVER.SubString(2)

环境变量使用类似 \\DOM1 的格式,但 Get-ADObject 期望 DOM1 的服务器值,因此我的默认值解析环境值。我本可以强制使用该参数,但这感觉有点矫枉过正。尽管我可以将 ArgumentCompleter 添加到 Server 参数中。

[ArgumentCompleter({(Get-ADDomain).ReplicaDirectoryServers})]

在小域中,这可能是一个不错的选择。

LDAP 查询

我的 HTML 报告脚本使用单个过滤器来获取自给定日期时间以来修改的所有对象。但我的新 PowerShell 工具预计会更加精细。我开始尝试根据函数中的参数值动态构建一个复杂的 Get-ADObject 过滤表达式。这很快就失控了。此外,Active Directory 中有一个我已经忘记的怪癖。我的早期版本为用户和计算机返回更改的对象,即使我只搜索用户。这是因为计算机对象是从用户对象派生的并且搜索后者包括前者。

我的解决方案是退回到 LDAP 过滤器,结果证明这是一件好事。现在我可以有一个过滤器来准确返回我正在寻找的对象类型。然而,包括对日期时间值的过滤又增加了另一个问题。我无法包含“WhenChanged >= ‘1/28/2021 2:00PM’”之类的内容。我需要将日期时间值转换为格式为 yyyyMMddhhmmss.ff 的字符串,后跟时区偏移量(如 -0400)。我编写了一个私有辅助函数来重新格式化日期时间值。

Function _ConvertToLDAPTime {
        #a private helper function to convert a date time object into a LDAP query-compatible value
        Param([datetime]$Date)
        $offset = (Get-TimeZone).baseUtcOffset
        #values must be formatted with leading zeros to the specified number of decimal places
        $tz = "{0:d2}{1:d2}" -f $offset.hours,$offset.Minutes
        "{0:yyyyMMddhhmmss}.0{1}" -f $date,$tz
}

我使用非标准名称,因为该函数不会向用户公开,并且仅在父函数中调用。

$dt = _ConvertToLDAPTime -Date $Since

在我的函数中,我将使用 LDAP 过滤器循环遍历每个对象类。

foreach ($objClass in $Category) {
        Switch ($objclass) {
            "User"     { $ldap = "(&(WhenChanged>=$dt)(objectclass=user)(!(objectclass=computer)))" }
            "Computer" { $ldap = "(&(WhenChanged>=$dt)(objectclass=computer))"}
            "Group"    { $ldap = "(&(WhenChanged>=$dt)(objectclass=group))"}
            "OU"       { $ldap = "(&(WhenChanged>=$dt)(objectclass=organizationalunit))"}
        }
        Write-Verbose "[$(Get-Date)] Using LDAP filter $ldap"
        $getparams["LDAPFilter"] = $ldap
        Get-ADObject @getParams | Foreach-Object { $items.Add($_)}
}

创建新对象

结果保存到列表对象中,然后对其进行处理并转换为类型名为 ADChange 的新自定义对象。

foreach ($item in $items) {
    if ($item.WhenCreated -ge $since) {
        $isNew = $True
    }
    else {
        $isNew = $false
    }
    #create a custom object based on each search result
    [PSCustomObject]@{
        PSTypeName        = "ADChange"
        ObjectClass       = $item.ObjectClass
        ObjectGuid        = $item.ObjectGuid
        DistinguishedName = $item.DistinguishedName
        Name              = $item.Name
        DisplayName       = $item.DisplayName
        Description       = $item.Description
        WhenCreated       = $item.WhenCreated
        WhenChanged       = $item.WhenChanged
        IsNew             = $IsNew
        IsDeleted         = $item.Deleted
        Container         = $item.distinguishedname.split(",", 2)[1]
        DomainController  = $Server.toUpper()
        ReportDate        = $ReportDate
    }
} #foreach item

为什么要做出这样的努力?这样我就可以自定义对象以使其易于在管道中使用。例如,我可以定义一些别名属性以及一组默认属性。

Update-TypeData -TypeName ADChange -DefaultDisplayPropertySet DistinguishedName,WhenCreated,WhenChanged,IsNew,IsDeleted,ObjectClass,ReportDate -Force
#define some alias properties for the custom object
Update-TypeData -TypeName ADChange -MemberType AliasProperty -MemberName class -Value ObjectClass -Force
Update-TypeData -TypeName ADChange -MemberType AliasProperty -MemberName DN -Value DistinguishedName -Force

我还可以创建自定义格式文件,这样我不仅可以获得我想要的默认格式化视图,而且还可以包含其他视图。

Update-FormatData $PSScriptRoot\ADchange.format.ps1xml

[玩转系统] 构建用于 Active Directory 更改的 PowerShell 工具

[玩转系统] 构建用于 Active Directory 更改的 PowerShell 工具

[玩转系统] 构建用于 Active Directory 更改的 PowerShell 工具

我在 PowerShell 工作中一直使用详细输出。在此函数中,我包含了我所说的运行时元数据。

[玩转系统] 构建用于 Active Directory 更改的 PowerShell 工具

我们的想法是,如果有人在运行命令时遇到问题,我可以让他们启动脚本,使用 -Verbose 运行命令,停止脚本并将其发送给我。希望详细的输出可以帮助我隔离问题。

获取代码

现在你们中的一些人正在寻找其余的代码。因为这是我可能会不断修改的东西,所以我在 Github 上发布了 Get-ADChange 函数和格式文件。转到 https://gist.github.com/jdhitsolutions/3bce157bd64717dd616b949f6e280433 并获取这两个文件。这些文件应位于同一文件夹中,并确保格式文件保存为 adchange.format.ps1xml 。然后您可以点源脚本文件:

. c:\scripts\get-adchange.ps1

显然使用适当的路径。该脚本还将格式文件加载到您的 PowerShell 会话中。您应该能够从安装了 ActiveDirectory 模块的 Windows 10 桌面运行 Get-ADChange 命令。

也就是说,此代码按原样提供,不提供任何保证或支持。我强烈鼓励您在非生产环境中进行彻底测试。

概括

我可以返回 HTML 报告脚本并修改它以使用这个新函数。或者我可能会围绕它构建其他控制器脚本。让我重申一下,如果您拥有更大的 Active Directory 基础设施,那么投资真正的管理和报告解决方案将会为您提供更好的服务。许多读者希望能够发现谁可能做出了改变。该信息不与 Active Directory 对象一起存储。这就是审计和日志记录发挥作用的地方,也是“真正的”管理产品值得投资的地方。

尽管如此,我还是希望您能从中了解一些有关构建 PowerShell 工具的过程。一如既往,欢迎提出问题和意见。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯