In my previous post about monitoring Conditional Access policy changes, I demonstrated how to close operational blind spots by proactively detecting policy modifications.
This is a critical oversight I needed to address. Named Locations are just as crucial to your Conditional Access security posture as the policies themselves. When someone modifies your trusted IP ranges or changes your approved country lists, you need to know about it immediately.
Why Named Locations Matter
Named Locations in Microsoft Entra ID define the network boundaries for your Conditional Access policies. They come in two types:
- IP Named Locations: Define trusted or untrusted IP address ranges (corporate offices, VPN endpoints, etc.)
- Country Named Locations: Define approved or blocked countries and continents for access
These locations are referenced by your Conditional Access policies to make trust decisions. If an attacker modifies your "Trusted Corporate IPs" location to include their malicious infrastructure, they can bypass your MFA requirements. Similarly, if someone accidentally removes a country from your approved list, legitimate users might be locked out.
The operational blindspot is the same: without monitoring, you won't know these critical security boundaries have changed until someone reports an access issue or you discover a breach.
The Enhanced Solution
I've updated the original script to include comprehensive Named Location monitoring alongside Conditional Access policy detection. The beauty of this approach is that it leverages the same Microsoft Graph APIs and audit log infrastructure, requiring only additional API calls and enhanced email formatting.
Code Additions: Dual Object Monitoring
The first enhancement expands the monitoring scope to include both object types:
# Get all Named Locations modified in the last 7 days
try {
$AllNamedLocations = Get-MgIdentityConditionalAccessNamedLocation
$ModifiedNamedLocations = $AllNamedLocations | Where-Object {
$_.ModifiedDateTime -ge $SevenDaysAgo
}
Write-Log "Found $($AllNamedLocations.Count) total Named Locations, $($ModifiedNamedLocations.Count) modified in the last 7 days" "INFO"
}
catch {
Write-Log "Failed to retrieve Named Locations: $($_.Exception.Message)" "ERROR"
}
# Exit only if BOTH policies and locations are unchanged
if ($ModifiedPolicies.Count -eq 0 -and $ModifiedNamedLocations.Count -eq 0) {
Write-Log "No Conditional Access policies or Named Locations were modified in the last 7 days. No alert needed." "INFO"
exit 0
}
The logic change here is subtle but important: the script now continues if either policies or locations have changed, providing comprehensive coverage of your Conditional Access infrastructure.
Enhanced Audit Log Correlation
Named Locations have their own audit log activity type, requiring separate queries to identify who made changes:
# Process Named Location changes
foreach ($Location in $ModifiedNamedLocations) {
try {
Write-Log "Processing Named Location: $($Location.DisplayName)" "INFO"
# Query audit logs for this specific named location
$AuditFilter = "activityDisplayName eq 'Update named location' and targetResources/any(x:x/displayName eq '$($Location.DisplayName)')"
$AuditLogs = Get-MgAuditLogDirectoryAudit -Filter $AuditFilter -Top 5 |
Where-Object { $_.ActivityDateTime -ge $SevenDaysAgo } |
Sort-Object ActivityDateTime -Descending |
Select-Object -First 1
# Extract user information (same logic as policies)
$ModifiedBy = if ($AuditLogs.InitiatedBy.User.UserPrincipalName) {
$AuditLogs.InitiatedBy.User.UserPrincipalName
} elseif ($AuditLogs.InitiatedBy.App.DisplayName) {
"$($AuditLogs.InitiatedBy.App.DisplayName) (Application)"
} else {
"System/Unknown"
}
The key difference is the audit filter: 'Update named location'
instead of 'Update conditional access policy'
. The user attribution logic remains identical, ensuring consistent identification of who made changes across both object types.
Intelligent Location Type Detection
Named Locations require special handling to provide meaningful context in alerts:
# Determine location type and additional info
$LocationType = "Unknown"
$AdditionalInfo = ""
if ($Location.'@odata.type' -eq '#microsoft.graph.ipNamedLocation') {
$LocationType = "IP Location"
$TrustedStatus = if ($Location.IsTrusted) { "Trusted" } else { "Untrusted" }
$AdditionalInfo = $TrustedStatus
}
elseif ($Location.'@odata.type' -eq '#microsoft.graph.countryNamedLocation') {
$LocationType = "Country Location"
$CountryCount = ($Location.CountriesAndRegions | Measure-Object).Count
$AdditionalInfo = "$CountryCount countries/regions"
}
This code examines the OData type to distinguish between IP and Country locations, then extracts relevant details:
- IP Locations: Shows whether the location is marked as "Trusted" or "Untrusted"
- Country Locations: Shows the count of countries/regions included
Visual Enhancement in Email Alerts
The email formatting has been enhanced to clearly distinguish between different types of changes:
# Different styling based on type
$TypeIcon = switch ($Change.Type) {
{ $_ -like "Conditional Access Policy" } { "🛡️" }
{ $_ -like "Named Location (IP Location)" } { "🌐" }
{ $_ -like "Named Location (Country Location)" } { "🗺️" }
default { "📍" }
}
$BorderColor = switch ($Change.Type) {
{ $_ -like "Conditional Access Policy" } { "#dc3545" } # Red
{ $_ -like "Named Location*" } { "#0078d4" } # Blue
default { "#17a2b8" }
}
This creates a visual hierarchy in the email:
- 🛡️ Red border: Conditional Access Policies (highest security impact)
- 🌐 Blue border: IP Named Locations (network security)
- 🗺️ Blue border: Country Named Locations (geographic security)
No Additional Permissions Required
The enhanced monitoring uses the same Microsoft Graph permissions as the original solution:
Policy.Read.All
- Covers both Conditional Access policies AND Named LocationsAuditLog.Read.All
- Provides audit trail for both object types
This means you can upgrade your existing monitoring without additional administrative approval or security reviews.
Conclusion
The enhanced Conditional Access monitoring solution now provides complete visibility into your security infrastructure changes. By extending monitoring to include Named Locations, you eliminate another critical operational blind spot that attackers could exploit.
Your Conditional Access policies are only as strong as the locations they trust. Now you'll know immediately when either changes, enabling rapid response to both legitimate administrative changes and potential security threats