Skip to main content
Post

#PowerPlatformTip 165: 'Read On-Prem AD Attributes via the User Profile and $select'

#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 Graph GET /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.

Power Automate designer: the HTTP with Microsoft Entra ID action with Method GET and the Graphusers URL including the $select list of onPremises attributes 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.

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).

This post is licensed under CC BY 4.0 by the author.

Microsoft and the Power Platform product names (for example Power Apps, Power Automate, Copilot Studio) are trademarks of the Microsoft group of companies. This is an independent website. It is not operated, sponsored or endorsed by Microsoft.