Get-m365ProductIDTable.ps1
<#PSScriptInfo
.VERSION 1.4.1 .GUID 79801e88-d136-4955-8730-07ae1dd65cb1 .AUTHOR June Castillote .COMPANYNAME june.castillote@gmail.com .COPYRIGHT june.castillote@gmail.com .TAGS Office 365 License, License Name, Friendly Name, Microsoft 365 License Name .LICENSEURI https://github.com/junecastillote/Microsoft-365-License-Friendly-Names/blob/master/LICENSE .PROJECTURI https://github.com/junecastillote/Microsoft-365-License-Friendly-Names .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES https://github.com/junecastillote/Microsoft-365-License-Friendly-Names/blob/master/release_notes.md #> <# .DESCRIPTION Get license IDs and friendly names directly from MS article in GitHub .SYNOPSIS Get the friendly names from the Microsoft Document "Product names and service plan identifiers for licensing" .DESCRIPTION This script downloads and parses the licensing-service-plan-reference.md file from GitHub and converts to a PowerShell object. .EXAMPLE PS C:\> .\Get-m365ProductIDTable.ps1 Get the product names and service plan identifiers online and display the result on the screen .EXAMPLE PS C:\> .\Get-m365ProductIDTable.ps1 | Export-Csv -NoTypeInformation .\m365-License-Reference.csv Get the product names and service plan identifiers and export to CSV. .EXAMPLE PS C:\> .\Get-m365ProductIDTable.ps1 -SkuId 245e6bf9-411e-481e-8611-5c08595e2988 Get the product names and service plan identifiers that matches the specified SkuId .EXAMPLE PS C:\> .\Get-m365ProductIDTable.ps1 -SkuPartNumber SPE_E5 Get the product names and service plan identifiers that matches the specified SkuPartNumber .EXAMPLE PS C:\> .\Get-m365ProductIDTable.ps1 -ForceOnline Force to download the SKU table from the online source and ignoring the locally available table version. #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( ## This is URL path to the the licensing reference table document from GitHub. ## The current working URL is the default value. ## In case Microsoft moved the document, use this parameter to point to the new URL. [parameter()] [string] $URL = 'https://raw.githubusercontent.com/MicrosoftDocs/entra-docs/main/docs/identity/users/licensing-service-plan-reference.md', # Return only the matching SkuId [Parameter(ParameterSetName = 'SkuId', Mandatory)] [ValidateNotNullOrEmpty()] [guid[]] $SkuId, # Return only the matching SkuPartNumber [Parameter(ParameterSetName = 'SkuPartNumber', Mandatory)] [ValidateNotNullOrEmpty()] [string[]] $SkuPartNumber, ## Force convert license names to title case. [parameter()] [switch] $TitleCase, ## Force to download the online version instead of checking table in the current session [parameter()] [switch] $ForceOnline, ## Specifiy the list delimiter for ChildServicePlan and ChildServicePlanName. ## Default character delimited is comma "," [parameter()] [string] $ListDelimiterCharacter ) function ShowResult { $visible_properties = [string[]]@('SkuName', 'SkuPartNumber', 'SkuId') [Management.Automation.PSMemberInfo[]]$default_properties = [System.Management.Automation.PSPropertySet]::new('DefaultDisplayPropertySet', $visible_properties ) $Global:SkuTable | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $default_properties -Force switch ($PSCmdlet.ParameterSetName) { # -SkuId <GUID> SkuId { Write-Verbose "Filtering by SkuId" foreach ($id in $SkuId) { $Global:SkuTable | Where-Object { $_.SkuId -eq $id } } } # -SkuPartNumber <SKU PART NUMBER> SkuPartNumber { Write-Verbose "Filtering by SkuPartNumber" foreach ($partNumber in $SkuPartNumber) { $Global:SkuTable | Where-Object { $_.SkuPartNumber -eq $partNumber } } } default { Write-Verbose "No filtering. Showing all results." $Global:SkuTable } } } $ErrorActionPreference = 'STOP' if ($ForceOnline) { $Global:SkuTable = @() } #https://learn.microsoft.com/en-us/entra/identity/users/licensing-service-plan-reference # Check first if the SKU table is already available in the session. This ensures that the script only downloads the online table once per session, unless the -ForceOnline switch is used. if ($Global:SkuTable) { Write-Verbose "SKU table exists in session." return ShowResult } # Continue if the SKU table is not yet in the session Write-Verbose "Downloading SKU table online..." ## Parse the Markdown Table from the $URL try { $raw_Table = Invoke-RestMethod -Uri $URL -ErrorAction Stop $raw_Table = $raw_Table -split "`n" } catch { Write-Output "There was an error getting the licensing reference table at [$URL]. Please make sure that the URL is still valid." Write-Output $_.Exception.Message return $null } ## Determine the starting row index of the table $startLine = ($raw_Table.IndexOf('| Product name | String ID | GUID | Service plans included | Service plans included (friendly names) |') + 1) ## Determine the ending index of the table $endLine = ($raw_Table.IndexOf('## Service plans that cannot be assigned at the same time') - 1) ## Extract the string in between the lines $startLine and $endLine $result = for ($i = $startLine; $i -lt $endLine; $i++) { if ($raw_Table[$i] -notlike "*---*") { $raw_Table[$i].Substring(1, $raw_Table[$i].Length - 1) } } ## Perform a little clean-up ## replace "[space] | [space]" with "|" ## replace "[space]<br/>[space]" with "," ## replace "((" with "(" ## replace "))" with ")" ## #replace ")[space](" with ")(" $result = $result ` -replace '\s*\|\s*', '|' ` -replace '\s*<br/>\s*', ',' ` -replace '\(\(', '(' ` -replace '\)\)', ')' ` -replace '\)\s*\(', ')(' # Force title case conversion if -TitleCase is not used. if (-not $PSBoundParameters.ContainsKey('TitleCase')) { $TitleCase = $true Write-Verbose "TitleCase name conversion enabled" } ## Create the result object $TextInfo = (Get-Culture).TextInfo # Set "," (comma) as the default delimiter character for ChildServicePlan and ChildServicePlanName if (-not $ListDelimiterCharacter) { $ListDelimiterCharacter = "," } $Global:SkuTable = @($result | ConvertFrom-Csv -Delimiter "|" -Header 'SkuName', 'SkuPartNumber', 'SkuID', 'ChildServicePlan', 'ChildServicePlanName' | ForEach-Object { if ($ListDelimiterCharacter -ne ",") { $childServicePlan = (([string]$_.ChildServicePlan).Split(",") | Sort-Object) -join $ListDelimiterCharacter $childServicePlanName = (([string]$_.ChildServicePlanName).Split(",") | Sort-Object) -join $ListDelimiterCharacter } else { $childServicePlan = $_.ChildServicePlan $childServicePlanName = $_.ChildServicePlanName } [pscustomobject]@{ SkuName = if ($TitleCase) { $TextInfo.ToTitleCase($_.SkuName) } else { $_.SkuName } SkuPartNumber = $_.SkuPartNumber SkuId = [guid]$_.SkuId ChildServicePlan = $childServicePlan ChildServicePlanName = if ($TitleCase) { $TextInfo.ToTitleCase($childServicePlanName) } else { $childServicePlanName } } }) ## return the result return ShowResult |