If you have ever wondered "how many unassigned licenses do I have in each SKU" or if someone has ever asked you "how many licenses are our clients paying for that they aren't using?", this is the script for you.  This script uses a credential that needs to have delegated administration rights to your tenants, then gets all those tenants, all their Office 365  SKUs and for each SKU compares the number of ConsumedUnits to the number of ActiveUnits.


function Get-Unassigned365Licenses 
        [Parameter(Mandatory = $True)] 
        [PSCredential]$Credential = (Get-Credential), 
        #Path to save exported .CSV File 
        [Parameter(Mandatory = $true)] 
    Import-Module MSOnline 
    $Tenants = (get-msolpartnercontract) 
    Write-Output "There are $($Tenants.count) tenants"  
    $TenantsLicenses = New-Object System.Collections.ArrayList; 
    Foreach ($Tenant in $Tenants) 
        $CompanyDetails = Get-MsolCompanyInformation -TenantId $Tenant.TenantId; 
        Write-Output "Processing:" $CompanyDetails.DisplayName; 
        $AccountSKU = Get-MsolAccountSku -TenantId $Tenant.TenantId | Where-Object {$_.AccountSkuId -notlike "*Power_BI*" -and $_.AccountSkuId -notlike "*PowerApps*"}; 
        Foreach ($Sku in $AccountSKU) 
            If ($Sku.ConsumedUnits -lt $Sku.ActiveUnits) 
                Write-Output "License $($Sku.AccountSkuId) has unassigned licenses" 
                $SkuInformation = @{ 
                    "Company Name" = $CompanyDetails.DisplayName; 
                    "License Type" = $Sku.AccountSkuId.Split(":")[1]; 
                    "Available" = $Sku.ActiveUnits; 
                    "Consumed" = $Sku.ConsumedUnits; 
                    "Unassigned" = ($Sku.ActiveUnits - $Sku.ConsumedUnits); 
                $SkuObject = New-Object PSObject -Property $SkuInformation; 
                $TenantsLicenses.Add($SkuObject| Out-Null; 
    $ResultsFilePath = $Path + "\" + "O365UnassignedLicenses.csv"; 
    Write-Output "Exporting results to:" $ResultsFilePath; 
    $TenantsLicenses | Export-Csv $ResultsFilePath -NoTypeInformation;