Advanced query capabilities on Microsoft Entra ID objects

As Microsoft Entra continues to deliver more capabilities and improvements in stability, availability, and performance, Microsoft Graph also continues to evolve and scale to efficiently access the data. One way is through Microsoft Graph's increasing support for advanced query capabilities on various Microsoft Entra ID objects, also called directory objects, and their properties. For example, the addition of not (not), not equals (ne), and ends with (endsWith) operators on the $filter query parameter.

The Microsoft Graph query engine uses an index store to fulfill query requests. To add support for additional query capabilities on some properties, these properties are now indexed in a separate store. This separate indexing allows Microsoft Entra ID to increase support and improve the performance of the query requests. However, these advanced query capabilities aren't available by default but, the requestor must also set the ConsistencyLevel header to eventual and, except for $search, use the $count query parameter. The ConsistencyLevel header and $count are referred to as advanced query parameters.

For example, to retrieve only inactive user accounts, you can run either of these queries that use the $filter query parameter.

Option 1: Use the $filter query parameter with the eq operator. This request works by default, that is, the request doesn't require the advanced query parameters.

GET https://graph.microsoft.com/v1.0/users?$filter=accountEnabled eq false

Option 2: Use the $filter query parameter with the ne operator. This request isn't supported by default because the ne operator is only supported in advanced queries. Therefore, you must add the ConsistencyLevel header set to eventual and use the $count=true query string.

GET https://graph.microsoft.com/v1.0/users?$filter=accountEnabled ne true&$count=true
ConsistencyLevel: eventual

Microsoft Entra ID (directory) objects that support advanced query capabilities

These advanced query capabilities are supported only on directory objects and their relationships, including the following frequently used objects:

