Creating custom Azure alerts from Log Analytics: parsing the alert in Azure Automation | Quisitive
Creating custom Azure alerts from Log Analytics: parsing the alert in Azure Automation
August 31, 2022
Cameron Fuller
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.

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:

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

Adding a webhook to a runbook

Use the “Create new webhook” as shown in Figure 2.

Figure 2: Creating a new webhook

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

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

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

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

Configuring an action groups 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.