In mature hybrid environments, the usageLocation attribute is the silent gatekeeper of the Microsoft 365 license lifecycle. Without a valid ISO 3166-1 alpha-2 country code assigned to a user object, Entra ID prevents license assignment, resulting in provisioning failures during automated workflows. Relying on manual cloud-side updates is a recipe for drift; the source of truth must reside in your on-premises Active Directory.
usageLocation is null. Intune enrollment and Exchange Online mailbox creation can stall if this attribute is missing, regardless of your Group-Based Licensing configuration.
Architectural Implementation
To automate this, we modify the In from AD - User Common synchronization rule. We transform the c (Country) attribute into usageLocation using a direct mapping expression. If your AD c attribute stores full country names (e.g., "United States") instead of ISO codes ("US"), you must implement a lookup transformation in the sync rule.
Target Attribute: usageLocation Source: c Merge Type: Update
Start-ADSyncSyncCycle -PolicyType Delta. Use -PolicyType Initial only if the rule scoping filter itself has changed and existing connector space data must be re-evaluated.
Auditing and Remediation
Before applying the sync rule, identify users currently missing the usageLocation attribute to avoid mass-triggering license assignment failures or compliance flags. This requires the Microsoft.Graph.Users module and a connection scoped with User.Read.All.
try {
# Ensure required module is available
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Users)) {
throw "Required module 'Microsoft.Graph.Users' is not installed. Run: Install-Module Microsoft.Graph -Scope CurrentUser"
}
# Connect with the required read scope (uses existing context if already connected with sufficient scope)
$context = Get-MgContext
if (-not $context -or ($context.Scopes -notcontains "User.Read.All")) {
Connect-MgGraph -Scopes "User.Read.All" -NoWelcome -ErrorAction Stop
$context = Get-MgContext
}
if (-not $context -or ($context.Scopes -notcontains "User.Read.All")) {
throw "Unable to establish a Microsoft Graph session with the required 'User.Read.All' scope."
}
# Audit enabled users with a missing usageLocation attribute
$usersMissingLocation = Get-MgUser -Filter "usageLocation eq null and accountEnabled eq true" `
-ConsistencyLevel eventual -CountVariable userCount `
-Property "displayName,userPrincipalName,usageLocation" `
-All `
-ErrorAction Stop |
Select-Object DisplayName, UserPrincipalName, UsageLocation
if (-not $usersMissingLocation) {
Write-Output "No enabled users found with a missing usageLocation attribute."
}
else {
$count = @($usersMissingLocation).Count
Write-Output "Found $count enabled user(s) with a missing usageLocation attribute:"
$usersMissingLocation
}
}
catch {
Write-Error "Failed to audit users for missing usageLocation: $($_.Exception.Message)"
}-ConsistencyLevel eventual combined with -CountVariable, otherwise Graph will reject the advanced query. If you are moving to a Cloud-Only lifecycle for specific identities, ensure your sync rules are scoped to exclude them, otherwise Entra Connect will overwrite any manual usageLocation changes you make via Graph API on the next sync cycle.
API Reference: Property Mapping
| Attribute | Source | Requirement |
|---|---|---|
c |
On-Prem AD | Required |
usageLocation |
Entra ID | Required |
co |
On-Prem AD | Optional |
Troubleshooting Sync Failures
If the attribute isn't flowing, check the Metaverse object via the Synchronization Service Manager. If the Metaverse attribute usageLocation is populated but the cloud attribute is not, you have a connector-space permission issue or a throttling event on the Azure AD Connector export step.
usageLocation back to on-premises AD via Writeback unless you have a strict schema governance policy. Writing cloud values back to AD often triggers a circular sync loop if the AD attribute is used as the authoritative source for other internal systems.
For large-scale remediation of existing users whose synchronization rule has not yet been deployed, use the following interim script. This bridges the gap by reading the AD c attribute directly and writing it to Entra ID via Graph, and should be retired once the sync rule transformation is live:
# Interim bulk remediation: set usageLocation for a specific regional OU/value
# Requires the ActiveDirectory module (RSAT) and Microsoft.Graph.Users
function Invoke-BulkUsageLocationRemediation {
[CmdletBinding()]
param(
[string]$CountryCode = "US"
)
try {
# Ensure ActiveDirectory module (RSAT) is available
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
Write-Error "Required module 'ActiveDirectory' is not installed. Install RSAT with: Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'"
return
}
Import-Module ActiveDirectory -ErrorAction Stop
# Ensure Microsoft.Graph.Users module is available
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Users)) {
Write-Error "Required module 'Microsoft.Graph.Users' is not installed. Run: Install-Module Microsoft.Graph -Scope CurrentUser"
return
}
Import-Module Microsoft.Graph.Users -ErrorAction Stop
# Connect with the required write scope, avoiding WAM interactive issues in headless/embedded terminals
$context = Get-MgContext
if (-not $context -or ($context.Scopes -notcontains "User.ReadWrite.All")) {
Connect-MgGraph -Scopes "User.ReadWrite.All" -UseDeviceCode -NoWelcome -ErrorAction Stop
$context = Get-MgContext
}
if (-not $context -or ($context.Scopes -notcontains "User.ReadWrite.All")) {
throw "Unable to establish a Microsoft Graph session with the required 'User.ReadWrite.All' scope."
}
# Retrieve on-prem AD users in the target region (country code parameterized)
$users = Get-ADUser -Filter "c -eq '$CountryCode'" -Properties c, UserPrincipalName -ErrorAction Stop
if (-not $users) {
Write-Output "No on-premises AD users found matching country code '$CountryCode'."
return
}
foreach ($user in $users) {
if ([string]::IsNullOrWhiteSpace($user.UserPrincipalName)) {
Write-Warning "Skipping user '$($user.SamAccountName)': missing UserPrincipalName."
continue
}
try {
Update-MgUser -UserId $user.UserPrincipalName -UsageLocation $CountryCode -ErrorAction Stop
Write-Output "Updated usageLocation for $($user.UserPrincipalName)."
}
catch {
Write-Error "Failed to update $($user.UserPrincipalName): $($_.Exception.Message)"
}
}
}
catch {
Write-Error "Bulk remediation failed: $($_.Exception.Message)"
}
}
Invoke-BulkUsageLocationRemediation -CountryCode "US"