Using PowerShell, Azure Automation, and OMS - Part II

So last time we learned how to upload our custom modules into Azure Automation so we can start using them in Azure Automation Runbooks.  This week we're going to take a look at configuring a runbook to see what kind of data we can ingest from OMS Webhook data, and how we can leverage that data to pass into our functions.

Creating the Runbook Script

So first off, let's talk about basic runbooks and running them against objects in Azure.  As previously discussed, when your automation account is created, it creates with it an AzureRunAsAccount.  This account is configured to act on behalf of the user that has access to the automation account and the runbooks in order to perform the runbook task.  In order to leverage this account, you need to invoke it in the runbook itself.  You can actually find an example of this snippet in the AzureAutomationTutorialScript runbook in your automation account.

$connectionName = "AzureRunAsConnection"
    # Get the connection "AzureRunAsConnection "
    $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName         

    "Logging in to Azure..."
    Add-AzureRmAccount `
        -ServicePrincipal `
        -TenantId $servicePrincipalConnection.TenantId `
        -ApplicationId $servicePrincipalConnection.ApplicationId `
        -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 
catch {
    if (!$servicePrincipalConnection)
        $ErrorMessage = "Connection $connectionName not found."
        throw $ErrorMessage
    } else{
        Write-Error -Message $_.Exception
        throw $_.Exception

So now that we've got our opening snippet, we'll add that into a new .ps1 script file in our preferred integrated scripting environment tool and get to work.

Now, in order to be able to ingest data from an OMS Alert, we need to be able to pass the data to our Azure Automation runbook.  In order to do so, we only need to add a $WebHookData parameter to the runbook and specify the data type as object.

Param (



Now, we need to convert that data from a JSON object into something readable in our output.  Webhook data is presented with three primary datasets - WebhookName, RequestHeader, and RequestBody.  WebhookName, obviously is the name of the incoming webhook.  RequestHeader is a hash table containing all of the header data for the incoming requestion.  And finally, RequestBody is the body of the incoming request.  This is where the data we want to parse will reside.  Specifically, it will reside under the SearchResults property of the RequestHeader dataset.


So let's configure our runbook to display the incoming data to examine what we have to play with.

$SearchResults = (ConvertFrom-Json $WebhookData.RequestBody).SearchResults.value



Publish the Runbook

Now, we'll go ahead and save our script as a .ps1 file and upload it to our automation account with the Import-AzureRmAutomationRunbook cmdlet.

Import-AzureRmAutomationRunbook -Path 'C:\Scripts\Presentations\OMSAutomation\ExampleRunbookScript.ps1' -Name WebhookNSGRule -Type PowerShell -ResourceGroupName $AutoAcct.ResourceGroupName -AutomationAccountName $AutoAcct.AutomationAccountName -Published

And now we can see our return.

And if we check through the UI, we can see a brand-new, shiny runbook sitting in our automation account!  Now, we can configure a basic alert to monitor in OMS.

Create an Alert

For the purposes of this example, I've create a couple of virtual machines with network security group rules for HTTP:80 and RDP:3389 accepting connections from anywhere.  I do not recommend doing this for a production virtual machine.  /endDisclaimer

As you can well expect, these machines are throwing MaliciousIP traffic alerts in Operations Management Suite's console:

So if we click on the MaliciousIP flag, it'll take us to the Log Search screen.  This includes the query data that we can use for the alert.  However, you'll want to clean up the query data a bit to generalize it.  In this example, the query is specific to the country that is displayed in the given flag.  But if we remove the country specific portion of the query, it'll allow us to cast a wider net and get data on potentially malicious traffic from any given country.

Canned Query:
MaliciousIP=* AND (RemoteIPCountry=* OR MaliciousIPCountry=*) AND (((Type=WireData AND Direction=Outbound) OR (Type=WindowsFirewall AND CommunicationDirection=SEND) OR (Type=CommonSecurityLog AND CommunicationDirection=Outbound)) OR (Type=W3CIISLog OR Type=DnsEvents OR (Type = WireData AND Direction!= Outbound) OR (Type=WindowsFirewall AND CommunicationDirection!=SEND) OR (Type = CommonSecurityLog AND CommunicationDirection!= Outbound))) (RemoteIPCountry="People's Republic of China" OR MaliciousIPCountry="People's Republic of China")

Modified Query:
MaliciousIP=* AND (RemoteIPCountry=* OR MaliciousIPCountry=*) AND (((Type=WireData AND Direction=Outbound) OR (Type=WindowsFirewall AND CommunicationDirection=SEND) OR (Type=CommonSecurityLog AND CommunicationDirection=Outbound)) OR (Type=W3CIISLog OR Type=DnsEvents OR (Type = WireData AND Direction!= Outbound) OR (Type=WindowsFirewall AND CommunicationDirection!=SEND) OR (Type = CommonSecurityLog AND CommunicationDirection!= Outbound)))

After testing our query to make sure it's valid, we can now hit the alert button and configure the alert.  Here you'll need to give it an alert name, a schedule, and number of results before it triggers the alert.  You'll also want to select the Runbook option under actions and select the test runbook we created.  Then we hit save, and wait for our alert to trigger and the runbook to fire.

And as you can see, I didn't have to wait long:

Validate our Data

If we click on one of the completed instances, and navigate to the output blade, we can now see the data we're receiving from our triggered alert.  This particular data shows that inbound traffic from Colombia is attempting an RDP connection to my virtual machine.  With the inbound IP Address and target system name, we now have enough data to be able to create a full-blown auto-remediation solution.

Logging in to Azure...

Environments                                                                                           Context          
------------                                                                                           -------          
{[AzureCloud, AzureCloud], [AzureChinaCloud, AzureChinaCloud], [AzureUSGovernment, AzureUSGovernment]} Microsoft.Azur...

Computer                   : server1
MG                         : 00000000-0000-0000-0000-000000000001
ManagementGroupName        : AOI-cb0eefe8-b88f-47ce-ae91-dbc46df99751
SourceSystem               : OpsManager
TimeGenerated              : 2017-07-21T12:17:37.45Z
SessionStartTime           : 2017-07-21T12:16:52Z
SessionEndTime             : 2017-07-21T12:16:52Z
LocalIP                    :
LocalSubnet                :
LocalMAC                   : 00-0d-3a-03-ea-a6
LocalPortNumber            : 3389
RemoteIP                   :
RemoteMAC                  : 12-34-56-78-9a-bc
RemotePortNumber           : 4935
SessionID                  :
SequenceNumber             : 0
SessionState               : Listen
SentBytes                  : 20
ReceivedBytes              : 40
TotalBytes                 : 60
ProtocolName               : TCP
IPVersion                  : IPv4
SentPackets                : 1
ReceivedPackets            : 2
Direction                  : Inbound
ApplicationProtocol        : RDP
ProcessID                  : 888
ProcessName                : C:\Windows\System32\svchost.exe
ApplicationServiceName     : ms-wbt-server
LatencyMilliseconds        : 116
LatencySamplingTimeStamp   : 2017-07-21T12:16:52Z
LatencySamplingFailureRate : 0.0%
MaliciousIP                :
IndicatorThreatType        : Botnet
Confidence                 : 75
Severity                   : 2
FirstReportedDateTime      : 2017-07-20T20:10:32Z
LastReportedDateTime       : 2017-07-21T11:25:11.0661909Z
IsActive                   : true
ReportReferenceLink        :
RemoteIPLongitude          : -75.88
RemoteIPLatitude           : 8.77
RemoteIPCountry            : Colombia
id                         : 149270bc-74fc-13d0-34a9-3fd665a457b2
Type                       : WireData
__metadata                 : @{Type=WireData; TimeGenerated=2017-07-21T12:17:37.45Z}

It's a long road, and we're almost there!  Next week, I'll take you through my process of modifying my module to directly ingest webhook data, and how we can take our OMS queries and deploy them to other Operations Management Suite solutions using PowerShell.  See you then!

Part I - Azure Automation Account Creation and Adding Modules

Part II - Configuring Azure Automation Runbooks And Understanding Webhook Data

Part III - Utilizing Webhook Data in Functions and Validate Results - Coming Soon!

About Will Anderson

Webmaster. PowerShell Junkie and MVP / Director - The DevOps Collective / Cloud Solutions Architect - Coretek Services / Twitter: @GamerLivingWill