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

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

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

使用 CIM 和 PowerShell 构建 Active Directory 观察程序


[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

上周,Adam Bertram 发布了一条推文,寻找任何 PowerShell 代码,以便在创建新的 Active Directory 用户帐户时通知用户。我挖出了一些非常非常古老的代码,这些代码使用 WMI 事件订阅来监视 Active Directory 中的此类事件。我分享的代码是我在 PowerShell v2 时代编写的代码,但今天仍然有效。然而,它使用了 Register-WMIEvent,在当今的 PowerShell 世界中我认为已弃用。我们应该使用较新的 CIM 等效 cmdlet。我决定花一点时间完善代码,使其达到(我的)当前标准。

WMI/CIM 事件订阅

在 Windows 世界中,当发生某些事情(例如服务启动或进程结束)时,我们会收到一个事件。在 Active Directory 中,创建新用户帐户也会触发事件。在许多情况下,您可以使用 WMI/CIM 监视这些事件。您可以通过创建临时事件订阅来实现此目的。这就像任何其他订阅一样。你是说,“当有新的事情要报告时,给我发送一个新的问题。”您可能每两周或每 2 分钟收到一个新“问题”。

您将使用 Register-CimInspirationEvent 来创建订阅。您可以在本地计算机或远程计算机上创建事件订阅。此订阅仅在您的 PowerShell 会话期间持续。但请注意,除了小型环境之外,我会谨慎围绕它构建完整的管理系统。虽然很高兴知道何时发生服务停止或用户帐户被修改等情况,但在较大的环境中,您最好投资专为此类监控而设计的工具和软件。我会使用临时事件订阅来进行短期监控或故障排除。考虑到这一点,请将本文中的代码视为概念验证。

注册-CIMInductionEvent

我相信您会花时间阅读此 cmdlet 的完整帮助和示例。 WMI 事件是一个复杂的主题,我不会深入探讨。使用 cmdlet 的方法有多种。我将使用老式查询。

Register-CimIndicationEvent -Query "Select * from __InstanceCreationEvent Within 10 where TargetInstance ISA 'DS_USER'" -Namespace root\directory\ldap -SourceIdentifier NewUser

我在域成员 Windows 10 桌面上运行此代码。该查询表示:“每 10 秒检查一次创建事件,其中对象类型是用户帐户。” 内部部分称为轮询。您不想太快进行民意调查。如果您确实需要在几秒钟内知道事件发生的时间,则需要找到真正的软件解决方案。我的演示用了 10 秒。

这将创建一个事件订阅。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

SourceIdentifier是您引用订阅者和后续事件的方式。如果您不指定,您将获得一个 GUID。就我个人而言,我发现定义自己的标识符要容易得多。

事件订阅者在我的本地 PowerShell 会话中运行,每 10 秒检查一次域中的新用户创建事件。我将创建一个新的域用户。 10 秒后,我的会话中没有发生任何事情。我们稍后会改变这一点。相反,我将运行 Get-Event。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

有关用户的信息隐藏在事件中。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

我稍后会回到这个对象。

注册-ADWatcher

考虑到这些基础知识,我编写了一个包含 Register-CimInductionEvent 的 PowerShell 函数。该函数允许我监视不同类别的 Active Directory 对象的不同类型的事件。

Function Register-ADWatcher {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(Mandatory, HelpMessage = "What type of object do you want to watch?")]
        [ValidateSet("User", "Group", "Computer", "OU")]
        [string]$Category,
        [Parameter(Mandatory, HelpMessage = "What type of activity watch?")]
        [ValidateSet("Create", "Modify", "Remove")]
        [string]$Activity,
        [Parameter(HelpMessage = "How often do you want to poll? Enter a value in seconds. The minimum and default is 10.")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ $_ -ge 10 })]
        [int]$Poll = 10,
        [Parameter(HelpMessage = "Specify an action scriptblock when the event fires.")]
        [scriptblock]$Action,
        [Parameter(HelpMessage = "Specify additional data to associate with the watcher.")]
        [string]$MessageData,
        [Parameter(HelpMessage = "Specify a name for the watcher")]
        [string]$SourceIdentifier,
        [parameter(HelpMessage = "Specify the name of a computer where you want to create the watcher.")]
        [string]$Computername,
        [switch]$Passthru
    )

    Write-Verbose "Starting $($myinvocation.mycommand)"
    #remove bound parameters that don't belong to Register-CimIndicationEvent
    $drop = "Category", "Activity", "Poll", "WhatIf", "Confirm","Passthru"
    foreach ($item in $drop) {
        if ($PSBoundParameters.ContainsKey($item)) {
            [void]$PSBoundParameters.Remove($item)
        }
    }

    Switch ($Category) {
        "User" { $DS = "DS_USER" }
        "Group" { $DS = "DS_GROUP" }
        "Computer" { $DS = "DS_COMPUTER" }
        "OU" { $DS = "DS_ORGANIZATIONALUNIT" }
    }

    Switch ($Activity) {
        "Create" { $DSAction = "__InstanceCreationEvent" }
        "Modify" { $DSAction = "__InstanceModificationEvent" }
        "Remove" { $DSAction = "__InstanceDeletionEvent" }
    }

    $query = "Select * from $DSAction Within $Poll where TargetInstance ISA '$DS'"
    $PSBoundParameters.Add("Query", $query)
    $PSBoundParameters.Add("Namespace", "root\directory\ldap")

    Write-Verbose "Creating an AD Watcher with these parameters"
    $PSBoundParameters | Out-String | Write-Verbose

    $target = "$Activity $Category within $Poll seconds"
    If ($pscmdlet.ShouldProcess($target.toUpper(), "Register AD Watcher")) {
        [void](Register-CimIndicationEvent @PSBoundParameters)
        if ($Passthru) {
            Start-sleep -Seconds 2
            Get-EventSubscriber | Sort-Object -property SubscriberID | Select-Object -last 1
        }
    }
    Write-Verbose "Ending $($myinvocation.mycommand)"
}

