#PowerPlatformTip 165: 'Read On-Prem AD Attributes via the User Profile and $select'
TL;DR: The on-prem AD attributes synced by Entra Connect (
onPremisesSamAccountName,onPremisesDistinguishedName,onPremisesExtensionAttributes, …) are not returned by a default user profile lookup. Add?$select=onPremisesSamAccountName,onPremisesExtensionAttributes,…to a GraphGET /users/{upn}and they show up. Run it via HTTP with Microsoft Entra ID.
⚠️ Licensing: HTTP with Microsoft Entra ID is a Premium connector. The on-premises properties are available only when the user is synchronized from on-premises Active Directory.
You need a user’s old samAccountName, their AD distinguishedName, or one of the Exchange custom attributes (extensionAttribute1-15) inside a flow - classic hybrid scenarios like matching a line-of-business system or writing an on-prem-style username. You read the user profile… and the fields are empty or missing. They’re not missing - Graph just doesn’t hand them over unless you explicitly ask.
💡 Challenge
Your users are synced from on-premises Active Directory via Microsoft Entra Connect, so their AD attributes live in Entra ID as onPremises* properties. But a standard “Get user profile” only returns a small default set (displayName, mail, UPN, job title…). The on-prem fields you actually need - samAccountName, distinguishedName, the extension attributes - simply aren’t in the response.
✅ Solution
Microsoft Graph returns only a limited default property set for a user; every onPremises* attribute is flagged “requires $select to retrieve”. So name them explicitly in a $select on a GET /users/{id-or-upn} call and Graph includes them. From a flow, use the preauthorized HTTP with Microsoft Entra ID action (see #138). It avoids your own app registration but requires a Premium connector license.
⚠️ Note: The standard Office 365 Users → Get user profile (V2) action exposes only its own fixed field list and doesn’t surface the
onPremises*attributes - even with its Select box. That’s why we go to Graph directly here.
🔧 How It’s Done
1. Call Graph with an explicit $select.
🔸 HTTP with Microsoft Entra ID → Method GET, Uri (URL-encode the $select list, or let the action handle it):
1
https://graph.microsoft.com/v1.0/users/jane@contoso.com?$select=displayName,onPremisesSamAccountName,onPremisesUserPrincipalName,onPremisesDomainName,onPremisesDistinguishedName,onPremisesSecurityIdentifier,onPremisesImmutableId,onPremisesSyncEnabled,onPremisesLastSyncDateTime,onPremisesExtensionAttributes
🔸 Without the $select, none of these come back. With it, the JSON response contains each requested field.
The complete action: Method GET and the Url of the request pointing at /v1.0/users/{upn} with the $select list of onPremises* attributes. No headers or body are needed for this call.
2. Read the values from the response.
🔸 The synced AD login name is body('HTTP')?['onPremisesSamAccountName'], the OU path is body('HTTP')?['onPremisesDistinguishedName'], and the Exchange custom attributes sit under onPremisesExtensionAttributes, e.g. body('HTTP')?['onPremisesExtensionAttributes']?['extensionAttribute1'].
3. Confirm the user is actually synced.
🔸 Check onPremisesSyncEnabled: if it’s true, the onPremises* values are authoritative (read-only, sourced from AD). For cloud-only users it’s null/false and the on-prem fields are empty - handle that branch so your flow doesn’t break on cloud accounts.
4. Reuse the value.
🔸 Feed onPremisesSamAccountName into an on-prem SQL/LOB lookup, build a DOMAIN\samAccountName string, or map extensionAttribute* to your own business logic - all inside the same flow.
🎉 Result
Your flow now reads the real on-premises AD attributes - samAccountName, distinguishedName, the extension attributes and more - straight from the synced Entra ID user object. No AD connector, no on-prem gateway, just one Graph GET with the right $select.
🌟 Key Advantages
🔸 The hidden fields, revealed: $select is the only switch that turns on the onPremises* properties.
🔸 No gateway, no AD connector: you read the AD data that Entra Connect already synced into the cloud.
🔸 No custom app registration: HTTP with Microsoft Entra ID authenticates with the connection’s identity (#138); the connector is Premium.
🔸 Hybrid-friendly: perfect for matching users to legacy systems that still key on samAccountName or an OU.
🔁 Reverse lookup: find the user by samAccountName or extensionAttribute
The other direction (you hold the AD login name or an extensionAttribute value and need the Entra user) is a $filter on /users, and it only works with the advanced-query switches:
1
2
GET https://graph.microsoft.com/v1.0/users?$filter=onPremisesSamAccountName eq 'jdoe'&$count=true&$select=id,displayName,userPrincipalName
Header: ConsistencyLevel: eventual
Tested through HTTP with Microsoft Entra ID:
🔸 Without the header: 400 Request_UnsupportedQuery - “Unsupported or invalid query filter clause specified for property ‘onPremisesSamAccountName’ of resource ‘User’.”
🔸 Header set but no &$count=true: the same 400.
🔸 Header plus $count=true: 200 with @odata.count and value[].
The same rule applies to $filter=onPremisesExtensionAttributes/extensionAttribute1 eq 'x' (400 “…property ‘extensionAttribute1’…” without both, 200 with both) and to startsWith(onPremisesSamAccountName,'j'). In the action, the header goes into the Headers field as ConsistencyLevel / eventual; in the Office 365 Outlook Send an HTTP request action it would be CustomHeader1 = ConsistencyLevel: eventual, but that action rejects /users?$filter=... anyway (see FAQ 6).
🛠️ FAQ
1. Why are the fields empty even with $select?
The onPremises* attributes are only populated for users synchronized from on-premises AD via Microsoft Entra Connect. For cloud-only accounts they are not just empty, most of them are absent from the JSON: a $select=displayName,onPremisesSamAccountName,onPremisesSyncEnabled,onPremisesDistinguishedName,onPremisesLastSyncDateTime,onPremisesExtensionAttributes on a cloud-only user came back with only displayName and onPremisesExtensionAttributes (an object with extensionAttribute1 to extensionAttribute15, all null). So always read them with the safe navigator ?[...], and treat a missing onPremisesSyncEnabled as “cloud-only”.
2. How do I get the Exchange custom attributes 1-15?
Select onPremisesExtensionAttributes (the whole complex object), then drill in: ...?['onPremisesExtensionAttributes']?['extensionAttribute3']. Each holds up to 1024 characters and is read-only for synced users.
3. Which Graph permission do I need?
Reading these is a standard user read: User.Read.All (delegated or application) is sufficient. No special directory-write scope is required just to read the profile.
4. Can’t I just use the Office 365 Users connector?
Its Get user profile (V2) action returns a fixed schema that doesn’t include the onPremises* attributes, so its Select box won’t surface them. Go straight to Graph GET /users with $select to get these fields reliably.
5. What comes back without $select?
Only the default set came back: businessPhones, displayName, givenName, mail, mobilePhone, preferredLanguage, surname, userPrincipalName, id. No onPremises* property at all, which is the whole point of this tip.
6. Can I run this through the Office 365 Outlook “Send an HTTP request” action instead?
No. That action only accepts mailbox paths. GET /v1.0/me?$select=displayName,onPremisesSamAccountName,... and GET /v1.0/users?$filter=... both return 400: “URI path is not a valid Graph endpoint … Invalid resource, Allowed values: me,users. Invalid Object, Allowed values: messages,mailFolders,events,calendar,calendars,outlook,inferenceClassification.” Use HTTP with Microsoft Entra ID (#138) or the plain HTTP action.
7. The call fails with 404 Request_ResourceNotFound - what is wrong?
The UPN in the URL no longer exists, typically after a domain rename. The message reads “Resource ‘jane@old-domain.com’ does not exist or one of its queried reference-property objects are not present.” Query by object id (/users/{id}) when you have it; the id survives UPN changes.
🔗 Related Tips
- #PowerPlatformTip 138: Graph API via HTTP with Microsoft Entra ID, the auth-free way to run this GET.
- #PowerPlatformTip 95: Optimized SharePoint Queries, more on getting the most out of
$selectand$filter. - #PowerPlatformTip 58: HTTP Actions, the fundamentals of calling REST APIs from a flow.
This tip is by Marcel Lehmann, Microsoft MVP (Business Applications) and founder of the Power Platform Academy, which runs free live workshops (in German). See the next free workshop dates (in German). For companies that want this implemented for them, see KMUpower: Power Platform consulting & automation for SMEs (DACH).