Translate Microsoft 365 License GUIDs to Product Names in PowerShell

Working with Microsoft 365 and Azure AD licenses and understanding them can be quite the task for many system administrators. Did you know that the official Microsoft Learn article lists more than 3230 different SKU IDs and names as of June 1st 2023?

A confused giraffe staring at a wall of hieroglyphs, digital art

The problem is that Graph or PowerShell Modules never return a product name that actually means something to an admin. The names you’ll see there are not the same ones we’re used to from the Azure or M365 Admin Portals.

Let’s take a look at what the subscribedSkus Graph Endpoint returns.
This is how we get a list of all SKUs a tenant is subscribed to.

$licenseSkus = Invoke-RestMethod -Method Get -Headers $Header -Uri ""

Obviously, most tenants have many different SKUs. For the sake of simplicity, I’ll just show the details of the last entry returned by Graph.

PS C:\Temp\GitHub\TeamsPhoneAutomation> $sku = $licenseSkus.value[-1]
PS C:\Temp\GitHub\TeamsPhoneAutomation> $sku

accountName : company
accountId : 4bffbf87-53a0-4fce-b58b-xxxxxxxxxxxx
appliesTo : User
capabilityStatus : Enabled
consumedUnits : 7
id : 4bffbf87-53a0-4fce-b58b-xxxxxxxxxxxx_18181a46-0d4e-45cd-891e-60aabd171b4e
skuId : 18181a46-0d4e-45cd-891e-60aabd171b4e
skuPartNumber : STANDARDPACK
subscriptionIds : {3474312c-2333-47bc-9d87-da1cfcb75f85}
prepaidUnits : @{enabled=9; suspended=0; warning=0}
servicePlans : {@{servicePlanId=a82fbf69-b4d7-49f4-83a6-915b2cf354f4; servicePlanName=VIVAENGAGE_CORE; provisioningStatus=PendingProvisioning;
appliesTo=User}, @{servicePlanId=199a5c09-e0ca-4e37-8f7c-b05d533e1ea2; servicePlanName=MICROSOFTBOOKINGS; provisioningStatus=Success;
appliesTo=User}, @{servicePlanId=b76fb638-6ba6-402a-b9f9-83d28acb3d86; servicePlanName=VIVA_LEARNING_SEEDED; provisioningStatus=Success;
appliesTo=User}, @{servicePlanId=db4d623d-b514-490b-b7ef-8885eee514de; servicePlanName=Nucleus; provisioningStatus=Success;

The skuPartNumber is what Microsoft calls String ID on their learn article. As we all know, Microsoft is no stranger to changing or rebranding product names. I have never seen them change an skuPartNumber though. Even though we can count on that name staying the same, it most likely won’t mean much to most people. Now what the heck is a STANDARDPACK!?

A quick search on the Microsoft Learn article will do the trick and give us the desired information. STANDARDPACK = Office 365 E1.
But there’s a better way!

What’s really nice is that Microsoft also provides a download link to a CSV file which contains all the SKUs, Ids and names. If it can be downloaded by using a browser, it can also be downloaded by PowerShell.

All we need to do is Invoke-RestMethod and use | ConvertFrom-CSV to convert the retrieved content to a PowerShell object.

$translationTable = Invoke-RestMethod -Method Get -Uri "" | ConvertFrom-Csv

The SKU information retrieved by graph is already in our $sku variable. So, all that’s left to do is a simple one-liner to search the $translationTable.GUID for a matching $sku.skuId .

$skuNamePretty = ($translationTable | Where-Object {$_.GUID -eq $sku.skuId} | Sort-Object Product_Display_Name -Unique).Product_Display_Name

This will translate the skuPartNumber to the familiar License name known from the admin portals.

PS C:\Temp\GitHub\TeamsPhoneAutomation> $skuNamePretty
Office 365 E1

I’ve put all the above code snippets together and created a little more sophisticated, yet still very basic script which will output a table with all the available license names. Please note that you’ll need an existing Azure AD App Registration/Service Principal which has at least the Organization.Read.All Graph permission.

The output will look like this.

Translated output of the script

As far as I understand, the download URL of the CSV should not be changed once a new version is uploaded. I verified this by viewing GitHubs commit history for that article.

GitHub Commit History

As you can see, even though the date in the note was updated, the URL stayed the same. That’s why it should be safe to assume that we won’t need to update that URL in any script where we download that list.

If you’re wondering what my use case for this was… I was creating an Azure runbook which monitors remaining free licenses and sends alerts to a Teams channel in case any of the licenses fall below a certain threshold. Obviously, I wanted to mention the well-known license name and not the string ID returned by Graph on the messages sent to Teams. Thanks to the CSV file hosted by Microsoft my scripts can now always grab the latest data and use that to transalte any license GUID into something more recognizable.

I hope that this helps you too create more robust and better scripts when you need to deal with licensing in MS Graph or PowerShell.



Martin Heusser | M365 Apps & Services MVP

I 'm a Microsoft MVP and work as a Microsoft Teams Voice Engineer. I like to share my knowledge about Teams, Power Automate, Azure and PowerShell on Medium.