Last active
March 22, 2019 08:05
-
-
Save AlexAsplund/d62ff8275768900d07eb7b28fef1b6ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Pulls Azure Identity Risk logs and sends them to a gelf-server through TCP. | |
.DESCRIPTION | |
Pulls Azure Identity Risk logs and sends them to a gelf-server. | |
Requires the PSGelf module (Install-Module -Name PSGELF). | |
AppCredentials should be supplied as Credential object with AppID as username and AppKey as password. | |
.EXAMPLE | |
PS C:\> .\Script.ps1 -AppCredential $Credential -TenantName mytenant.onmicrosoft.com -GelfServer gelf.domain.com -GelfPort <portnumber> | |
Explanation of what the example does | |
#> | |
param( | |
[parameter(Mandatory)] | |
[PSCredential]$AppCredential, | |
[parameter(Mandatory)] | |
[String]$TenantName, | |
[parameter(Mandatory)] | |
[String]$GelfServer, | |
[parameter(Mandatory)] | |
[int]$GelfPort | |
) | |
$ErrorActionPreference = "STOP" | |
Import-Module PSGELF | |
################################################# | |
# Function for aquiring token | |
################################################# | |
<# | |
.SYNOPSIS | |
Gets a graph access token | |
.DESCRIPTION | |
Gets a graph access token with client credentials. | |
You need to create a new app in azure AD and supply it [pscredential]$ClientCredential. | |
Username = application id | |
Password = Application key/client secret | |
See MS Graph documentation for beta/identityRiskEvents to give your app the right api-permissions. | |
.EXAMPLE | |
PS C:\> Get-MyAADAccessToken -ClientCredential $Credential -TenantName contoso.onmicrosoft.com | |
Explanation of what the example does | |
#> | |
Function Get-MyAADAccessToken { | |
param( | |
# application secrets. Username = application id, password = client secret | |
[parameter(Mandatory)] | |
[PSCredential]$ClientCredential, | |
# Name of your tenant. ie contoso.onmicrosoft.com | |
[parameter(Mandatory)] | |
[string]$TenantName | |
) | |
Add-Type -AssemblyName System.Web | |
# Decode securestring | |
$SecureString = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($ClientCredential.password) | |
$Password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($SecureString) | |
[System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($SecureString) | |
$BodyList = @( | |
"client_id=$([System.Web.HttpUtility]::UrlEncode($ClientCredential.UserName))" | |
"scope=https%3A%2F%2Fgraph.microsoft.com%2F.default" | |
"client_secret=$([System.Web.HttpUtility]::UrlEncode($Password))", | |
"grant_type=client_credentials" | |
) | |
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" | |
$PostSplat = @{ | |
ContentType = 'application/x-www-form-urlencoded' | |
Method = 'POST' | |
Body = ($BodyList -join '&') | |
Uri = $Url | |
} | |
$Request = Invoke-RestMethod @PostSplat | |
return $Request | |
} | |
################################################# | |
# Functions for geohash | |
################################################# | |
function Create-Geohash($lat,$lng,$precision) { | |
$minLat = -90 | |
$maxLat = 90 | |
$minLng = -180 | |
$maxLng = 180 | |
$idx = 0 | |
$bit = 0 | |
$geohash="" | |
while ($geohash.Length -lt $precision) { | |
$i++ | |
if ($($i%2) -eq 1) { # even bit: longitude | |
$midpoint = ($minLng + $maxLng) / 2 | |
if ($lng -ge $midpoint) { | |
$minLng = $midpoint # shrink range down | |
$idx = $idx*2 | |
} else { | |
$maxLng = $midpoint # shrink range up | |
$idx = $idx*2 + 1 | |
} | |
}else{ #odd bit: latitude | |
$midpoint = ($minLat + $maxLat) / 2 | |
if ($lat -ge $midpoint) { | |
$minLat = $midpoint # shrink range down | |
$idx = $idx*2 | |
} else { | |
$maxLat = $midpoint # shrink range up | |
$idx = $idx*2 + 1 | |
} | |
} | |
$bit++ | |
if ($bit -eq 5) { | |
# 5 bits gives us a character: append it and start over | |
$geohash = $geohash+ $( ToBase32String $idx ) | |
$bit = 0 | |
$idx = 0 | |
} | |
} | |
return $geohash | |
} | |
function ToBase32String($idx){ | |
$Base32 = "0123456789bcdefghjkmnpqrstuvwxyz" | |
$varbase32=$Base32[$(($idx) % 32)] | |
return $varbase32 | |
} | |
################################################################ | |
# Get last sent event date from file. | |
# Set date to 2000-01-01 if it does not exist | |
################################################################ | |
$DatePath = ".\LastIdentityRiskDate.txt" | |
if(!(test-path $DatePath)){ | |
$LastEvent = Get-Date "2000-01-01" | |
} | |
else{ | |
$LastEvent = cat $DatePath | Get-Date | |
} | |
# Aquire access token and create header | |
$AccessToken = (Get-MyAADAccessToken -TenantName $TenantName -ClientCredential $AppCredential).access_token | |
$Header = @{ | |
Authorization = 'Bearer '+$AccessToken | |
} | |
# Get all identity risk events | |
$Events = @() | |
$Request = Invoke-RestMethod -Uri 'https://graph.microsoft.com/beta/identityRiskEvents' -Headers $Header | |
$Events += $Request.value | |
while($Request.'@odata.nextLink' -ne $null){ | |
$Request = Invoke-RestMethod -Uri $Request.'@odata.nextLink' -Headers $Header | |
Write-Output $Request.'@odata.nextLink' | |
$Events += $Request.Value | |
$More = $Request.'@odata.nextLink' -ne $null | |
} | |
# Sort all events and select those that occured after last read event | |
$NewEvents = $Events | sort riskEventDateTime -Descending | ? {(get-date $_.riskEventDateTime) -gt $LastEvent} | |
$c = 0 | |
foreach($Event in $NewEvents | select @{Name="Message";Expression={$_.riskEventType}},@{Name='TimeCreated';Expression={$_.riskEventDateTime}},@{Name="Type";Expression={"AzureRiskEvent"}},*){ | |
# Create geohash from found long/lat data | |
$LocationGeoHash = Create-Geohash -lat (-$Event.location.geoCoordinates.latitude) -lng (-$Event.location.geoCoordinates.longitude) -precision 7 | |
$Event | Add-Member -MemberType NoteProperty -Name LocationGeoHash -Value $LocationGeoHash | |
if($Event.previousLocation -ne $null){ | |
$PreviousLocationGeoHash = Create-Geohash -lat (-$Event.previousLocation.geoCoordinates.latitude) -lng (-$Event.previousLocation.geoCoordinates.longitude) -precision 7 | |
$Event | Add-Member -MemberType NoteProperty -Name previousLocationGeoHash -Value $PreviousLocationGeoHash | |
} | |
# Add geohash | |
$Event | Add-Member -MemberType NoteProperty -Name geoHashMetricSize -Value 153 | |
# Send to gelf-server | |
Send-PSGelfTCPFromObject -GelfServer $GelfServer -Port $GelfPort -GelfMessage $Event -Verbose | |
$c++ | |
} | |
# Select last event | |
$LastEventString = $Events.riskEventDateTime | sort -Descending | select -first 1 | |
if($c -gt 0){ | |
if($LastEventString -eq $null){ | |
throw "Error saving last event date" | |
} | |
# Save last event to file | |
$LastEventString | Out-File $DatePath | |
} | |
Write-Output "Sent $c events to graylog" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment