PowerShell Get-WinEvent获取计划任务失败事件发邮件异常求助
问题原因
脚本异常的核心原因有两个:
- 获取事件时提前调用了
Format-Table(别名ft),该命令属于PowerShell的格式化命令,输出的是供控制台渲染的格式化中间对象,不是原始事件数据,直接插入HTML时只会输出对象类型名。注意:所有Format-*开头的命令(Format-Table/Format-List等)输出结果仅用于控制台显示,不能作为数据源做后续处理,这是PowerShell初学者常见误区。 - 你直接将PowerShell对象数组插入HTML代码中,没有手动拼接HTML要求的
<tr>(行)、<td>(单元格)标签,邮件客户端无法识别原生PowerShell对象结构。
另外原脚本中遍历$eventEntries追加到$array的操作是完全冗余的,Select-Object输出的结果本身就是数组,可以直接使用。
修正代码1:所有事件汇总为单封邮件发送
这个版本会把所有匹配到的失败事件整理为标准HTML表格,放在同一封邮件里发送,加了空结果判断,没有匹配事件时不会发空邮件。
# 加载程序集用于HTML转义,避免消息里的特殊字符破坏表格结构 Add-Type -AssemblyName System.Web # 邮件配置项 $MailFrom = "helpdesk@mydomain.com" $MailTo = "myemail@mydomain.com" $SmtpServer = "smtp.mail.outlook.com" $SmtpPort = 587 $SmtpUser = "helpdesk@mydomain.com" $SmtpPassword = "passwordexample" # 获取近1天匹配的事件,去掉Format-Table,保留原始对象 $eventEntries = Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TaskScheduler/Operational' StartTime = (Get-Date).AddDays(-1) ID = 103 } | Where-Object {$_.Message -like '*Task Scheduler failed to start instance*'} | Select-Object ID, TimeCreated, Message # 无匹配事件直接退出 if (-not $eventEntries) { Write-Host "未找到匹配的计划任务启动失败事件,无需发送邮件" exit 0 } # 拼接HTML表格行 $tableRows = foreach ($event in $eventEntries) { $encodedMsg = [System.Web.HttpUtility]::HtmlEncode($event.Message) "<tr> <td style='text-align:center;padding:5px;border:1px solid #ccc;'>$($event.ID)</td> <td style='text-align:center;padding:5px;border:1px solid #ccc;'>$($event.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss'))</td> <td style='padding:5px;border:1px solid #ccc;'>$encodedMsg</td> </tr>" } # 组装完整HTML邮件正文 $emailBody = @" <table style='width:100%;border-collapse:collapse;border:1px solid #ccc;'> <tr style='background:#f0f0f0;'> <th style='text-align:center;padding:5px;border:1px solid #ccc;'>事件ID</th> <th style='text-align:center;padding:5px;border:1px solid #ccc;'>生成时间</th> <th style='text-align:center;padding:5px;border:1px solid #ccc;'>事件详情</th> </tr> $($tableRows -join "`n") </table> "@ # 发送邮件 $SMTPClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $SmtpPort) $SMTPClient.EnableSsl = $true $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($SmtpUser, $SmtpPassword) $mail = New-Object System.Net.Mail.MailMessage($MailFrom, $MailTo) $mail.Subject = "计划任务启动失败告警(共$($eventEntries.Count)条)" $mail.IsBodyHtml = $true $mail.Body = $emailBody $SMTPClient.Send($mail) Write-Host "汇总告警邮件发送成功,共包含$($eventEntries.Count)条事件"
修正代码2:每条失败事件单独发送告警邮件
这个版本会遍历每一条匹配到的事件,为每个事件单独生成告警邮件发送,主题会附带事件时间方便区分。
Add-Type -AssemblyName System.Web # 邮件配置项 $MailFrom = "helpdesk@mydomain.com" $MailTo = "myemail@mydomain.com" $SmtpServer = "smtp.mail.outlook.com" $SmtpPort = 587 $SmtpUser = "helpdesk@mydomain.com" $SmtpPassword = "passwordexample" # 初始化SMTP客户端,复用连接不用每次新建 $SMTPClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $SmtpPort) $SMTPClient.EnableSsl = $true $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($SmtpUser, $SmtpPassword) # 获取匹配事件 $eventEntries = Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TaskScheduler/Operational' StartTime = (Get-Date).AddDays(-1) ID = 103 } | Where-Object {$_.Message -like '*Task Scheduler failed to start instance*'} | Select-Object ID, TimeCreated, Message if (-not $eventEntries) { Write-Host "未找到匹配的计划任务启动失败事件,无需发送邮件" exit 0 } # 逐事件发送邮件 $sendCount = 0 foreach ($event in $eventEntries) { $encodedMsg = [System.Web.HttpUtility]::HtmlEncode($event.Message) $eventTime = $event.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss') # 单事件正文 $emailBody = @" <h3>检测到计划任务启动失败事件</h3> <table style='width:100%;border-collapse:collapse;border:1px solid #ccc;'> <tr style='background:#f0f0f0;'> <th style='text-align:left;padding:5px;border:1px solid #ccc;width:120px;'>属性</th> <th style='text-align:left;padding:5px;border:1px solid #ccc;'>值</th> </tr> <tr> <td style='padding:5px;border:1px solid #ccc;'>事件ID</td> <td style='padding:5px;border:1px solid #ccc;'>$($event.ID)</td> </tr> <tr> <td style='padding:5px;border:1px solid #ccc;'>生成时间</td> <td style='padding:5px;border:1px solid #ccc;'>$eventTime</td> </tr> <tr> <td style='padding:5px;border:1px solid #ccc;'>事件详情</td> <td style='padding:5px;border:1px solid #ccc;'>$encodedMsg</td> </tr> </table> "@ $mail = New-Object System.Net.Mail.MailMessage($MailFrom, $MailTo) $mail.Subject = "计划任务启动失败告警 - $eventTime" $mail.IsBodyHtml = $true $mail.Body = $emailBody $SMTPClient.Send($mail) $sendCount++ } Write-Host "单事件告警邮件发送完成,共发送$sendCount封邮件"
补充说明
- 把原来的
.Contains()判断改成了-like通配符匹配,不区分大小写,且遇到空消息字段不会报错,兼容性更好 - 所有输出到HTML的内容都做了HTML转义,避免事件消息里的
<、>、&等特殊字符破坏邮件排版 - 时间字段统一格式化为
yyyy-MM-dd HH:mm:ss,避免默认时间输出格式带冗余内容 - 单事件发送场景下复用SMTP连接,不用每次发送都重新建立连接,执行效率更高
内容的提问来源于stack exchange,提问作者rabocii
相关产品推荐
相关产品推荐

