We use cookies to improve your experience.
By your continued use of this site you accept such use.
For more details please see our privacy policy and cookies policy.

Script repository

Send notification if number of unused Microsoft 365 licenses falls below limit

April 14, 2022 Views: 12810

The scripts send an email notification if the number of unused licenses for the specified enabled Microsoft 365 license plans falls below a specific limit. To execute either of the scripts, create a Scheduled Task configured for the Domain-DNS object type.

Check all registered tenants

Parameters:

  • $limit - Specifies the minimum number of unused licenses. The script will send an email if the number of available licenses is below this limit.
  • $skus - Specifies the SKU Part Numbers of the licenses to check. If set to NULL, the script will check all the enabled licenses.
  • $to - Specifies email addresses of the email notification recipient(s).
  • $subject - Specifies the email notification subject.
  • $reportHeader - Specifies the email notification header.
  • $htmlTableCoulmns - Specifies column headers for the table that contains details on the number of Microsoft 365 (Office 365) licenses.
  • $reportFooter - Specifies the email notification footer.

To get the SKU Part Number of a license plan in Adaxes:

  1. In Adaxes Administration Console, expand the service node that represents your Adaxes service.
  2. Navigate to Configuration\Cloud Services and select Microsoft 365.
  3. Double-click the Microsoft 365 (Office 365) tenant to which the license belongs.
  4. Click the necessary license plan. The SKU Part Number is displayed below the Display Name field.
Edit Remove
PowerShell
$limit = 5 # TODO: modify me
$skus = @("ENTERPRISEPREMIUM", "ENTERPRISEPACK") # TODO: modify me

# E-mail settings
$to = "recipient@domain.com" # TODO: modify me
$subject = "List of Microsoft 365 plans that have $limit or less licenses available" # TODO: modify me
$reportHeader = "<h2>List of Microsoft 365 plans that have $limit or less licenses available</h2>"
$htmlTableColumns = "
<table border='1'>
    <tr>
        <th>License Plan</th>
        <th>Total Licenses</th>
        <th>Unused Licenses</th>
    </tr>
" # TODO: modify me
$reportFooter = "<hr /><p><i>Please do not reply to this e-mail, it has been sent to you for notification purposes only.</i></p>" # TODO: modify me

# Find all tenants
$microsoft365TenantsPath = $Context.GetWellKnownContainerPath("CloudServicesO365")
$searcher = $Context.BindToObject($microsoft365TenantsPath)
$searcher.SearchFilter = "(objectClass=adm-O365Tenant)"
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$searcher.PageSize = 500

try
{
    $searchResultIterator = $searcher.ExecuteSearch()
    $searchResults = $searchResultIterator.FetchAll()
    
    $report = $NULL
    foreach ($searchResult in $searchResults)
    {
        # Bind to the tenant
        $tenant = $Context.BindToObject($searchResult.AdsPath)
        
        # Check licenses
        $htmlTable = $NULL
        foreach ($sku in $tenant.Skus)
        {
            if (($skus -ne $NULL) -and ($skus -notcontains $sku.SkuPartNumber))
            {
                continue
            }
            
            $difference = $sku.TotalUnits - $sku.ConsumedUnits
            if ($difference -gt $limit)
            {
                continue
            }
            
            if ($htmlTable -eq $NULL)
            {
                $htmlTable += "<h3>$($tenant.TenantName)</h3>$htmlTableColumns"
            }
            
            $htmlTable += "<tr><td>$($sku.DefaultDisplayName)</td><td>$($sku.TotalUnits)</td><td>$difference</td></tr>"
        }
        if ($htmlTable -eq $NULL)
        {
            continue
        }
        
        $htmlTable += "</table>"
        $report += $htmlTable
    }
    
    if ($report -eq $NULL)
    {
        return # There are no licenses in any tenant that are below the limit
    }
    
    # Build report
    $html = $reportHeader + $report + $reportFooter
    
    # Send mail
    $Context.SendMail($to, $subject, $NULL, $html)
}
finally
{
    # Release resources
    $searchResultIterator.Dispose()
}

Check only the specified tenant

Parameters:

  • $limit - Specifies the minimum number of unused licenses. The script will send an email if the number of available licenses is below this limit.
  • $skus - Specifies the SKU Part Numbers of the licenses to check. If set to NULL, the script will check all the enabled licenses.
  • $tenantName - Specifies the name of the Microsoft 365 tenant to check licenses in. The variable should contain the tenant name as it is specified in Adaxes.
  • $to - Specifies email addresses of the email notification recipient(s).
  • $subject - Specifies the email notification subject.
  • $reportHeader - Specifies the email notification header.
  • $htmlTableCoulmns - Specifies column headers for the table that contains details on the number of Microsoft 365 (Office 365) licenses.
  • $reportFooter - Specifies the email notification footer.