Object Relationships
administrativeUnit
  • members
  • application
  • owners
  • appRoleAssignment -
    device
  • memberOf
  • transitiveMemberOf
  • registeredUsers
  • registeredOwners
  • group
  • members
  • transitiveMembers
  • memberOf
  • transitiveMemberOf
  • owners
  • appRoleAssignments
  • oAuth2PermissionGrant (delegated permission grants) -
    orgContact
  • memberOf
  • transitiveMemberOf
  • servicePrincipal
  • memberOf
  • transitiveMemberOf
  • appRoleAssignments
  • appRoleAssignmentsTo
  • oAuth2PermissionGrant
  • user
  • memberOf
  • transitiveMemberOf
  • ownedObjects
  • registeredDevices
  • ownedDevices
  • transitiveManagers
  • directReports
  • transitiveReports
  • appRoleAssignments
  • oAuth2PermissionGrant
  • Query scenarios that require advanced query capabilities

    The following table lists query scenarios on directory objects that are supported only in advanced queries:

    Description Example
    Use of $count as a URL segment GET ~/groups/$count
    Use of $count as a query string parameter GET ~/servicePrincipals?$count=true
    Use of $count in a $filter expression GET ~/users?$filter=assignedLicenses/$count eq 0&$count=true
    Use of $search GET ~/applications?$search="displayName:Browser"
    Use of $orderby on select properties GET ~/applications?$orderby=displayName&$count=true
    Use of $filter with the endsWith operator GET ~/users?$count=true&$filter=endsWith(mail,'@outlook.com')
    Use of $filter and $orderby in the same query GET ../applications?$orderby=displayName&$filter=startsWith(displayName, 'Box')&$count=true
    Use of $filter with the startsWith operators on specific properties. GET ~/users?$filter=startsWith(mobilePhone, '25478') OR startsWith(mobilePhone, '25473')&$count=true
    Use of $filter with ne and not operators GET ~/users?$filter=companyName ne null and NOT(companyName eq 'Microsoft')&$count=true
    Use of $filter with not and startsWith operators GET ~/users?$filter=NOT startsWith(displayName, 'Conf')&$count=true
    Use of $filter on a collection with endsWith operator GET ~/users?$count=true&$filter=proxyAddresses/any (p:endsWith(p, 'contoso.com'))&$select=id,displayName,proxyaddresses
    Use of OData cast with transitive members list GET ~/me/transitiveMemberOf/microsoft.graph.group?$count=true

    Note

    • Using $filter and $orderby together is supported only with advanced queries.
    • $expand is not currently supported with advanced queries.
    • The advanced query capabilities are currently not available for Azure AD B2C tenants.
    • To use advanced query capabilities in batch requests, specify the ConsistencyLevel header in the JSON body of the POST request.

    Support for filter by properties of Microsoft Entra ID (directory) objects

    Properties of directory objects behave differently in their support for query parameters. The following are common scenarios for directory objects:

    • Queries that are supported by default will also work with advanced query parameters, but the response will be eventually consistent.
    • The in operator is supported by default whenever eq operator is supported by default.
    • The endsWith operator is supported only with advanced query parameters by mail, otherMails, userPrincipalName, and proxyAddresses properties.
    • Getting empty collections (/$count eq 0, /$count ne 0) and collections with less than one object (/$count eq 1, /$count ne 1) is supported only with advanced query parameters.
    • The not and ne negation operators are supported only with advanced query parameters.
      • All properties that support the eq operator also supports the ne or not operators.
      • For queries that use the any lambda operator, use the not operator. See Filter using lambda operators.

    The following tables summarize support for $filter operators by properties of directory objects, and indicates where querying is supported through advanced query capabilities.

    Legend

    • Works by default. Does not require advanced query parameters. The $filter operator works by default for that property.
    • Requires advanced query parameters. The $filter operator requires advanced query parameters, which are:
      • ConsistencyLevel=eventual header
      • $count=true query string
    • Not supported. The $filter operator isn't supported on that property. Send us feedback to request that this property support $filter for your scenarios.
    • Blank cells indicate that the query isn't valid for that property.
    • The null value column indicates that the property is nullable and filterable using null.
    • Properties that aren't listed here don't support $filter at all.

    Administrative unit properties

    Property eq startsWith eq Null
    description
    displayName
    isMemberManagementRestricted
    membershipRule
    membershipRuleProcessingState
    scopedRoleMembers/any(s:s/id)

    Application properties

    Property eq startsWith ge/le eq Null
    appId
    createdDateTime
    createdOnBehalfOf/id
    description
    disabledByMicrosoftStatus
    displayName
    federatedIdentityCredentials/any(f:f/issuer)
    federatedIdentityCredentials/any(f:f/name)
    federatedIdentityCredentials/any(f:f/subject)
    identifierUris/any(p:p)
    info/logoUrl
    info/termsOfServiceUrl
    notes
    publicClient/redirectUris/any(p:p)
    publisherDomain
    requiredResourceAccess/any(r:r/resourceAppId)
    serviceManagementReference
    signInAudience
    spa/redirectUris/any(p:p)
    tags/any(p:p)
    uniqueName
    verifiedPublisher/displayName
    web/homePageUrl
    web/redirectUris/any(p:p)

    The following properties of the application entity support $count of a collection in a filter expression.

    Property eq Count 0 eq Count 1
    extensionProperties/$count
    federatedIdentityCredentials/$count

    Contract properties

    Property eq startsWith
    customerId
    defaultDomainName
    displayName

    Device properties

    Property eq startsWith ge/le eq Null
    accountEnabled
    alternativeSecurityIds/any(a:a/identityProvider)
    alternativeSecurityIds/any(a:a/type)
    approximateLastSignInDateTime
    deviceCategory
    deviceId
    deviceOwnership
    displayName
    enrollmentProfileName
    extensionAttributes/extensionAttribute1-15
    hostnames/any(p:p)
    isCompliant
    isManaged
    isRooted
    managementType
    manufacturer
    mdmAppId
    model
    onPremisesLastSyncDateTime
    onPremisesSecurityIdentifier
    onPremisesSyncEnabled
    operatingSystem
    operatingSystemVersion
    physicalIds/any(p:p)
    profileType
    registrationDateTime
    trustType

    The following properties of the device entity support $count of a collection in a filter expression.

    Property eq Count 0 eq Count 1
    physicalIds/$count
    systemLabels/$count

    Directory role properties

    Property eq startsWith eq Null
    description
    displayName
    roleTemplateId

    Group properties

    Property eq startsWith ge/le eq Null
    appRoleAssignments/any(a:a/id)
    assignedLicenses/any(a:a/skuId)
    classification
    createdByAppId
    createdOnBehalfOf/id
    description
    displayName
    expirationDateTime
    hasMembersWithLicenseErrors
    infoCatalogs/any(p:p)
    isAssignableToRole
    mail
    mailEnabled
    mailNickname
    membershipRule
    membershipRuleProcessingState
    onPremisesLastSyncDateTime
    onPremisesProvisioningErrors/any(o:o/category)
    onPremisesProvisioningErrors/any(o:o/propertyCausingError)
    onPremisesSamAccountName
    onPremisesSecurityIdentifier
    onPremisesSyncEnabled
    preferredLanguage
    proxyAddresses/any(p:p)
    renewedDateTime
    resourceBehaviorOptions/any(p:p)
    resourceProvisioningOptions/any(p:p)
    securityEnabled
    settings/any(s:s/displayName)
    settings/any(s:s/id)
    uniqueName

    The following properties of the group entity support $count of a collection in a filter expression.

    Property eq Count 0 eq Count 1
    assignedLicenses/$count
    onPremisesProvisioningErrors/$count
    proxyAddresses/$count

    Organizational contact properties

    Property eq startsWith ge/le eq Null
    companyName
    department
    displayName
    givenName
    jobTitle
    mail
    mailNickname
    manager/id
    onPremisesLastSyncDateTime
    onPremisesProvisioningErrors/any(o:o/category)
    onPremisesProvisioningErrors/any(o:o/propertyCausingError)
    onPremisesSyncEnabled
    proxyAddresses/any(p:p)
    surname

    The following properties of the orgContact entity support $count of a collection in a filter expression.

    Property eq Count 0 eq Count 1
    onPremisesProvisioningErrors/$count
    proxyAddresses/$count

    Service principal properties

    Property eq startsWith ge/le eq Null
    accountEnabled
    alternativeNames/any(p:p)
    appId
    appOwnerOrganizationId
    appRoleAssignedTo/any(a:a/id)
    appRoleAssignmentRequired
    appRoleAssignments/any(a:a/id)
    applicationTemplateId
    createdObjects/any(c:c/id)
    delegatedPermissionClassifications/any(d:d/id)
    description
    displayName
    federatedIdentityCredentials/any(f:f/issuer)
    federatedIdentityCredentials/any(f:f/name)
    federatedIdentityCredentials/any(f:f/subject)
    homepage
    info/logoUrl
    info/termsOfServiceUrl
    notes
    oauth2PermissionGrants/any(o:o/id)
    preferredSingleSignOnMode
    preferredTokenSigningKeyEndDateTime
    publisherName
    remoteDesktopSecurityConfiguration/id
    remoteDesktopSecurityConfiguration/targetDeviceGroups/any(t:t/displayName)
    remoteDesktopSecurityConfiguration/targetDeviceGroups/any(t:t/id)
    servicePrincipalNames/any(p:p)
    servicePrincipalType
    tags/any(p:p)
    verifiedPublisher/displayName

    The following properties of the servicePrincipal entity support $count of a collection in a filter expression.

    Property eq Count 0 eq Count 1
    federatedIdentityCredentials/$count
    ownedObjects/$count

    User properties

    Property eq startsWith ge/le eq Null
    accountEnabled
    ageGroup
    appRoleAssignments/any(a:a/id)
    assignedLicenses/any(a:a/skuId)
    assignedPlans/any(a:a/capabilityStatus)
    assignedPlans/any(a:a/service)
    assignedPlans/any(a:a/servicePlanId)
    authorizationInfo/certificateUserIds/any(p:p)
    businessPhones/any(p:p)
    city
    cloudRealtimeCommunicationInfo/isSipEnabled
    companyName
    consentProvidedForMinor
    country
    createdDateTime
    createdObjects/any(c:c/id)
    creationType
    department
    displayName
    employeeHireDate
    employeeId
    employeeOrgData/costCenter
    employeeOrgData/division
    employeeType
    externalUserState
    faxNumber
    givenName
    identities/any(i:i/issuer)
    imAddresses/any(p:p)
    infoCatalogs/any(p:p)
    isLicenseReconciliationNeeded
    isResourceAccount
    jobTitle
    licenseDetails/any(l:l/id)
    mail
    mailNickname
    manager/id
    mobilePhone
    oauth2PermissionGrants/any(o:o/id)
    officeLocation
    onPremisesDistinguishedName
    onPremisesExtensionAttributes/extensionAttribute1-15
    onPremisesImmutableId
    onPremisesLastSyncDateTime
    onPremisesProvisioningErrors/any(o:o/category)
    onPremisesProvisioningErrors/any(o:o/propertyCausingError)
    onPremisesSamAccountName
    onPremisesSecurityIdentifier
    onPremisesSipInfo/isSipEnabled
    onPremisesSyncEnabled
    otherMails/any(p:p)
    passwordPolicies
    passwordProfile/forceChangePasswordNextSignIn
    passwordProfile/forceChangePasswordNextSignInWithMfa
    postalCode
    preferredLanguage
    provisionedPlans/any(p:p/provisioningStatus)
    provisionedPlans/any(p:p/service)
    proxyAddresses/any(p:p)
    scopedRoleMemberOf/any(s:s/id)
    showInAddressList
    state
    streetAddress
    surname
    usageLocation
    userPrincipalName
    userType

    The following properties of the user entity support $count of a collection in a filter expression.

    Property eq Count 0 eq Count 1
    assignedLicenses/$count
    onPremisesProvisioningErrors/$count
    otherMails/$count
    ownedObjects/$count
    proxyAddresses/$count

    The following table shows support for $filter by other extension properties on the user object.

    Extension type eq startsWith eq null
    Schema extensions
    Open extensions
    Directory extensions

    Support for sorting by properties of Microsoft Entra ID (directory) objects

    The following table summarizes support for $orderby by properties of directory objects and indicates where sorting is supported through advanced query capabilities.

    Legend

    • Works by default. Does not require advanced query parameters. The $orderby operator works by default for that property.
    • Requires advanced query parameters. The $orderby operator requires advanced query parameters, which are:
      • ConsistencyLevel=eventual header
      • $count=true query string
    • Use of $filter and $orderby in the same query for directory objects always requires advanced query parameters. For more information, see Query scenarios that require advanced query capabilities.
    Directory object Property name $orderby
    administrativeUnit createdDateTime
    administrativeUnit deletedDateTime
    administrativeUnit displayName
    application createdDateTime
    application deletedDateTime
    application displayName
    orgContact createdDateTime
    orgContact displayName
    device approximateLastSignInDateTime
    device createdDateTime
    device deletedDateTime
    device displayName
    group deletedDateTime
    group displayName
    servicePrincipal createdDateTime
    servicePrincipal deletedDateTime
    servicePrincipal displayName
    user createdDateTime
    user deletedDateTime
    user displayName
    user userPrincipalName
    [all others] [all others]

    Error handling for advanced queries on directory objects

    Counting directory objects is only supported using the advanced queries parameters. If the ConsistencyLevel=eventual header isn't specified, the request returns an error when the $count URL segment is used or silently ignores the $count query parameter (?$count=true) if it's used.

    GET https://graph.microsoft.com/v1.0/users/$count
    
    {
        "error": {
            "code": "Request_BadRequest",
            "message": "$count is not currently supported.",
            "innerError": {
                "date": "2021-05-18T19:03:10",
                "request-id": "d9bbd4d8-bb2d-44e6-99a1-71a9516da744",
                "client-request-id": "539da3bd-942f-25db-636b-27f6f6e8eae4"
            }
        }
    }
    

    For directory objects, $search works only in advanced queries. If the ConsistencyLevel header isn't specified, the request returns an error.

    GET https://graph.microsoft.com/v1.0/applications?$search="displayName:Browser"
    
    {
        "error": {
            "code": "Request_UnsupportedQuery",
            "message": "Request with $search query parameter only works through MSGraph with a special request header: 'ConsistencyLevel: eventual'",
            "innerError": {
                "date": "2021-05-27T14:26:47",
                "request-id": "9b600954-ba11-4899-8ce9-6abad341f299",
                "client-request-id": "7964ef27-13a3-6ca4-ed7b-73c271110867"
            }
        }
    }
    

    If a property or query parameter in the URL is supported only in advanced queries but either the ConsistencyLevel header or the $count=true query string is missing, the request returns an error.

    GET https://graph.microsoft.com/beta/users?$filter=endsWith(userPrincipalName,'%23EXT%23@contoso.com')
    
    {
        "error": {
            "code": "Request_UnsupportedQuery",
            "message": "Operator 'endsWith' is not supported because the required parameters might be missing. Try adding $count=true query parameter and ConsistencyLevel:eventual header. Refer to https://aka.ms/graph-docs/advanced-queries for more information",
            "innerError": {
                "date": "2023-07-14T08:43:39",
                "request-id": "b3731da7-5c46-4c37-a8e5-b190124d2531",
                "client-request-id": "a1556ddf-4794-929d-0105-b753a78b4c68"
            }
        }
    }
    

    If a property hasn't been indexed to support a query parameter, even if the advanced query parameters are specified, the request returns an error.

    GET https://graph.microsoft.com/beta/groups?$filter=createdDateTime ge 2021-11-01&$count=true
    ConsistencyLevel: eventual
    
    {
        "error": {
            "code": "Request_UnsupportedQuery",
            "message": "Unsupported or invalid query filter clause specified for property 'createdDateTime' of resource 'Group'.",
            "innerError": {
                "date": "2023-07-14T08:42:44",
                "request-id": "b6a5f998-94c8-430d-846d-2eaae3031492",
                "client-request-id": "2be83e05-649e-2508-bcd9-62e666168fc8"
            }
        }
    }
    

    However, a request that includes query parameters might fail silently. For example, for unsupported query parameters and for unsupported combinations of query parameters. In these cases, examine the data returned by the request to determine whether the query parameters you specified had the desired effect. For example, in the following example, the @odata.count parameter is missing even if the query is successful.

    GET https://graph.microsoft.com/v1.0/users?$count=true
    
    HTTP/1.1 200 OK
    Content-type: application/json
    
    {
      "@odata.context":"https://graph.microsoft.com/v1.0/$metadata#users",
      "value":[
        {
          "displayName":"Oscar Ward",
          "mail":"oscarward@contoso.com",
          "userPrincipalName":"oscarward@contoso.com"
        }
      ]
    }