我将使用此功能来监视新群组。

Register-ADWatcher -Category Group -Activity Create -MessageData "A new group has been created" -SourceIdentifier "NewGroup"

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

我最终将替换新的用户订阅,因此我将删除当前的订阅。

Get-EventSubscriber newuser | Unregister-Event

现在创建一个新组并查看该事件。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

辅助函数

我知道有关该组的信息就在那里,但我懒得解析该对象。相反,我将编写一个辅助函数。

Function Get-TargetInstance {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$EventResult,
        [Parameter(HelpMessage = "Only show defined properties")]
        [switch]$ShowDefined
    )

    Process {
        if ($ShowDefined) {
            $t = $EventResult.SourceEventArgs.NewEvent.TargetInstance
            $t.psobject.Properties.where({ $_.name -match "^DS|ADSIPath" -AND $_.value }) |
            ForEach-Object -Begin {
                #create a temporary ordered hashtable
                $h = [ordered]@{
                    TimeGenerated = $EventResult.timeGenerated
                    EventClass    = $EventResult.SourceEventArgs.NewEvent.CimSystemProperties.Classname
                }
            } -Process {
                #add each property that has a value to the hashtable
                $h.add($_.name, $_.value) } -End {
                #Create a custom object from the hashtable
                New-Object -TypeName PSObject -Property $h
            }
        }
        else {
            $EventResult.sourceEventArgs.NewEvent.TargetInstance
        }
    }
}

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

因为我知道该对象将具有许多未定义或 null 属性值,所以我添加了一个参数以仅显示填充的属性。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

行动

从这个角度来看,我发现我对组名的修改是错误的。完美的。让我们创建一个观察器来监视组对象的更改。我还可以向您展示如何添加操作。这是事件触发时运行的脚本块。我将使用 BurntToast 模块(您可以从 PowerShell 库安装该模块)来向我显示通知。

Register-ADWatcher -Category Group -Activity Modify -SourceIdentifier "GroupChange" -Action { New-BurntToastNotification -Text "A group has changed"}

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

但使用动作时需要权衡。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

没有事件对象。好吧,我应该说,没有对象写入我的控制台。我可以在我的动作脚本块中引用一个对象。但首先让我展示那是什么。

这是更改用户对象的观察者。

Register-ADWatcher -Category User -Activity Modify -SourceIdentifier UserChange -Passthru

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

当更改事件触发时,您会得到一个“之前”和“之后”对象。您自然想知道发生了什么变化。我尝试使用 Compare-Object 这看起来是合适的工具。但除非我指定一个属性,否则它不会给我结果。所以我写了自己的比较函数。