To get the SKU Part Number of a license plan in Adaxes:

  1. In Adaxes Administration Console, expand the service node that represents your Adaxes service.
  2. Navigate to Configuration\Cloud Services and select Microsoft 365.
  3. Double-click the Microsoft 365 (Office 365) tenant to which the license belongs.
  4. Click the necessary license plan. The SKU Part Number is displayed below the Display Name field.
Edit Remove
PowerShell
$limit = 5 # TODO: modify me
$skus = @("ENTERPRISEPREMIUM", "ENTERPRISEPACK") # TODO: modify me
$tenantName = "My tenant" # TODO: modify me

# E-mail settings
$to = "recipient@domain.com" # TODO: modify me
$subject = "List of Microsoft 365 plans that have $limit or less licenses available" # TODO: modify me
$reportHeader = "<h2>List of Microsoft 365 plans that have $limit or less licenses available</h2>"
$htmlTableColumns = "
<table border='1'>
    <tr>
        <th>License Plan</th>
        <th>Total Licenses</th>
        <th>Unused Licenses</th>
    </tr>
" # TODO: modify me
$reportFooter = "<hr /><p><i>Please do not reply to this e-mail, it has been sent to you for notification purposes only.</i></p>" # TODO: modify me

# Bind to the Microsoft 365 tenant
$microsoft365TenantsPath = $Context.GetWellKnownContainerPath("CloudServicesO365")
$container = $Context.BindToObject($microsoft365TenantsPath)
$tenant = $container.GetObject("adm-O365Tenant", "CN=$tenantName")

# Check licenses
$htmlTable = $NULL
foreach ($sku in $tenant.Skus)
{
    if (($skus -ne $NULL) -and ($skus -notcontains $sku.SkuPartNumber))
    {
        continue
    }
    
    $difference = $sku.TotalUnits - $sku.ConsumedUnits
    if ($difference -gt $limit)
    {
        continue
    }
    
    if ($htmlTable -eq $NULL)
    {
        $htmlTable += "<h3>$($tenant.TenantName)</h3>$htmlTableColumns"
    }
    
    $htmlTable += "<tr><td>$($sku.DefaultDisplayName)</td><td>$($sku.TotalUnits)</td><td>$difference</td></tr>"
}

if ($htmlTable -eq $NULL)
{
    return # There are no licenses in the tenant that are below the limit
}

# Build report
$htmlTable += "</table>"
$html = $reportHeader + $htmlTable + $reportFooter

# Send mail
$Context.SendMail($to, $subject, $NULL, $html)

Comments 27
avatar
Dave T. May 13, 2019
Can the script be modified to only return values for the select products only or a specific subset of products?
O365_BUSINESS_ESSENTIALS
Office 365 Business
avatar
Support May 14, 2019

Hello Dave,

Yes, it is possible. We have updated the script with the possibility to specify SKU Part Numbers of the licenses to check.

avatar
Amz Jul 29, 2019
Running the script throws an error saying "You cannot call a method on null valued expression". Please help me fix. Have set $skus to NULL to check for all the license enabled in our tenant
avatar
Support Jul 30, 2019

Hello,

Could you, please, send us (support[at]adaxes.com) the script you are using with all the modifications and a screenshot of the full error message you are getting?

avatar
GRes Mar 05, 2020
Hello, I am receiving the same message as Amz. Did you update the script to fix this issue? Thanks.
avatar
Support Mar 05, 2020

Hello,

We were not able to reproduce the issue and the script works just fine. Could you, please, send us (support[at]adaxes.com) the script you are using with all the modifications and a screenshot of the full error message you are getting?

Also, please, specify how exactly the script is executed. A screenshot of the Scheduled Task that runs the script would be very helpful.

avatar
Jack Dec 03, 2020
This worked great. Thank you!
avatar
Daniel Sep 19, 2021
Can we make it not send the email at all if all SKUs are good on the counts?
We don't want a email at all unless it falls below on something.
avatar
Support Sep 22, 2021
Hello Daniel,

Sorry for the confusion, but we are not sure what exactly you mean. The current script sends an email notification only if at least one of the specified Microsoft 365 licenses falls below the limit. If none of the licenses fall below the limit no email notification is send. If the email is still sent, please, make sure that you are using the exact above script without any modifications.
avatar
Daniel Oct 13, 2021
Yes sorry its working as expected. But is there a way to add multiple TO emails?
avatar
Support Oct 13, 2021
Hello Daniel,

