我正在尝试编写一个 PowerShell 脚本来查看特定任务的服务器任务调度程序日志。我想知道是否存在任何错误或任何异常情况。
通常,我会看到事件 ID 、、、、107
以及作业成功运行时的情况。我只想知道何时发生其他事情,例如错误、警告等。100
129
200
201
102
最后,我想为名为“ ”的任务查找除上述事件 ID 之外的事件 ID Batch001
。如果出现任何其他事件 ID,请向我发送电子邮件。
- 我的电子邮件发送 PowerShell 代码运行良好——如果仅作为部分运行的话。但是我需要弄清楚电子邮件代码的搜索、过滤和触发。
下面的脚本查看特定任务的日志,并将过去 24 小时内相应的全套事件附加到文本文件(一种审计日志)。这部分也正常工作。如果连接服务器时出现问题,那么它应该将一条消息写入错误文本文件并通过 Office 365 将该内容通过电子邮件发送给我。
我无法让此逻辑在发出警告时触发电子邮件。我尝试通过启动批处理作业来模拟失败/错误/警告作业,并在其运行时让计划尝试启动另一个作业,从而对此进行测试。这会导致出现警告,提示无法启动任务,因为有另一个实例正在运行(code 322
)。我需要捕获此信息并通过电子邮件发送,但我无法让它工作。
我的 PowerShell 脚本
Start-Transcript -Path "C:\BatchLogs\BatchTranscripts.txt"
$task_name = "Batch001"
$file = "C:\BatchLogs\Batch001.txt"
$file_path = Test-Path $file
$pc = $_
#If file hasn't been created, create it
If ($file_path -ne $true){
New-Item C:\BatchLogs\Batch001.txt
}
#Filter xml to pull task scheduler events
try {
invoke-command -ComputerName "servername.domain" -ErrorAction Stop -ScriptBlock {
try {
$events = @(
Get-WinEvent -FilterXml @'
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">
*[EventData/Data[@Name='TaskName']='\Batch001'] and *[System[TimeCreated[timediff(@SystemTime) <= 86400000]]]
</Select>
</Query>
</QueryList>
'@ -ErrorAction Stop
)
}
catch {
Write-Warning -Message "Failed to query $($env:computername) because $($_.Exception.Message)" *> "C:\BatchLogs\errors.txt"
Get-Content -Path "C:\BatchLogs\errors.txt" | Add-Content -Path "C:\BatchLogs\Batch001.txt"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$EmailTo = "[email protected]"
$EmailFrom = "[email protected]"
$Subject = "Batch001 Failure"
$Body = Get-Content -Path "C:\BatchLogs\errors.txt"
$SMTPServer = "smtp.office365.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("[email protected]", "Password123");
$SMTPClient.Send($SMTPMessage)
}
if ($events) {
$events | Select MachineName,TimeCreated,Id,TaskDisplayName | Out-File -Append C:\BatchLogs\Batch001.txt
}
} # end of scriptblock
} catch {
Write-Warning -Message "Failed to contact $pc because $($_.Exception.Message)" *> "C:\BatchLogs\errors.txt"
Get-Content -Path "C:\BatchLogs\errors.txt" | Add-Content -Path "C:\BatchLogs\Batch001.txt"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$EmailTo = "[email protected]"
$EmailFrom = "[email protected]"
$Subject = "Batch001 Failure"
$Body = Get-Content -Path "C:\BatchLogs\errors.txt"
$SMTPServer = "smtp.office365.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("[email protected]", "Password123");
$SMTPClient.Send($SMTPMessage)
}
Stop-Transcript
答案1
可能有帮助/澄清的 PowerShell 代码
首先,你做得非常好将逻辑拼凑在一起,几乎可以得到您所需的一切,干得好。由于您精通 PowerShell,以下逻辑应该可以帮助您 100% 巩固您的首选解决方案。
调整
将非错误/警告事件 ID 数值放入数组中,$notin
这些数值不应触发任何电子邮件警告或错误通知。我故意省略了这些数值100
,以便可以测试针对该事件 ID 发送电子邮件。
使用命令-ArgumentList
的参数Invoke-Command
并将$jobName
其用作要传递的参数的值(即-ArgumentList $jobName
)。
在里面-FilterXml
:
使用
"Microsoft-Windows-TaskScheduler"
值Path=
并省略前面的/Operational
值(即<Query Id="0" Path="Microsoft-Windows-TaskScheduler">
)。$($args[0])
将作为也传入TaskName
(即*[EventData/Data[@Name='TaskName']='\$($args[0])']
)。
返回脚本块$events
内的Invoke-Command
(即Return $events
)。将Invoke-Command
输出设置为$events
变量(即$events = Invoke-Command
)。
将电子邮件发送逻辑包装在功能只需一次,因此您可以根据需要调用它执行多次,而无需重复相同的代码。
$events
将变量数组对象通过管道传输到ForEach-Object
循环,但仅输出数组对象中不存在的 ID 号的事件$notin
。将这些事件通过管道传输到Format-List
并将整个循环保存为$ebody
变量。
如果包含数组值$ebody
中没有的事件数据事件 ID ,则调用该函数。在函数中,消息变量将被传送到(即),因此必要的数据位于电子邮件正文中。$notin
email
email
$body
$ebody
Out-String
$Body = $ebody | Out-String
电源外壳
$notin = 102, 107, 129, 200, 201;
$jobName = "Batch001";
$events = Invoke-Command -ComputerName "servername.domain" -ScriptBlock {
$events = @(
Get-WinEvent -FilterXml @"
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">
*[EventData/Data[@Name='TaskName']='\$($args[0])'] and *[System[TimeCreated[timediff(@SystemTime) <= 86400000]]]
</Select>
</Query>
</QueryList>
"@ -ErrorAction Stop
);
Return $events;
} -ArgumentList $jobName;
Function email (){
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
$EmailTo = "[email protected]";
$EmailFrom = "[email protected]";
$Subject = "$jobName Failure";
$Body = $ebody | Out-String;
$SMTPServer = "smtp.office365.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("[email protected]", "Password123");
$SMTPClient.Send($SMTPMessage)
};
$ebody = @();
$ebody = $events | % { If ($_.id -notin $notin) {$_ | FL } }
If($ebody){email};
支持资源
-
-ArgumentList Object[]
在命令中设置局部变量。在远程计算机上运行命令之前,命令中的变量将被这些值替换。以逗号分隔的列表形式输入值。值按列出的顺序与变量相关联。ArgumentList 的别名为“Args”。
-
从最基本的角度来说,函数只是 function 关键字,后跟函数名称,然后是一对花括号内的某些代码。
function Add-Numbers { $args[0] + $args[1] } PS C:\> Add-Numbers 5 10 15
-
标准别名对于 Foreach 对象:'
%
' 符号,ForEach -
-notin
- 值不在集合中 -
格式列表的标准别名:
fl
答案2
感谢您提供代码帮助...这很棒,我添加了一个 try/catch 来处理前 24 小时内没有发生事件时弹出的错误。
以下是对我有用的最终清理代码:
Start-Transcript -Path "C:\LogPath\TranscriptName.txt"
$jobName = "NameOfTask";
$Date = Get-Date;
$file = "C:\Daily_Batch_Log_Files\$jobName.txt"
$file_path = Test-Path $file
$pc = $_
#If file hasn't been created, create it
If ($file_path -ne $true){
New-Item C:\LogPath\$jobName.txt
}
#Filter xml to pull task scheduler events
#Task scheduler common event IDs to ignore (treat as good/success)
$notin = 100, 102, 107, 110, 129, 140, 200, 201;
try {
#Command to connect to remote Server (if needed)
#$events = Invoke-Command -ComputerName "servername.domain" -ScriptBlock {
#Command to execute locally
$events = Invoke-Command -ScriptBlock {
#Pulls task scheduler, only events with the job (task's) name for events in the last 24 hours
$events = @(
Get-WinEvent -FilterXml @"
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">
*[EventData/Data[@Name='TaskName']='\$($args[0])'] and *[System[TimeCreated[timediff(@SystemTime) <= 86400000]]]
</Select>
</Query>
</QueryList>
"@ -ErrorAction Stop
);
Return $events;
} -ArgumentList $jobName;
}
catch {
Write-Warning -Message "Failed to query $($env:computername) because $($_.Exception.Message)" *> "C:\LogPath\errors.txt"
#If events exist from last 24 hours for this task, append the text (log) file with those events
}
if ($events) {
Add-Content C:\LogPath\$jobName.txt "As of $Date :" #-Encoding UTF8
$events | Select MachineName,TimeCreated,Id,TaskDisplayName | Out-File -Append C:\LogPath\$jobName.txt #-Encoding UTF8
}else {
Add-Content C:\LogPath\$jobName.txt "No files found for last 24 hours as of $Date `r" -Encoding UTF8
}
#Email function to email us but only email if there are tasks with codes other than the common success ones - only send an email if something is out of the ordinary
Function email (){
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
$EmailTo = "[email protected]";
$EmailFrom = "[email protected]";
$Subject = "$jobName Failure";
$Body = $ebody | Out-String;
$SMTPServer = "smtp.office365.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("[email protected]", "Password123");
$SMTPClient.Send($SMTPMessage)
};
$ebody = @();
$ebody = $events | % { If ($_.id -notin $notin) {$_ | FL } }
If($ebody){email};
Stop-Transcript