The script generates a report that includes information on Microsoft 365 licenses. The report will include numbers of available and used licenses in all registered tenants. To execute the script, create a report with corresponding custom columns. The report should have no scope or parameters
Parameters:
- $sumLicensesColumn - Specifies the identifier of the custom column that will store the full number of licenses (sum of available and used). To get the identifier:
- In the Report-specific columns section, on the Columns tab, right-click the custom column.
- In the context menu, navigate to Copy and click Column ID.
- The column identifier will be copied to clipboard.
- $availableLicensesColumn - Specifies the identifier of the custom column that will store the number of available licenses.
- $usedLicensesColumn - Specifies the identifier of the custom column that will store the number of used licenses.
- $tenantColumn - Specifies the identifier of the custom column that will store the name of the Microsoft 365 tenant.
PowerShell
$sumLicensesColumn = "{b78da4eb-6d3b-4ff3-ac1c-090d68b0498f}" # TODO: modify me
$availableLicensesColumn = "{2e05722c-9a82-4bd2-9059-5e5eee981e77}" # TODO: modify me
$usedLicensesColumn = "{51b55b9c-109f-49a6-ac8f-75c280996281}" # TODO: modify me
$tenantColumn = "{53a1bd7f-b3b3-4191-889e-948c92ed14f4}"
# Find all Microsoft 365 tenants
$configurationContainerPath = $Context.GetWellKnownContainerPath("CloudServicesO365")
$tenantSearcher = $Context.BindToObject($configurationContainerPath)
$tenantSearcher.Criteria = New-AdmCriteria "adm-O365Tenant"
$tenantSearcher.SearchScope = "ADS_SCOPE_SUBTREE"
try
{
# Execute search
$tenantSearchResultIterator = $tenantSearcher.ExecuteSearch()
$tenants = $tenantSearchResultIterator.FetchAll()
foreach ($tenantID in $tenants)
{
# Bind to the Tenant
$tenant = $Context.BindToObject($tenantID.AdsPath)
foreach ($sku in $tenant.Skus)
{
# Get license plan display name
if (-not([System.String]::IsNullOrEmpty($sku.CustomDisplayName)))
{
$skuDisplayName = $sku.CustomDisplayName
}
else
{
$skuDisplayName = $sku.DefaultDisplayName
}
$Context.Items.Add(-1, $skuDisplayName, "License", @{
$sumLicensesColumn = $sku.TotalUnits;
$availableLicensesColumn = $sku.TotalUnits - $sku.ConsumedUnits;
$usedLicensesColumn = $sku.ConsumedUnits;
$tenantColumn = $tenant.TenantName
})
}
}
}
finally
{
# Release resources
if ($tenantSearchResultIterator){ $tenantSearchResultIterator.Dispose() }
}
$licenseNameToSkuPartNumber = @{}
It seems like it should be populated with data later but is not.
Thank you for pointing this out. You are right, the line is not required in the script. We removed it.
Is there a way to filter and display only the Active SKUs, excluding all Disabled, Expired, etc.?
Unfortunately, there is no such possibility.