Function Compare-DSObject {
    #get-event -sourceIdentifier changeduser | Compare-DSObject

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$EventResult
    )

    Process {
        #write-host "Processing an event" -ForegroundColor Green
        $p = $EventResult.SourceEventArgs.NewEvent.PreviousInstance
        $t = $EventResult.SourceEventArgs.NewEvent.TargetInstance
        $prop = $p.psobject.Properties.name.where( { $_ -match "^DS" })
        Write-Verbose "Processing Changes to $($t.ADSIPath)"
        foreach ($item in $prop) {
            if (Compare-Object -ReferenceObject $p -DifferenceObject $t -Property $item) {
                [pscustomobject]@{
                    PSTypeName    = "DSComparison"
                    ADSIPath      = $t.ADSIPath
                    TimeGenerated = $eventResult.TimeGenerated
                    Property      = $item
                    PreviousValue = $p.$item
                    NewValue      = $t.$item
                }
            }
        }
    } #process
}

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

正如您所看到的,这实际上取决于您在事件触发时想要执行的操作或看到的内容。

在操作脚本块中,您可以使用内置的 $event 对象引用事件对象。这是新用户帐户的观察程序,它将使用 BurntToast 并显示新对象的可分辨名称。

$action = { 
 $toast = @{
  Text = "$($event.MessageData) $($event.SourceEventArgs.NewEvent.TargetInstance.DS_distinguishedName)" 
  Sound = "Alarm5"
 }
 New-BurntToastNotification @toast
 }
 $watch = @{
  Category = "User"
  Activity = "Create"
  MessageData = "A domain user account has been created." 
  SourceIdentifier = "NewUser" 
  Action = $Action
 }
 Register-ADWatcher @watch

MessageData 的值将在通知中使用。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

把它们放在一起

让我将所有内容放在一起来结束这篇文章。我们来看一个删除事件。我想在我的小域中保留审计跟踪。当用户被删除时,我想将事件信息保存到磁盘并获取我的 toast 通知。在我的操作脚本块中,我将使用 Export-CliXML 将事件导出到磁盘。

$action = {
     #export event to disk
     if (-not (Test-Path C:\ADWatch)) {
         New-Item -Path C:\ -Name ADWatch -ItemType Directory
     }
     $file = "{0}_{1}.xml" -f $event.sourceIdentifier.Replace(" ",""),($event.TimeGenerated.tostring("u").replace(" ","-").replace(":",""))
     $export = Join-Path -Path C:\ADWatch -ChildPath $file
     $event | Export-Clixml -Path $export
     $ti = $event.SourceEventArgs.NewEvent.TargetInstance
     $msg = "[$($event.TimeGenerated)] $($event.messagedata) $($ti.DS_DistinguishedName) See $export for details."
     $toast = @{
         Header = New-BTHeader -Title "AD Watcher"
         Text =  $msg
         SnoozeAndDismiss = $True
         AppLogo = "C:\scripts\db.png"
         Sound = "alarm10"
     }
     New-BurntToastNotification @toast
 }
 Register-ADWatcher -Category User -Activity Remove -Action $action -SourceIdentifier "DeleteUser" -MessageData "ALERT! A domain user account has been removed." -Verbose

已删除用户的目标实例将序列化为 C:\ADWatch 中的 XML 文件。我正在创建 UTC 时间戳作为文件名的一部分。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

警报消息指示可以轻松带回 PowerShell 的文件。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

我可以使用我的辅助功能来查看已删除的用户信息。

[玩转系统] 使用 CIM 和 PowerShell 构建 Active Directory 观察程序

概括

正如您所看到的,当 Active Directory 中发生事件时,可以收到通知。但仅仅因为您可以做到这一点,并不意味着应该。还有很多其他选项是更好的选择,尤其是更接近您需要的实时通知的选项。我想说这还取决于您需要如何处理这些信息。您只是需要知道还是必须发生某些事情?

为了避免我肯定会遇到的一个问题,我不知道这是否或如何与 Azure AD 一起使用。拥有 Azure AD 环境的人必须让我知道。

但对于本地事件,无论是在 Active Directory 还是其他地方,使用 CIM 事件都可以成为 PowerShell 工具箱中的有用工具。

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

取消回复欢迎 发表评论:

关灯