You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell Get-WinEvent获取计划任务失败事件发邮件异常求助

问题原因

脚本异常的核心原因有两个:

  1. 获取事件时提前调用了Format-Table(别名ft),该命令属于PowerShell的格式化命令,输出的是供控制台渲染的格式化中间对象,不是原始事件数据,直接插入HTML时只会输出对象类型名。注意:所有Format-*开头的命令(Format-Table/Format-List等)输出结果仅用于控制台显示,不能作为数据源做后续处理,这是PowerShell初学者常见误区。
  2. 你直接将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 11:27:17