forked from KenHoover/AzureSHStuff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ASHWebhookToMessageCardFn.ps1
175 lines (139 loc) · 7.23 KB
/
ASHWebhookToMessageCardFn.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# ASHWebhookToMessageCard.ps1 (for Azure Powershell function app preview)
# by Ken Hoover <ken at khoover dot com>
# August 2019
#
# This is Powershell code for an Azure function app to parse an incoming webhook generated by a Service Health alert and turn it into a MessageCard which
# is then sent to a destination such as Teams via an outgoing webhook. It can probably be adapted fairly easily to send webhooks to other recipients like Slack.
# This assumes that the incoming message uses the Common Alert Schema and is a Service Health Alert
# https://docs.microsoft.com/en-us/azure/azure-monitor/platform/alerts-common-schema-definitions#monitoringservice--servicehealth
#
# IMPORTANT: The uri to send the outgoing webhook to must be set as an environment variable for the function named "webhookuri"
# This is done using the "Application Settings" screen from the Azure Portal
#
using namespace System.Net
# Incoming data from the trigger
param ( $Request, $TriggerMetadata )
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function received a request."
####### Setup stuff
# Get the payload from the incoming webhook as an object
$payload = ($request.rawbody | convertfrom-json)
# Get the subscription ID that this alert is for from the payload
$subscriptionId = $payload.data.essentials.alertid.split('/')[2]
# Connect to the storage account using MSI to load config.json
$storageAccountName = (${env:AzureWebJobsStorage}).split('AccountName=')[1].split(';')[0]
$storageContext = New-AzStorageContext -UseConnectedAccount -StorageAccountName $storageAccountName
$configTempFile = New-TemporaryFile
Get-AzStorageBlobContent -Context $storageContext -Blob "config.json" -Container data -Destination $configTempFile.FullName -Force
$config = Get-Content -Encoding utf8 -Path $configTempFile | ConvertFrom-Json
$subsDictionary = @{}
$config.${env:AZURE_FUNCTIONS_ENVIRONMENT}.subscriptions | ForEach-Object {$subsDictionary.Add($_.Id, $_.Name)}
Write-Host "Known subscriptions:"
$subsDictionary
# Start assembling the message card from the payload data
# This script uses the legacy-but-simpler O365 "Message Card" format described at
# https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference
# In the future this should be upgraded to the newer-but-more-complex "Adaptive Card" format
# https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/cards/cards-reference#adaptive-card
$MessageCard = @{}
$sections = @() ; $SectionsHash = @{}
$MessageCard.Add("@type", "MessageCard")
$MessageCard.Add("@context", "http://schema.org/extensions")
$MessageCard.Add("summary", $payload.data.alertcontext.properties.title)
$MessageCard.Add("title", "Azure Service Health Alert$($subsDictionary[$subscriptionId] | % {if ($_.Length -gt 0) {' for ' + $_} else {''} })")
$MessageCard.Add("text", ("**Subscription ID " + $subscriptionId + "**" ))
# Set the theme color for the message card based on the event "stage"
# Possible stages are: Active, Planned, InProgress, Canceled, Rescheduled, Resolved, Complete and RCA
# Different incident types use different subsets of the possible stages so some types of alerts will be presented differently based
# on their urgency. This should limit the amount of red that comes through.
$red = "FF0000" ; $yellow = "FFFF00" ; $blue = "000000" ; $green = "008000"
switch ($payload.data.alertcontext.properties.IncidentType) {
"Informational" {
$MessageCard.Add("themeColor", $blue)
}
"ActionRequired" {
$MessageCard.Add("themeColor", $yellow)
}
"Incident" { # apply different themes based on the alert stage.
switch ($payload.data.alertcontext.properties.stage) {
"Active" {
$MessageCard.Add("themeColor", $red)
}
"Resolved" {
$MessageCard.Add("themeColor", $blue)
}
"RCA" {
$MessageCard.Add("themeColor", $blue)
}
}
}
"Maintenance" {
$MessageCard.Add("themeColor", $green)
}
"Security" {
switch ($payload.data.alertcontext.properties.stage) {
"Active" {
$MessageCard.Add("themeColor", $red)
}
"Resolved" {
$MessageCard.Add("themeColor", $blue)
}
"RCA" {
$MessageCard.Add("themeColor", $blue)
}
}
}
Default { # in case something comes through that we don't recognize
$MessageCard.Add("themeColor", $yellow)
}
}
$SectionsHash.Add("activityTitle", ("# " + $payload.data.alertcontext.properties.title))
$SectionsHash.Add("activitySubTitle",("# Alert State: " + $payload.data.alertcontext.properties.stage))
# Copy the properties of the payload (a list of name-value pairs) into the card's "facts" section
$facts = $payload.data.alertcontext.properties
if($Facts -ne $null){
$factsCollection = @()
foreach($fact in $Facts) {
$Fact.psobject.properties | ForEach-Object {
# skip redundant fields and ones that have data that won't parse cleanly
if (!( ($_.name.tostring().contains("default")) -or ($_.name.tostring().contains("impactedServices")) )) {
$factsCollection += @{"name"=$_.name ; "value"=$_.value }
}
}
}
$SectionsHash.Add("facts",$factsCollection)
}
$Sections += $SectionsHash
$MessageCard.Add("sections", $Sections)
##
# Now add the actions part of the card. This is where we put buttons and such to let users take an action right from the card.
# In this case we're just adding a button to open the service health issues blade in the Azure portal.
#
# Ref: https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#actions
#
$potentialActions = @() ;
$potentialActionsHash = @{}
$potentialActionsHash.Add("@type","openuri")
$potentialActionsHash.Add("name","Open Service Issues Page")
$targetsList = @() ; $targetsHash = @{}
$targetsHash.Add("os","default")
$targetsHash.Add("uri","https://portal.azure.com/#blade/Microsoft_Azure_Health/AzureHealthBrowseBlade/serviceIssues")
$targetslist += $targetsHash
$potentialActionsHash.Add("targets",$targetslist)
$potentialActions += $potentialActionsHash
$messageCard.add("potentialAction",$potentialActions)
### Send the message card to Teams
# Now that we have the message card, convert it to JSON so it can be sent as the body of the outgoing webhook
$messageCardJSON = $messageCard | ConvertTo-Json -Depth 15
$webhookDictionary = @{}
$config.${env:AZURE_FUNCTIONS_ENVIRONMENT}.webhookIntegrations | ForEach-Object {$webhookDictionary.Add($_.channel, $_.uri)}
####### Now that the MessageCard is complete, send the outgoing webhook(s) to post the card
foreach ($key in $webhookDictionary.Keys) {
Write-Host "Sending message card to ${key} channel."
invoke-webrequest -method POST -uri $webhookDictionary[$key] -body $messageCardJSON
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $messageCardJSON
})