This blog post will show the next step in how to create a custom alert format using a combination of Kusto and Azure Automation. In the first part of this blog post series, I introduced the steps I am utilizing to provide what I refer to as a “human readable alert”. This blog post series is to provide another option to the default functionality available in Azure Monitor. Relevant blog posts:
- Explanations of the current alert format available in Azure Monitor
- Introducing the Kusto query and configuring alert notifications
- Receiving and processing the alert (discussed in this blog post).
- Emailing the alert (discussed in the next blog post)
As a reminder, the solution we are using is built from four major components: a custom Kusto query, alert notification, Azure Automation, and LogicApps (or Azure Automation with SendGrid). To get to the script below, we are using Azure Monitor with a Kusto query that uses an action group to send a webhook to a runbook in Azure Automation. This blog post will show the process to create the Azure Automation script, how to create the webhook and how to integrate the webhook into an action group.
Creating the Azure Automation script:
The script below takes an incoming webhook, parses it for relevant information, formats it to JSON and calls a webhook that will email the reformatted content.
<#
#.SYNOPSIS
#Take an alert passed to this script via a webhook and convert it from JSON to a formatted email structure
#>
param (
[Parameter(Mandatory=$false)]
[object] $WebhookData
)
# If runbook was called from Webhook, WebhookData will not be null.
if ($WebhookData) {
write-output "Webhook data $WebhookData"
# Logic to allow for testing in Test Pane
if(-Not $WebhookData.RequestBody)
{
$WebhookData = (ConvertFrom-Json -InputObject $WebhookData)
write-output "test Webhook data $webhookData"
}
}
function CreateObjectView {
param(
$data
)
# Find the number of entries we'll need in this array
$count = 0
foreach ($table in $data.Tables) {
$count += $table.Rows.Count
}
$objectView = New-Object object[] $count
$i = 0;
foreach ($table in $data.Tables) {
foreach ($row in $table.Rows) {
# Create a dictionary of properties
$properties = @{}
for ($columnNum = 0; $columnNum -lt $table.Columns.Count; $columnNum++) {
if ([string]::IsNullOrEmpty($row[$columnNum])) {
$properties[$table.Columns[$columnNum].name] = $null
}
else {
$properties[$table.Columns[$columnNum].name] = $row[$columnNum]
}
}
$objectView[$i] = (New-Object PSObject -Property $properties)
$null = $i++
}
}
$objectView
}
$Spacer = ": "
$linespacer = "</p><p>"
$RequestBody = ConvertFrom-JSON -InputObject $WebhookData.RequestBody
if($RequestBody.SearchResult -eq $null){
$RequestBody = $RequestBody.data
}
# Get all metadata properties
$WebhookName = $WebhookData.WebhookName
write-output "Webhookname is: $($WebhookName | Out-String)"
$AlertId = $RequestBody.essentials.alertId
write-output "AlertId is: $($AlertId | Out-String)"
$AlertRule = $RequestBody.essentials.alertRule
write-output "AlertRule is: $($AlertRule | Out-String)"
$QueryResults = $RequestBody.alertContext.condition.allOf.linkToSearchResultsUI
write-output "QueryResults is: $($QueryResults | Out-String)"
# Connect to Azure
Connect-AzAccount -identity
set-azcontext -subscriptionid "<subscription>"
$AccessToken = Get-AzAccessToken -ResourceUrl 'https://api.loganalytics.io'
$data = Invoke-RestMethod -Uri $RequestBody.alertContext.condition.allOf.linkToSearchResultsAPI -Headers @{ Authorization = "Bearer " + $AccessToken.Token }
write-output "Information found from the linkToSearchResultsAPI $($data)"
$SearchResults = CreateObjectView $data
write-output "SearchResult is: $($SearchResults | Out-String)"
# Get detailed search results
foreach ($Result in $SearchResults)
{
write-output "In search results"
$Body = $Result.Body
write-output "Result.body is $($Body)"
#subject
#notificationemail
$Body = $Result.Body+"<p>Query: <a href=<code>""+$QueryResults+"</code>">Link</a>"
$Subject = $Result.Subject
$NotificationEmail = $Result.NotificationEmail
$ResourceId = $Result._ResourceId
$params = @{"To"="$NotificationEmail";"Subject"="$Client $AlertRuleName for ticket $TicketID – $Source";"Body"=$message}
write-output "Subject is: $($Subject | Out-String)"
write-output "Body is: $($Body | Out-String)"
write-output "ResourceId is: $($ResourceId | Out-String)"
write-output "NotificationEmail is: $($NotificationEmail | Out-String)"
$params = @{"To"="$NotificationEmail";"Subject"="$Subject";"Body"=$Body;"From"="$NotificationEmail"}
$json = ConvertTo-Json $params
write-output "json value is $($json)"
# Call the LogicApp that will send the email
$uri = <URLToLogicApp>
Invoke-RestMethod -Uri $uri -Method Post -Body $($json)
}
Where to create the webhook for the Azure Automation runbook:
Once the runbook has been saved and published in Azure Automation there is an option at the top to “add webhook” as shown in Figure 1 below.
Figure 1: Adding a webhook to a runbook
Use the “Create new webhook” as shown in Figure 2.
Figure 2: Creating a new webhook
Give the new webhook a name, make sure it is enabled, set the expiration date, and copy out the URL from the webhook as shown in Figure 3.
Figure 3: Creating a new webhook – specifying the name and expiration
Finish the steps to create the webhook. Now that we have the webhook we can add it into the appropriate action group.
How to integrate the webhook for the Azure Automation runbook into an action group:
Now that we have the webhook to call the runbook that we have created, we can now add the integration for the alert in Azure Monitor. The integration requires a rule, an action group, and configuration for the action group to use the webhook. The details on the rule are explained in this previous blog post. To create an action group, open Monitor and open “Action groups” shown below in Figure 4.
Figure 4: Creating an action group
Choose the option to create an action group and then specify the configurations required (shown in Figure 5) for the subscription, resource group, action group name and display name.
Figure 5: Configuring an action group
Choose the action type of Webhook and give it a name and the URL gathered in the previous section of this blog post (shown in Figure 6). Please note, there are other (potentially better) options to choose from the Action type list that may be better options (Automation Runbook or Secure runbook).
Figure 6: Configuring an action group actions
To complete the integration between the alert and the call to the runbook, assign the Action group to the alert.
Summary: This blog post shows how to create a runbook that will take an existing Kusto generated alert and reformat it into a human-readable format. This is done through leveraging Azure Monitor, alert rules, action groups, Azure Automation and PowerShell called through a webhook. The next blog post will explain how to use a Logic App to parse the JSON provided by the Azure Automation script provided in this blog post.