Public/Functions/Licensing/Get-AzureAdLicense.ps1
# Module: TeamsFunctions # Function: Licensing # Author: Philipp, Scripting.up-in-the.cloud # Updated: 01-DEC-2020 # Status: RC function Get-AzureAdLicense { <# .SYNOPSIS License information for AzureAD Licenses related to Teams .DESCRIPTION Returns an Object containing all Teams related Licenses .EXAMPLE Get-TeamsLicense Returns 39 Azure AD Licenses that relate to Teams for use in other commands .NOTES Source https://scripting.up-in-the.cloud/licensing/o365-license-names-its-a-mess.html With very special thanks to Philip Reads https://docs.microsoft.com/en-us/azure/active-directory/users-groups-roles/licensing-service-plan-reference .COMPONENT Teams Migration and Enablement. License Assignment .ROLE Licensing .FUNCTIONALITY Returns a list of Licenses .LINK Get-TeamsTenantLicense Get-TeamsUserLicense Set-TeamsUserLicense Test-TeamsUserLicense Add-TeamsUserLicense (deprecated) Get-TeamsLicense Get-TeamsLicenseServicePlan Get-AzureAdLicense Get-AzureAdLicenseServicePlan #> [CmdletBinding()] [OutputType([Object[]])] param( [Parameter()] [switch]$FilterRelevantForTeams ) #param begin { Show-FunctionStatus -Level RC Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)" [System.Collections.ArrayList]$Products = @() $srcProductPlans = @{} $planServicePlanNames = @{} [System.Collections.ArrayList]$ProductsNotAdded = @() [System.Collections.ArrayList]$PlansNotAdded = @() } #begin process { #read the content of the Microsoft web page and extract the first table $url = "https://docs.microsoft.com/en-us/azure/active-directory/users-groups-roles/licensing-service-plan-reference" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $content = (Invoke-WebRequest $url -UseBasicParsing).Content $content = $content.SubString($content.IndexOf("<tbody>")) $content = $content.Substring(0, $content.IndexOf("</tbody>")) #eliminate line feeds so that we can use regular expression to get the table rows... $content = $content -replace "`r?`n", '' $rows = (Select-String -InputObject $content -Pattern "<tr>(.*?)</tr>" -AllMatches).Matches | ForEach-Object { $_.Groups[1].Value } #on each table row, get the column cell content # 1st cell contains the product display name # 2nd cell contains the Sku ID (called 'string ID' here) # 3rd cell contains the included service plans (with string IDs) # 3rd cell contains the included service plans (with display names) $rows | ForEach-Object { $cells = (Select-String -InputObject $_ -Pattern "<td>(.*?)</td>" -AllMatches).Matches | ForEach-Object { $_.Groups[1].Value } $srcProductName = $cells[0] $srcSkuPartNumber = $cells[1] $srcSkuId = $cells[2] $srcServicePlan = $cells[3] $srcServicePlanName = $cells[4] $srcProductPlans = $null [System.Collections.ArrayList]$srcProductPlans = @() #region Sub-Skus (Plans) # Preparing Plans if (($srcServicePlan.Trim() -ne '') -and ($srcServicePlanName.Trim() -ne '')) { #store the service plan string IDs for later match $srcServicePlan -split "<br.?>" | ForEach-Object { $planServicePlanName = ($_.SubString(0, $_.LastIndexOf("("))).Trim() $planServicePlanId = $_.SubString($_.LastIndexOf("(") + 1) if ($planServicePlanId.Contains(")")) { $planServicePlanId = $planServicePlanId.SubString(0, $planServicePlanId.IndexOf(")")) } if (-not $planServicePlanNames.ContainsKey($planServicePlanId)) { $planServicePlanNames.Add($planServicePlanId, $planServicePlanName) } } #get te included service plans $srcServicePlanName -split "<br.?>" | ForEach-Object { $planProductName = ($_.SubString(0, $_.LastIndexOf("("))).Trim() $planServicePlanId = $_.SubString($_.LastIndexOF("(") + 1) if ($planServicePlanId.Contains(")")) { $planServicePlanId = $planServicePlanId.SubString(0, $planServicePlanId.IndexOf(")")) } # Add RelevantForTeams if ( $planServicePlanNames[$planServicePlanId] ) { if ( $planServicePlanNames[$planServicePlanId].Contains('TEAMS') -or $planServicePlanNames[$planServicePlanId].Contains('MCO') ) { $Relevant = $true } else { $Relevant = $false } } else { $Relevant = $false } # reworking ProductName into TitleCase $TextInfo = (Get-Culture).TextInfo $planProductName = $TextInfo.ToTitleCase($planProductName.ToLower()) $planProductName = Format-StringRemoveSpecialCharacter -String $planProductName -SpecialCharacterToKeep "()+ -" # Building Object if ($srcProductPlans.ServicePlanId -notcontains $planServicePlanId) { try { [void]$srcProductPlans.Add([TFTeamsServicePlan]::new("$planProductName", "$($planServicePlanNames[$planServicePlanId])", "$planServicePlanId", $Relevant)) } catch { Write-Verbose "[TFTeamsServicePlan] Couldn't add entry for $planProductName" if ( $planProductName -ne "Powerapps For Office 365 K1") { $PlansNotAdded += $planProductName } } } } } #endregion #region Reworking Parameters # Adding ParameterName $ParameterName = switch ($srcSkuPartNumber) { # Main Licenses "M365EDU_A3_FACULTY" { "Microsoft365A3faculty" } "M365EDU_A3_STUDENT" { "Microsoft365A3students" } "M365EDU_A5_FACULTY" { "Microsoft365A5faculty" } "M365EDU_A5_STUDENT" { "Microsoft365A5students" } "SMB_BUSINESS_ESSENTIALS" { "Microsoft365BusinessBasic" } "SMB_BUSINESS_PREMIUM" { "Microsoft365BusinessStandard" } "SPB" { "Microsoft365BusinessPremium" } "SPE_E3" { "Microsoft365E3" } "SPE_E5" { "Microsoft365E5" } "M365_F1" { "Microsoft365F1" } "SPE_F1" { "Microsoft365F3" } "ENTERPRISEPREMIUM_FACULTY" { "Office365A5faculty" } "ENTERPRISEPREMIUM_STUDENT" { "Office365A5students" } "STANDARDPACK" { "Office365E1" } "STANDARDWOFFPACK" { "Office365E2" } "ENTERPRISEPACK" { "Office365E3" } "DEVELOPERPACK" { "Office365E3Dev" } "ENTERPRISEWITHSCAL" { "Office365E4" } "ENTERPRISEPREMIUM" { "Office365E5" } "ENTERPRISEPREMIUM_NOPSTNCONF" { "Office365E5NoAudioConferencing" } "DESKLESSPACK" { "Office365F1" } "SPE_E3_USGOV_DOD" { "Microsoft365E3USGOVDOD" } "SPE_E3_USGOV_GCCHIGH" { "Microsoft365E3USGOVGCCHIGH" } "ENTERPRISEPACK_USGOV_DOD" { "Office365E3USGOVDOD" } "ENTERPRISEPACK_USGOV_GCCHIGH" { "Office365E3USGOVGCCHIGH" } # Standalone Licenses "MCOCAP" { "CommonAreaPhone" } "PHONESYSTEM_VIRTUALUSER" { "PhoneSystemVirtualUser" } "MCOSTANDARD" { "SkypeOnlinePlan2" } # Add-on Licenses "MCOEV" { "PhoneSystem" } "MCOMEETADV" { "AudioConferencing" } # Microsoft Calling Plans "MCOPSTN2" { "InternationalCallingPlan" } "MCOPSTN1" { "DomesticCallingPlan" } "MCOPSTN5" { "DomesticCallingPlan120" } "MCOPSTN_5" { "DomesticCallingPlan120b" } "MCOPSTNC" { "CommunicationCredits" } default { "" } } # determining LicenseType if ( $srcProductPlans.Count -gt 1 ) { $LicenseType = "Package" $IncludesTeams = ($srcProductPlans.ServicePlanName -like "Teams*") $IncludesPhoneSystem = ( $srcProductPlans.ServicePlanName -like "MCOEV*") } else { $LicenseType = switch -Regex ( $srcProductPlans.ServicePlanName ) { "MCOPSTN" { "CallingPlan" } "MCOEV" { "Add-On" } "MCOMEETADV" { "Add-On" } default { "Standalone" } } $IncludesTeams = ($srcProductPlans.ServicePlanName -like "Teams*") $IncludesPhoneSystem = ( $srcProductPlans.ServicePlanName -like "MCOEV*") } # reworking ProductName into TitleCase $TextInfo = (Get-Culture).TextInfo $ProductName = $TextInfo.ToTitleCase($srcProductName.ToLower()) #Normalising "SKYPE FOR BUSINESS PSTN" from ProductName for Calling Plans $StringToCut = "Skype For Business Pstn " if ( $ProductName -match "$StringToCut" ) { $ProductName = $ProductName.Substring($StringToCut.Length, $ProductName.Length - $StringToCut.Length) } $ProductName = Format-StringRemoveSpecialCharacter -String $ProductName -SpecialCharacterToKeep "()+ " # Building Object try { [void]$Products.Add([TFTeamsLicense]::new( "$ProductName", "$srcSkuPartNumber", "$LicenseType", "$ParameterName", $IncludesTeams, $IncludesPhoneSystem, "$srcSkuId", $srcProductPlans)) } catch { Write-Verbose "[TFTeamsLicense] Couldn't add entry for '$ProductName'" -Verbose $ProductsNotAdded += $ProductName } } # Add License Products not on Website # Adding Common Area Phone [System.Collections.ArrayList]$ServicePlanCAP = @() [void]$ServicePlanCAP.Add([TFTeamsServicePlan]::new("Microsoft Teams", "TEAMS1", "57ff2da0-773e-42df-b2af-ffb7a2317929", $true)) [void]$ServicePlanCAP.Add([TFTeamsServicePlan]::new("Microsoft 365 Phone System", "MCOEV", "4828c8ec-dc2e-4779-b502-87ac9ce28ab7", $true)) [void]$ServicePlanCAP.Add([TFTeamsServicePlan]::new("Skype For Business Online (Plan 2)", "MCOSTANDARD", "0feaeb32-d00e-4d66-bd5a-43b5b83db82c", $true)) [void]$Products.Add([TFTeamsLicense]::new( "Common Area Phone", "MCOCAP", "Package", "CommonAreaPhone", $true, $true, "295a8eb0-f78d-45c7-8b5b-1eed5ed02dff", $ServicePlanCAP)) # Adding Microsoft 365 Phone System – Virtual User [System.Collections.ArrayList]$ServicePlanPSVU = @() [void]$ServicePlanPSVU.Add([TFTeamsServicePlan]::new("Phone System - Virtual User", "MCOEV_VIRTUALUSER", "f47330e9-c134-43b3-9993-e7f004506889", $true)) [void]$Products.Add([TFTeamsLicense]::new( "Microsoft 365 Phone System – Virtual User", "PHONESYSTEM_VIRTUALUSER", "Standalone", "PhoneSystemVirtualUser", $false, $true, "440eaaa8-b3e0-484b-a8be-62870b9ba70a", $ServicePlanPSVU)) # Adding Communication Credits [System.Collections.ArrayList]$ServicePlanCC = @() [void]$ServicePlanCC.Add([TFTeamsServicePlan]::new("Communications Credits", "MCOPSTNC", "505e180f-f7e0-4b65-91d4-00d670bbd18c", $true)) [void]$Products.Add([TFTeamsLicense]::new( "Communications Credits", "MCOPSTNC", "CallingPlan", "CommunicationsCredits", $false, $false, "47794cd0-f0e5-45c5-9033-2eb6b5fc84e0", $ServicePlanCC)) # Output if ( $ProductsNotAdded.Count -gt 0 ) { Write-Warning -Message "The following Products could not be added: $ProductsNotAdded" } $ProductsSorted = $Products | Sort-Object ProductName | Sort-Object LicenseType -Desc if ($FilterRelevantForTeams) { $ProductsSorted = $ProductsSorted | Where-Object { $_.ParameterName -NE "" -or $_.IncludesTeams -or $_.IncludesPhoneSystem } } return $ProductsSorted } #process end { Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)" } #end } #Get-AzureAdLicense |