Advanced query capabilities on Microsoft Entra ID objects
Article
As Microsoft Entra ID 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 eventualand, 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
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Users.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "accountEnabled eq false";
});
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new UsersRequestBuilderGetRequestConfiguration();
$queryParameters = UsersRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "accountEnabled eq false";
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->users()->get($requestConfiguration)->wait();
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 eventualand use the $count=true query string.
GET https://graph.microsoft.com/v1.0/users?$filter=accountEnabled ne true&$count=true
ConsistencyLevel: eventual
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Users.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "accountEnabled ne true";
requestConfiguration.QueryParameters.Count = true;
requestConfiguration.Headers.Add("ConsistencyLevel", "eventual");
});
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new UsersRequestBuilderGetRequestConfiguration();
$headers = [
'ConsistencyLevel' => 'eventual',
];
$requestConfiguration->headers = $headers;
$queryParameters = UsersRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "accountEnabled ne true";
$queryParameters->count = true;
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->users()->get($requestConfiguration)->wait();
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.
The following tables summarize support for $filter operators by properties of directory objects, and indicates where querying is supported through advanced query capabilities.
Legend
The $filter operator works by default for that property.
The $filter operator requiresadvanced query parameters, which are:
ConsistencyLevel=eventual header
$count=true query string
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
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
owners/$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
deviceId
displayName
extensionAttributes/extensionAttribute1
extensionAttributes/extensionAttribute10
extensionAttributes/extensionAttribute11
extensionAttributes/extensionAttribute12
extensionAttributes/extensionAttribute13
extensionAttributes/extensionAttribute14
extensionAttributes/extensionAttribute15
extensionAttributes/extensionAttribute2
extensionAttributes/extensionAttribute3
extensionAttributes/extensionAttribute4
extensionAttributes/extensionAttribute5
extensionAttributes/extensionAttribute6
extensionAttributes/extensionAttribute7
extensionAttributes/extensionAttribute8
extensionAttributes/extensionAttribute9
hostnames/any(p:p)
isCompliant
isManaged
mdmAppId
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.
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
The $orderby operator works by default for that property.
The $orderby operator requiresadvanced query parameters, which are:
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.
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
await graphClient.Users.Count.GetAsync();
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$graphServiceClient->users()->count()->get()->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
await graph_client.users.count.get()
GET https://graph.microsoft.com/v1.0/applications?$search="displayName:Browser"
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Applications.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Search = "\"displayName:Browser\"";
});
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new ApplicationsRequestBuilderGetRequestConfiguration();
$queryParameters = ApplicationsRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->search = "\"displayName:Browser\"";
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->applications()->get($requestConfiguration)->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
query_params = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetQueryParameters(
search = "\"displayName:Browser\"",
)
request_configuration = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetRequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.applications.get(request_configuration = request_configuration)
{
"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')
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Users.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "endsWith(userPrincipalName,'";
});
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new UsersRequestBuilderGetRequestConfiguration();
$queryParameters = UsersRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "endsWith(userPrincipalName,'";
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->users()->get($requestConfiguration)->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
query_params = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
filter = "endsWith(userPrincipalName,'",
)
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.users.get(request_configuration = request_configuration)
{
"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
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Groups.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "createdDateTime ge 2021-11-01";
requestConfiguration.QueryParameters.Count = true;
requestConfiguration.Headers.Add("ConsistencyLevel", "eventual");
});
// THE CLI IS IN PREVIEW. NON-PRODUCTION USE ONLY
mgc-beta groups list --filter "createdDateTime ge 2021-11-01" --count "true" --consistency-level "eventual"
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new GroupsRequestBuilderGetRequestConfiguration();
$headers = [
'ConsistencyLevel' => 'eventual',
];
$requestConfiguration->headers = $headers;
$queryParameters = GroupsRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->filter = "createdDateTime ge 2021-11-01";
$queryParameters->count = true;
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->groups()->get($requestConfiguration)->wait();
{
"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
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Users.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Count = true;
});
<?php
// THIS SNIPPET IS A PREVIEW VERSION OF THE SDK. NON-PRODUCTION USE ONLY
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestConfiguration = new UsersRequestBuilderGetRequestConfiguration();
$queryParameters = UsersRequestBuilderGetRequestConfiguration::createQueryParameters();
$queryParameters->count = true;
$requestConfiguration->queryParameters = $queryParameters;
$result = $graphServiceClient->users()->get($requestConfiguration)->wait();
# THE PYTHON SDK IS IN PREVIEW. FOR NON-PRODUCTION USE ONLY
graph_client = GraphServiceClient(credentials, scopes)
query_params = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
count = True,
)
request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.users.get(request_configuration = request_configuration)