How to Use /Any Filters in Microsoft Graph API Queries with PowerShell

Why Lambda Operators are Sometimes Needed

A reader asked about the meaning of x:x in a Graph API query included in the article about upgrading Office 365 PowerShell scripts to use the Graph. You see this construct (a Lambda operator) in queries like those necessary to find the set of accounts assigned a certain license. For example, to search for accounts assigned Office 365 E3 (its SKU or product identifier is always 6fd2c87f-b296-42f0-b197-1e91e994b900):$filter=assignedLicenses/any(s:s/skuId eq 6fd2c87f-b296-42f0-b197-1e91e994b900)

Find the set of Microsoft 365 Groups in the tenant:$filter=groupTypes/any(a:a eq 'unified')

Find the set of Teams in the tenant:$filter=resourceProvisioningOptions/Any(x:x eq 'Team')

As you might expect, because the cmdlets in the Microsoft Graph SDK for PowerShell essentially are wrappers around Graph API calls, these cmdlets use the same kind of filters. For example, here’s how to find accounts with the Office 365 licenses using the Get-MgUser cmdlet:

[array]$Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq 6fd2c87f-b296-42f0-b197-1e91e994b900)" -all

Lambda Operators

All these queries use lambda operators to filter objects using values applied to multi-valued properties. For example, the query to find users based on an assigned license depends on the data held in the assignedLicenses property of Azure AD accounts, while discovering the set of Teams in a tenant relies on checking the resourceProvisioningOptions property for Microsoft 365 groups. These properties hold multiple values or multiple sets of values rather than simple strings or numbers.

Accessing license information is a good example to discuss because Microsoft is deprecating the Azure AD cmdlets for license management at the end of 2022, forcing tenants to upgrade scripts which include these cmdlets to replace them with cmdlets from the Microsoft Graph SDK for PowerShell or Graph API calls. This article explains an example of upgrading a script to use the SDK cmdlets.

If we look at the value of assignedLicenses property for an account, we might see something like this, showing that the account holds three licenses, one of which has a disabled service plan.

disabledPlans                          skuId
-------------                          -----
{33c4f319-9bdd-48d6-9c4d-410b750a4a5a} 6fd2c87f-b296-42f0-b197-1e91e994b900
{}                                     1f2f344a-700d-42c9-9427-5cea1d5d7ba6
{}                                     8c4ce438-32a7-4ac5-91a6-e22ae08d9c8b

It’s obvious that assignedLicenses is a more complex property than a single-value property like an account’s display name, which can be retrieved in several ways. For instance, here’s the query with a filter to find users whose display name starts with Tony.$filter=startswith(displayName,'Tony')

As we’re discussing PowerShell here, remember that you must escape the dollar character in filters. Taking the example above, here’s how it is passed in PowerShell:

$Uri = "`$filter=startswith(displayName,'Tony')"
[array]$Users = Invoke-WebRequest -Method GET -Uri -ContentType "application/json" -Headers $Headers | ConvertFrom-Json

The data returned by the query is in the $Users array and can be processed like other PowerShell objects.

Using Any and All

Getting back to the lambda operators, while OData defines two (any and all), it seems like the all operator, which “applies a Boolean expression to each member of a collection and returns true if the expression is true for all members of the collection (otherwise it returns false)” is not used. At least, Microsoft’s documentation says it “is not supported by any property.”

As we’ve seen from the examples cited above, the any operator is used often. This operator “iteratively applies a Boolean expression to each member of a collection and returns true if the expression is true for any member of the collection, otherwise it returns false.”

If we look at the filter used to find accounts assigned a specific license:

filter=assignedLicenses/any(s:s/skuId eq 6fd2c87f-b296-42f0-b197-1e91e994b900)

My interpretation of the component parts (based on Microsoft documentation) of the filter is:

  • assignedLicenses is the parameter, or the property the filter is applied to. The property can contain a collection of values or a collection of entities. In this case, the assignedLicenses property for an account contains a collection of one or more license entities. Each license is composed of the SkuId and any disabled plans unavailable to the license holders.
  • s:sis a range variable that holds the current element of the collection during iteration.” The interesting thing is that you can give any name you like to the range variable. In this case, it could be license:license or even rubbish:debris. It’s just a name for a variable.
  • SkuId is the subparam, or value within the property being checked. When there’s only one value in a collection (as when you check for team-enabled groups), you don’t need to specify a subparam. In the case of assignedLicenses, it is needed because we want to match against the SkuId within the collection of entities in the assignedLicenses property.
  • 6fd2c87f-b296-42f0-b197-1e91e994b900 is the value to match against items.

I’m Not a Developer

All of this is second nature to professional developers but not so much to tenant administrators who want to develop some PowerShell scripts to automate operations. This then poses the question about how to discover when lambda qualifiers are needed. I don’t have a great answer except to look for examples in:

  • Microsoft’s Graph API documentation.
  • Code posted online as others describe their experiences working with the Graph APIs.

And when you find something which might seem like it could work, remember that the Graph Explorer is a great way to test queries against live data in your organization. Figure 1 shows the results of a query for license information.

Running a query with a lambda qualifier in the Graph Explorer
Figure 1: Running a query with a lambda qualifier in the Graph Explorer

Exploring the Mysteries of the Graph

One complaint often extended about Microsoft’s documentation for the Graph APIs is that it pays little attention to suitable PowerShell examples. The Graph SDK developers say that they understand this situation must change and they plan to improve their documentation for PowerShell over the next year. Although understandable that languages like Java and C# have been priorities up to now, Microsoft can’t expect the PowerShell community to embrace the Graph and learn its mysteries (like lambda qualifiers) without help. Let’s hope that the Graph SDK developers live up to their promise!

Learn how to exploit the Office 365 data available to tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

4 Replies to “How to Use /Any Filters in Microsoft Graph API Queries with PowerShell”

  1. Great Article, it explains a lot about lambda qualifiers and Graph queries. Appreciate your tremendous work.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.