Powershell & PowerPlatform API ... now using the Graph API

Querying PowerPlatform API via. Powershell ... updated!

I’ve already written about Querying PowerPlatform API via. Powershell, but I received notice on Friday that Microsoft has now set a date when they will cease supporting the Azure AD Graph endpoint, which also means they’ll stop supporting license assignment operation from MSOnline and Azure AD Powershell modules. As of June 30, 2022, you’ll need to use the MS Graph API endpoint and/or the Microsoft Graph Powershell module.

What does this announcement from Microsoft mean? It means that I need to move away from calls to Connect-MsolService and Get-MsolUser in my script. You can see my script in my prior post.

Now, technically, they should still work because I’m not assigning licenses, only reading them. But I don’t want my scripts to be even more fragmented, or have different EOL dates.

 
Sidenote: Working with MS cloud services is a mess - some things are done with this module, others with that, some with this API, others with another API… oh wait, that module has been deprecated… get your act together Microsoft! Some modules work with Powershell 5.1, but others only work with 7. If Microsoft really wants people to embrace this (instead of having to use it out of necessessity), please, give us something well documented, mature and well-thought-out!

Ranting aside, I figured that it’d be a good opportunity to practise API calls from Powershell.

The two licenses that I’m interested are are related to Dynamics 365. Per Microsoft’s Product names and service plan identifiers for licensing, I need to query for:

GUID License Name
8e7a3d30-d97d-43ab-837c-d7701cef83dc DYN365_ENTERPRISE_TEAM_MEMBERS
1e1a282c-9c54-43a2-9310-98ef728faace DYN365_ENTERPRISE_SALES

Easy! Let’s query with a filter to get users with either license SKU:

Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://graph.microsoft.com/v1.0/users?$filter=(assignedLicenses/any(x:x/skuId eq 8e7a3d30-d97d-43ab-837c-d7701cef83dc)) or (assignedLicenses/any(x:x/skuId eq 1e1a282c-9c54-43a2-9310-98ef728faace))'

Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://gr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

Oof! That’s not so hot. All the Microsoft documentation and examples I could find only dealt with AND filters, or OR filters that did not query for the same value. I ended up choosing to make two calls, then merge the results:

$DYN365_ENTERPRISE_SALES = Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://graph.microsoft.com/v1.0/users?$filter=assignedLicenses/any(x:x/skuId eq 1e1a282c-9c54-43a2-9310-98ef728faace)&$select=userPrincipalName,assignedLicenses'
$DYN365_ENTERPRISE_TEAM_MEMBER = Invoke-RestMethod -Method get -Headers $headerParams -Uri 'https://graph.microsoft.com/v1.0/users?$filter=assignedLicenses/any(x:x/skuId eq 8e7a3d30-d97d-43ab-837c-d7701cef83dc)&$select=userPrincipalName,assignedLicenses'

$Users = Compare-Object -ReferenceObject $DYN365_ENTERPRISE_SALES.value.UserPrincipalName -DifferenceObject $DYN365_ENTERPRISE_TEAM_MEMBER.value.UserPrincipalName -IncludeEqual | Select-Object -ExpandProperty InputObject | Sort-Object

That gave me the results I needed.

I ended up asking on Reddit/r/PowerShell, and the end answer I got was that a few additional options are needed for Advanced query capabilities on Azure AD directory objects: -ConsistencyLevel = eventual needs to be set in the header -&$count=true needs to be included in the filter

Then something like this becomes possible:

https://graph.microsoft.com/v1.0/users?$filter=(assignedLicenses/any(x:x/skuId eq 06ebc4ee-1bb5-47dd-8120-11324bc54e06) or assignedLicenses/any(x:x/skuId eq 66b55226-6b4f-492c-910c-a3b7a3c9d993))&$count=true

One of the other great changes I ended up making was to add some table formatting in my output. I set the table style to a fixed-width font (Hack is a personal favourite) because Powershell output doesn’t align well with anything else. There’s nothing worse then great output in Powershell turned to gobblygook in an email client!

$msg = $Report | Sort-Object LastAccess, User | ConvertTo-Html -Head "<style>table{font-family:lucida console, courier new, monospace;font-size:12}</style>" | Out-String
Built with Hugo
Theme Stack designed by Jimmy