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