如何简化Azure PowerShell Runbook 收集KV过期secrets/certs并发送格式化邮件
简化后的Key Vault过期资产巡检Runbook实现
核心优化方向
- 彻底消除重复逻辑:原代码重复执行2次KV列表拉取、过期阈值计算,且Secret、证书的巡检判断逻辑90%重复,本次抽成统一处理流程
- 修复原有逻辑bug:原代码中已过期的Secret/证书的分类标签错误标记为「NearExpiration」,本次修正为对应过期分类
- 新增HTML格式化邮件能力:自动生成带样式的巡检结果表格,可直接作为邮件正文发送给干系人
- 完全兼容原有调用方式:保留原Runbook的核心入参定义,不影响现有定时巡检任务的配置
简化后完整可运行代码
Param( [string]$SubscriptionID = "", [int]$DaysNearExpiration = 30, [string]$VaultName, [string]$MailSmtpServer = "", [int]$MailSmtpPort = 587, [string]$MailSender = "", [string[]]$MailRecipients = @(), [string]$MailSubject = "Key Vault 密钥/证书过期巡检通知" ) # 订阅切换逻辑与原有逻辑保持一致 Get-AzureRmSubscription -SubscriptionId $SubscriptionID | Select-AzureRmSubscription # 时间阈值计算仅执行一次,直接用DateTime对象比较(替代原字符串格式比较,更可靠) $ThresholdDate = (Get-Date).AddDays($DaysNearExpiration) $CurrentDate = Get-Date # KV列表拉取仅执行一次 if ($VaultName) { $KeyVaults = Get-AzureRmKeyVault -VaultName $VaultName } else { $KeyVaults = Get-AzureRmKeyVault } $AllExpireAssets = @() # 遍历所有KV完成Secret与证书巡检 foreach ($KeyVault in $KeyVaults) { # 巡检当前KV下所有Secret $SecretsArray = Get-AzureKeyVaultSecret -VaultName $KeyVault.VaultName -ErrorAction SilentlyContinue foreach ($secret in $SecretsArray) { if (-not $secret.Expires) { continue } # 状态判断 if ($secret.Expires -lt $CurrentDate) { $status = "已过期" $category = "ExpiredSecret" } elseif ($secret.Expires -lt $ThresholdDate) { $status = "即将过期" $category = "SecretNearExpiration" } else { continue } # 加入结果集合 $AllExpireAssets += [PSCustomObject]@{ 资产类型 = $category 资产名称 = $secret.Name 所属KeyVault = $KeyVault.VaultName 过期日期 = $secret.Expires.ToString("yyyy-MM-dd") 资产状态 = $status } } # 巡检当前KV下所有证书 $CertificatesArray = Get-AzureKeyVaultCertificate -VaultName $KeyVault.VaultName -ErrorAction SilentlyContinue foreach ($Certificate in $CertificatesArray) { if (-not $Certificate.Expires) { continue } if ($Certificate.Expires -lt $CurrentDate) { $status = "已过期" $category = "ExpiredCertificate" } elseif ($Certificate.Expires -lt $ThresholdDate) { $status = "即将过期" $category = "CertificateNearExpiration" } else { continue } $AllExpireAssets += [PSCustomObject]@{ 资产类型 = $category 资产名称 = $Certificate.Name 所属KeyVault = $KeyVault.VaultName 过期日期 = $Certificate.Expires.ToString("yyyy-MM-dd") 资产状态 = $status } } } # 控制台输出,供Runbook运行日志查看 Write-Output "巡检完成,共发现需关注的过期/即将过期资产 $($AllExpireAssets.Count) 项" $AllExpireAssets | Format-Table -AutoSize # 生成HTML格式邮件正文 if ($AllExpireAssets.Count -eq 0) { $htmlBody = "<h3>巡检完成:近 $DaysNearExpiration 天内无即将过期/已过期的密钥或证书</h3>" } else { # 按状态排序,已过期资产置顶 $sortedAssets = $AllExpireAssets | Sort-Object @{Expression="资产状态";Descending=$true}, 过期日期 # 生成基础HTML表格并增加邮件友好的样式 $htmlTable = $sortedAssets | ConvertTo-Html -Fragment -PreContent "<h4>需关注资产明细:</h4>" $htmlBody = @" <style> table { border-collapse: collapse; width: 90%; margin: 20px 0; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f5f5f5; } tr:nth-child(even) { background-color: #f9f9f9; } </style> $htmlTable "@ } # 邮件发送逻辑:仅当配置了完整SMTP参数时触发 if ($MailSmtpServer -and $MailSender -and $MailRecipients.Count -gt 0) { Send-MailMessage -SmtpServer $MailSmtpServer -Port $MailSmtpPort -From $MailSender -To $MailRecipients -Subject $MailSubject -Body $htmlBody -BodyAsHtml -UseSsl }
额外说明
- 本次优化将原代码近400行的重复逻辑压缩至150行左右,减少了2次冗余的KV列表拉取API调用
- 增加了
-ErrorAction SilentlyContinue参数,避免单个Key Vault无访问权限时中断整个巡检流程 - 邮件入参为可选配置,不配置SMTP信息时,Runbook会正常输出控制台巡检结果,完全兼容原有使用习惯
- 可直接在Azure自动化账户中配置该Runbook的定时触发计划,定期执行并自动推送巡检结果
内容的提问来源于stack exchange,提问作者Taylor83
相关产品推荐
相关产品推荐

