Skip to content

Instantly share code, notes, and snippets.

@JustinGrote
Last active April 22, 2026 00:22
Show Gist options
  • Select an option

  • Save JustinGrote/29852aebc19dd235168130aaf23bc681 to your computer and use it in GitHub Desktop.

Select an option

Save JustinGrote/29852aebc19dd235168130aaf23bc681 to your computer and use it in GitHub Desktop.
Find the changes in an Azure Conditional Access Named Location when so many records exist it uses CompressedCidrIPRanges instead of CidrIPRanges
// The complete data we need gets split over multiple logs, so we have to recombine them. Export to CSV afterwards.
AuditLogs
| where OperationName == "Update policy"
| where isnotempty(CorrelationId) and isnotempty(AdditionalDetails)
| project TimeGenerated, CorrelationId, Identity, AdditionalDetails
| mv-apply d = AdditionalDetails on (
summarize
seq = tolong(take_anyif(tostring(d.value), tostring(coalesce(d.key, d.name)) == "seq")),
b = take_anyif(tostring(d.value), tostring(coalesce(d.key, d.name)) == "b")
)
| where isnotnull(seq) and isnotempty(b)
| project TimeGenerated, CorrelationId, Identity, seq, b
| order by CorrelationId asc, seq asc
| serialize
| summarize
combined = strcat_array(make_list(b), ""),
TimeGenerated = min(TimeGenerated),
Identity = take_any(Identity)
by CorrelationId
| extend combinedJson = parse_json(combined)
| extend properties = combinedJson.targetUpdatedProperties
| project-away combined,combinedJson
param(
$Path = '~\downloads\query_data.csv'
)
$logItems = Import-Csv $Path
function ConvertFrom-Base64DeflateText {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('InputObject')]
[string]$Base64Text,
[System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8
)
process {
$bytes = [Convert]::FromBase64String($Base64Text)
$inputStream = [System.IO.MemoryStream]::new($bytes)
$deflateStream = [System.IO.Compression.DeflateStream]::new(
$inputStream,
[System.IO.Compression.CompressionMode]::Decompress
)
$reader = [System.IO.StreamReader]::new($deflateStream, $Encoding)
try {
$reader.ReadToEnd()
} finally {
$reader.Dispose()
$deflateStream.Dispose()
$inputStream.Dispose()
}
}
}
$logItems
| Foreach-Object {
ConvertFrom-Json $_.properties
| Where-Object Name -EQ 'PolicyDetail'
| Add-Member -NotePropertyName 'Identity' -NotePropertyValue $_.Identity -PassThru
| Add-Member -NotePropertyName 'TimeGenerated' -NotePropertyValue $_.'TimeGenerated [Local Time]' -PassThru
}
| Foreach-Object {
$oldValue = $_.OldValue | ConvertFrom-Json |% CompressedCidrIPRanges | ConvertFrom-Base64DeflateText
$oldValues = $oldValue.split(',')
$newValue = $_.NewValue | ConvertFrom-Json |% CompressedCidrIPRanges | ConvertFrom-Base64DeflateText
$newValues = $newValue.split(',')
$diff = Compare-Object -ReferenceObject $oldValues -DifferenceObject $newValues
[PSCustomObject]@{
TimeGenerated = [DateTimeOffset]::Parse($_.TimeGenerated)
Identity = $_.Identity
Added = ($diff | Where-Object SideIndicator -EQ '=>').InputObject
Removed = ($diff | Where-Object SideIndicator -EQ '<=').InputObject
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment