Copy ############################################################
# Example Azure Automation Runbook for exporting data from #
# MS Graph and sending it to a custom Azure Monitor log #
############################################################
# Variables
$ProgressPreference = 'SilentlyContinue'
$WorkspaceID = Get-AutomationVariable - Name "WorkspaceID" # Saved as an encrypted variable in the automation account
$PrimaryKey = Get-AutomationVariable - Name "PrimaryKey" # Saved as an encrypted variable in the automation account
$LogType = "Devices" # The name of the log file that will be created or appended to
#############
# FUNCTIONS #
#############
# function to invoke a web request to MS Graph with error handling
Function script : Invoke-LocalGraphRequest {
Param ($URL , $Headers , $Method , $Body , $ContentType)
try {
If ($Method -eq "Post" )
{
$WebRequest = Invoke-WebRequest -Uri $URL -Method $Method -Headers $Headers -Body $Body -ContentType $ContentType -UseBasicParsing
}
else
{
$WebRequest = Invoke-WebRequest - Uri $URL - Method $Method - Headers $Headers - UseBasicParsing
}
}
catch {
$WebRequest = $_.Exception.Response
}
Return $WebRequest
}
# function to get managed Windows device data from MS Graph
Function Get-DeviceData {
$URL = "https://graph.microsoft.com/beta/deviceManagement/manageddevices?`$filter=startsWith(operatingSystem,'Windows')&`$select=deviceName,Id,lastSyncDateTime,managementAgent,managementState,osVersion,skuFamily,deviceEnrollmentType,emailAddress,model,manufacturer,serialNumber,userDisplayName,joinType"
$headers = @ { 'Authorization' = "Bearer " + $accessToken}
$GraphRequest = Invoke-LocalGraphRequest - URL $URL - Headers $headers - Method GET
If ($GraphRequest.StatusCode -ne 200 )
{
Return $GraphRequest
}
$JsonResponse = $GraphRequest.Content | ConvertFrom-Json
$DeviceData = $JsonResponse.value
If ($JsonResponse. '@odata.nextLink' )
{
do {
$URL = $JsonResponse. '@odata.nextLink'
$GraphRequest = Invoke-LocalGraphRequest - URL $URL - Headers $headers - Method GET
If ($GraphRequest.StatusCode -ne 200 )
{
Return $GraphRequest
}
$JsonResponse = $GraphRequest.Content | ConvertFrom-Json
$DeviceData += $JsonResponse.value
} until ( $null -eq $JsonResponse. '@odata.nextLink' )
}
Return $DeviceData
}
# Create the function to create the authorization signature
# ref https://docs.microsoft.com/en-us/azure/azure-monitor/logs/data-collector-api
Function Build-Signature ($customerId , $sharedKey , $date , $contentLength , $method , $contentType , $resource)
{
$xHeaders = "x-ms-date:" + $date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [ Text.Encoding ]::UTF8.GetBytes($stringToHash)
$keyBytes = [ Convert ]::FromBase64String($sharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.Key = $keyBytes
$calculatedHash = $sha256.ComputeHash($bytesToHash)
$encodedHash = [ Convert ]::ToBase64String($calculatedHash)
$authorization = 'SharedKey {0}:{1}' -f $customerId , $encodedHash
return $authorization
}
# Create the function to create and post the request
# ref https://docs.microsoft.com/en-us/azure/azure-monitor/logs/data-collector-api
Function Post-LogAnalyticsData ($customerId , $sharedKey , $body , $logType)
{
$method = "POST"
$contentType = "application/json"
$resource = "/api/logs"
$rfc1123date = [ DateTime ]::UtcNow.ToString( "r" )
$contentLength = $body.Length
$TimeStampField = ""
$signature = Build-Signature `
- customerId $customerId `
- sharedKey $sharedKey `
- date $rfc1123date `
- contentLength $contentLength `
- method $method `
- contentType $contentType `
- resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = @ {
"Authorization" = $signature;
"Log-Type" = $logType;
"x-ms-date" = $rfc1123date;
"time-generated-field" = $TimeStampField;
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
return $response.StatusCode
}
####################
## AUTHENTICATION ##
####################
## Get MS Graph access token
# Managed Identity
$url = $ env: IDENTITY_ENDPOINT
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add( "X-IDENTITY-HEADER" , $ env: IDENTITY_HEADER)
$headers.Add( "Metadata" , "True" )
$body = @ {resource = 'https://graph.microsoft.com/' }
$script:accessToken = (Invoke-RestMethod $url -Method 'POST' -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body $body ).access_token
#########################
## THUNDERBIRDS ARE GO ##
#########################
$Devices = Get-DeviceData
$Json = $Devices | ConvertTo-Json
Post-LogAnalyticsData -customerId $WorkspaceID -sharedKey $PrimaryKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType