![如何在 Powershell 中创建在事件发生时激活的 Windows 任务](https://linux22.com/image/1542670/%E5%A6%82%E4%BD%95%E5%9C%A8%20Powershell%20%E4%B8%AD%E5%88%9B%E5%BB%BA%E5%9C%A8%E4%BA%8B%E4%BB%B6%E5%8F%91%E7%94%9F%E6%97%B6%E6%BF%80%E6%B4%BB%E7%9A%84%20Windows%20%E4%BB%BB%E5%8A%A1.png)
嘿,我需要创建一个 Windows 任务,其作用与组策略中的注销脚本非常相似(我们的环境没有 AD)。该任务需要以登录用户的身份运行,以便脚本可以将运行它的用户的名称输出到跟踪用户注销/登录时间和日期的 .csv 中。我不确定 powershell 是否是执行此操作的绝对最佳方式,但我需要能够以管理员身份编写任务,但将用户设置为以当前登录用户的身份运行脚本,无论当时是谁。这将部署到几百台机器上。
如果有其他方法可以跟踪用户注销,我会对该方法非常感兴趣,无论它是否涉及 powershell。以前我曾尝试通过编辑注册表来运行脚本,以尝试在 GP 中设置注销脚本,但这似乎比我最初预期的要复杂得多。Zenworks 是另一种选择,但它只在用户注销后运行脚本,这导致脚本将错误的结果输出到 .csv 中。
作为参考,下面的链接包含有类似问题的人,但没有我需要针对的一些具体信息。
答案1
编辑:由于问题更新,答案进行了重大修改。
对于通用注销脚本来说,这仍然是一个坏主意 - 请参阅这个问题以解释原因。
您需要查看的关键参数是/MO
。这是修饰符。根据 Microsoft 文档任务列表,当/SC
参数为时ONEVENT
,则/MO
允许 XPath 事件查询字符串。
本 Technet 博客 - Windows 事件查看器中的高级 XML 过滤- 描述格式并提供有用的示例。
根据您对数据的实际操作,您实际上不需要在用户注销的上下文中执行。这很好,因为它避免了在注销过程中尝试启动新进程所固有的潜在竞争条件。它还简化了您的 XPath 查询:
*[System[EventID=4647]]
您真正需要做的就是确保从此事件触发的任务收到有关触发它的事件的详细信息。计划任务存储为 xml 文件,您可以手动编辑 XML 以添加未通过 UI 公开的任务计划程序 XML 架构的其他元素。我建议您先使用 SCHTASKS 或通过 UI 使用您可以指定的所有值创建一个任务。然后出口通过 UI 或以下方式:
schtasks /Query [/S <system> [/U <username> [/P [<password>]]]] `
/XML /TN <taskname> >event.xml
Technet 文章从 Windows 事件触发 PowerShell 脚本提供了一个很好的解释和如何使用它的示例。然而,他们添加的关键属性是值查询:
<ValueQueries>
<Value name="eventChannel">Event/System/Channel</Value>
<Value name="eventRecordID">Event/System/EventRecordID</Value>
<Value name="eventSeverity">Event/System/Level</Value>
</ValueQueries>
您不应该像他们演示的那样使用 EventRecordID 来查询事件,而应该能够直接提取所需的有限数据:
<Value name="logoffUsername">Event/EventData/Data[@Name='TargetUserName']</Value>
您可能还想引入 TimeCreated,因为它应该比从脚本中调用的时间函数更可靠、更准确:
<Value name="eventTime">Event/System/TimeCreated/@SystemTime</Value>
这将允许您引用$(logoffUsername)
(可选)$(eventTime)
任务执行的 Powershell 脚本。
如果你想引入其他值,事件 XML 模式的定义如下在 MSDN 上。我还参考了Michael Albert 关于传递事件参数的博客文章语法示例。
修改完任务定义后,您可以进口所有计算机上的相同 XML:
schtasks /Create [/S <system> [/U <username> [/P [<password>]]]]
/XML /TN
注意:原始问题要求在用户上下文中为该用户创建一个事件,并且仅在该用户注销时运行该事件。由于上述潜在的竞争条件,这是一个坏主意。作为参考,这是提供的解决方案。
$cred = Get-Credential
$password = $cred.GetNetworkCredential().Password
$command = <your command>
$myQuery = "*[System[EventID=4647] and EventData[Data [@Name='TargetUserName'] = '" + $env:username + "']]"
SCHTASKS /Create /TN "Logoff Monitor" /TR $command /SC ONEVENT `
/RL Highest /RU $cred.Username /RP $password `
/EC ScriptEvents /MO $myQuery
答案2
在尝试使用 taskscheduler 完成这项工作后,我最终得出结论,我最初的想法不会像我最初希望的那样简单可靠地实现。放弃使用 taskscheduler,但仍然保留使用 powershell 的想法,我编写了一个 powershell 脚本,zenworks 可以在用户注销时运行该脚本,以跟踪我认为相关的详细信息。该脚本可以在这里找到(由于兼容性问题,它是一个通过批处理文件运行的 powershell 脚本):
@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.
@@:: Any non-PowerShell content must be preceeded by "@@"
@@setlocal
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF
if( -Not (Test-Path -Path C:\Windows\Logs\$env:computername.csv ) )
{
New-Item -Path C:\Windows\Logs\ -name "$env:computername.csv" -itemtype "file"
}
$events = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4647} -MaxEvents 1
foreach ($event in $events)
{
#Writes events to XML file
$eventXML = [xml]$event.ToXml()
#Converts Datetime of event into Two entries
$DateTime = '{0:MM/dd/yyyy hh:mm:ss}' -f ($event.timecreated)
$date = ($DateTime -split ' ')[0]
$time = ($DateTime -split ' ')[1]
#Formats Outputs of data to prep for export
$NewLine = "{0},{1},{2},{3},{4}" -f
"Logoff",
$env:computername,
$eventXML.Event.EventData.Data[1].'#text',
$Date,
$Time
#Exports Data to .csv
$NewLine | add-content -path C:\Windows\Logs\$env:computername.csv
}