Yes, it is possible. Just specify all the addresses in the $to variable:

$to = "recipient@domain.com, recipient2@domain.com"
avatar
Raul Oct 19, 2021
How can I set different limits for different skus within the same email? or will it require a separate task for each limit?
avatar
Support Oct 20, 2021
Hello Raul,

Unfortunately, there is no such possibility in the current script. However, it is possible to modify the script to meet your needs. Please, describe the desired behavior in all the possible details with live examples.
avatar
Raul Oct 20, 2021
What we would like is a way to specify different limits that the script can select from based on limit quantity. So if there is one of the SKUs under $limit=10 is true, then it generates an email based on that, if there's one under the $limit=5 then it also generates one based on that, and so forth. I tried the following on my script, but it only executed it for $limit=3 and ignored the rest above:

$limit = 10
$skus = @("EXCHANGESTANDARD","MCOMEETADV","SPE_E3","IDENTITY_THREAT_PROTECTION","MCOEV","POWER_BI_PRO")

$limit = 5
$skus = @("EXCHANGEENTERPRISE","POWER_BI_PRO","PROJECTPROFESSIONAL")

$limit = 3
$skus = @("VISIOCLIENT","MEETING_ROOM","PROJECT_P1")
avatar
Support Oct 21, 2021
Hello Raul,

Thank you for clarifying. What about the email recipients? Should they be the same for all licenses?

Also, please, specify how Microsoft 365 licenses should be identified in the email notifications. Should that be the licenses SKU Part Numbers or their display names as specified in the tenant settings?

Any additional details will be much appreciated.
avatar
Raul Oct 21, 2021
Hi Support,

For our case only one recipient is needed; however, I can see how this could be really useful to be able to specify recipients to specific limits.

For identification purposes, the display names of each SKU are good enough. If there's a way to also include the SKU part number along with the display name then I think this would be perfect.

We also do manage a separate instance of Adaxes and MS tenant. I figured this will simply have to be managed separately with a script on that Adaxes admin console? Or is there a possibility to apply all of the above to multiple tenants from the same script? No big deal if this is not possible, but less management is always better :)
avatar
Support Oct 21, 2021
Hello Raul,

> For our case only one recipient is needed; however, I can see how this could be really useful to be able to specify recipients to specific limits.
We will update the script so that it is possible to specify a separate recipient for each license.

> We also do manage a separate instance of Adaxes and MS tenant.
If you have separate instances (not sharing common configuration) of Adaxes service you will need to configure the script execution separately on each of them.

> Or is there a possibility to apply all of the above to multiple tenants from the same script?
The script check licenses in all the Microsoft 365 tenants registered for the Adaxes service the script is executed by. If you need information about tenants included into the email, please, provide a sample email notification.
avatar
Casey Apr 13, 2022
Hi,

Is there a way to only report on the specific tenant rather than all the tenants?
avatar
Support Apr 14, 2022
Hello Casey,

Yes, it is possible. We added the corresponding script (Check only the specified tenant) to the article.
avatar
VonZ May 03, 2022
getting "You cannot call a method on a null-valued expression."

Looks like it's pointing to '"CloudServicesO365". What is that value exactly?

Thanks!
avatar
Support May 04, 2022
Hello,

The value is correct. For troubleshooting purposes, please, send us (support@adaxes.com) a full screenshot of the error you are facing.
avatar
Gareth Dec 20, 2022
Hi,

I do not seem to have the domain-dns object type available when selecting the object type, where can I find it?
avatar
Support Dec 20, 2022
Hello Gareth,

Could you, please, specify what version of Adaxes you are currently using? For information on how to check it, have a look at the following help article: https://www.adaxes.com/help/CheckServiceVersion.
avatar
Gareth Dec 20, 2022
Sure! My product version is 3.15.20817.0
avatar
Support Dec 20, 2022
Hello Gareth,

Thank you for specifying. The Domain-DNS object type is not present in Adaxes 2023 that you are using. You need to select the Domain object type. The rest remains the same.
avatar
Ray Bilyk Jun 14, 2023
In line 5, how do you add an additional email address other than creating a group?
$to = "recipient@domain.com" # TODO: modify me
[Product version=3.16.21408.0]
avatar
Support Jun 14, 2023
Hello Ray,

In your version of Adaxes, the following approach for multiple recipients should be used:

Edit Remove
PowerShell
$toAddresses = @("recipient@domain.com", "recipient2@domain.com") # TODO: modify me

...

# Send mail
foreach ($toAddress in $toAddresses)
{
    $Context.SendMail($toAddress, $subject, $NULL, $html)
}
Leave a comment
Loading...

Got questions?

Support Questions & Answers