请求创建Logic App实现租户内订阅密钥/证书到期邮件告警工作流及代码
租户内订阅Key Vault资源到期告警Logic App实现方案
前置权限准备
- 给Logic App关联的服务主体分配租户级Reader角色,确保能读取所有订阅;同时给该主体分配目标Key Vault的Secrets Reader、Certificates Reader、Keys Reader角色,保证能访问密钥、证书、机密数据。
详细工作流步骤(消耗型Logic App)
1. 设置定时触发
添加Recurrence触发器,配置执行频率(推荐每日一次),设置合适的时区。
2. 拉取租户内所有订阅
添加Azure Management Groups - List Subscriptions动作,无需额外参数,默认返回租户下所有有效订阅。
3. 遍历订阅,获取每个订阅的Key Vault
- 嵌套
For each循环,循环项选择上一步返回的订阅列表。 - 循环内添加
Azure Resource Graph - Resources动作:- 选择对应订阅ID(动态内容选当前循环的
subscriptionId) - Kusto查询代码:
Resources | where type =~ 'Microsoft.KeyVault/vaults' | project name, id, location
- 选择对应订阅ID(动态内容选当前循环的
4. 遍历Key Vault,筛选30天内到期的资源
在Key Vault的嵌套For each循环内,添加三个并行分支分别处理机密、证书、密钥:
分支1:筛选到期机密
- 添加
Azure Key Vault - List Secrets动作,选择当前循环的Key Vault名称和订阅ID。 - 添加
Filter array动作,输入为机密列表,过滤规则:@lessOrEquals(addDays(utcNow(), 30), item()?['attributes']?['exp'])
分支2:筛选到期证书
- 添加
Azure Key Vault - List Certificates动作,选择当前循环的Key Vault名称和订阅ID。 - 添加
Filter array动作,过滤规则同上。
分支3:筛选到期密钥
- 添加
Azure Key Vault - List Keys动作,选择当前循环的Key Vault名称和订阅ID。 - 添加
Filter array动作,过滤规则同上。
5. 合并结果并发送告警邮件
- 添加
Compose动作,整合三个分支的过滤结果:{ "订阅名称": "@{items('For_each_订阅')?['displayName']}", "订阅ID": "@{items('For_each_订阅')?['subscriptionId']}", "Key Vault名称": "@{items('For_each_KeyVault')?['name']}", "30天内到期的机密": "@{body('Filter_array_机密')}", "30天内到期的证书": "@{body('Filter_array_证书')}", "30天内到期的密钥": "@{body('Filter_array_密钥')}" } - 添加
Condition动作,判断是否存在到期资源:@or(not(empty(body('Compose')?['30天内到期的机密'])), not(empty(body('Compose')?['30天内到期的证书'])), not(empty(body('Compose')?['30天内到期的密钥']))) - 条件为真时,添加
Send email (V2)(Office 365 Outlook连接器):- 主题:
Azure Key Vault资源到期告警 - 订阅:@{items('For_each_订阅')?['displayName']} - 邮件内容(推荐用HTML格式化):
<h2>Key Vault资源到期提醒</h2> <p>订阅:@{items('For_each_订阅')?['displayName']} (ID: @{items('For_each_订阅')?['subscriptionId']})</p> <p>Key Vault:@{items('For_each_KeyVault')?['name']}</p> <h3>即将到期的机密</h3> <table border="1" cellpadding="4"> <tr><th>名称</th><th>到期时间</th></tr> @{join(body('Filter_array_机密'), '<tr><td>@{item()?["name"]}</td><td>@{formatDateTime(item()?["attributes"]?["exp"], "yyyy-MM-dd HH:mm:ss")}</td></tr>')} </table> <h3>即将到期的证书</h3> <table border="1" cellpadding="4"> <tr><th>名称</th><th>到期时间</th></tr> @{join(body('Filter_array_证书'), '<tr><td>@{item()?["name"]}</td><td>@{formatDateTime(item()?["attributes"]?["exp"], "yyyy-MM-dd HH:mm:ss")}</td></tr>')} </table> <h3>即将到期的密钥</h3> <table border="1" cellpadding="4"> <tr><th>名称</th><th>到期时间</th></tr> @{join(body('Filter_array_密钥'), '<tr><td>@{item()?["name"]}</td><td>@{formatDateTime(item()?["attributes"]?["exp"], "yyyy-MM-dd HH:mm:ss")}</td></tr>')} </table>
- 主题:
关键注意事项
- 若租户订阅数量较多,可调整
For each循环的并行度(默认10,最高可设为20),避免触发Azure限流。 - 确保服务主体权限覆盖所有目标订阅和Key Vault,否则会出现
403 Forbidden错误。 - 可根据需求修改Kusto查询,比如过滤掉已禁用的Key Vault:
Resources | where type =~ 'Microsoft.KeyVault/vaults' | where properties.enabledForDeployment == true | project name, id, location
内容的提问来源于stack exchange,提问作者Ravi Ranjan Gaurav
相关产品推荐
相关产品推荐

