Categories: ExchangeGFI

GFI MailEssentials 20 – How to count licenses for users?

Recently one of our Clients bought 750 licenses for GFI MailEssentials 20. As GFI on their websites claims that only Active Mailboxes are counted it was safe to assume that all Shared Mailboxes wouldn't count for the license limit. This allowed us to not have to delete old users mailboxes and simply make sure those mailboxes are either Shared Mailboxes or Disabled.

Problem Description

However the GFI Licensing interface was showing quite different story.

This wasn't good. Having double checked the count number in the Licensing section confirmed the information.

We've first tried to check things using standard method that makes a lot of sense in this scenario. Counting all mailboxes, counting all disabled mailboxes and getting all active mailboxes that way.

function GetUsersCount() {
    # Option 1
    $usersAll = (Get-Mailbox -ResultSize Unlimited).Count
    $usersDisabled = (Get-Mailbox -ResultSize Unlimited  | ?{$_.ExchangeUserAccountControl -eq 'AccountDisabled'}).Count
    write-host "Total Mailboxes = $usersAll"
    write-host "Total Disabled Mailboxes = $usersDisabled "
    write-host "Total Active Mailboxes = $($usersAll-$usersDisabled)"
}

However the result was a bit different then the one provided by GFI Interface

PS C:\Windows\system32> C:\ExchangeScripts\Manual\MailboxCount.ps1
Total Mailboxes = 1314
Total Disabled Mailboxes = 578 
Total Active Mailboxes = 736

Not knowing what it is we've actually asked GFI to provide information how they count the licenses. After a lot of going back and forth with GFI support there seems there is bad information on their website how it's counted along with bad counting in the product itself. It would seem the way they count the product licenses is by using LDAP query.

Solution

It seems the information which query is used for gathering the licenses is stored in C:\Program Files (x86)\GFI\MailEssentials\Attendant\debuglogs\usercntvsapi.txt

There are multiple queries used inside so we've created a simple way to check them

function GetUsersCountGFI () {
    # Option 2
    $strFilter = "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*))",
                 "(&(objectClass=msExchPrivateMDB)(msExchOwningServer=CN=MAIL1,CN=Servers,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=LOCAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL))",
                 "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))",
                 "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(homeMDB=CN=MailboxDatabase1,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=LOCAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))",
                 "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(homeMDB=CN=MailboxDatabase2,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=LOCAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))"
    foreach ($filter in $strFilter) {
        $objDomain = New-Object System.DirectoryServices.DirectoryEntry
        $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
        $objSearcher.SearchRoot = $objDomain
        $objSearcher.PageSize = 1000
        $objSearcher.Filter = $filter
        $objSearcher.SearchScope = "Subtree"
        $colResultsOption2 = $objSearcher.FindAll()
        write-host "Testing filter - $filter"
        write-host "Total License Count needed by GFI $($colResultsOption2.Count)"
    }
}

GetUsersCountGFI

After running the query the results were really weird. It would seem that depending on position of the databases the query would return different results on each server. So while on one server the query was showing over 750 user limit, on other server it was showing 361 users only.

Due to how the databases in Exchange were distributed server MAIL1 was reporting only Active Database users from MailboxDatabase1, server MAIL2 from MailboxDatabase2 and MAIL3 and MAIL4 were reporting over limit even thou the total count by 2 queries for MAIL1 and MAIL2 is giving 366 and 362 respectively.

Due to how the Mailbox Databases are spread across the servers it seems that GFI licensing counting is loosing it's count.

function GetUsersCount() {
    # Option 1
    $usersAll = (Get-Mailbox -ResultSize Unlimited).Count
    $usersDisabled = (Get-Mailbox -ResultSize Unlimited  | ?{$_.ExchangeUserAccountControl -eq 'AccountDisabled'}).Count
    write-host "Total Mailboxes = $usersAll"
    write-host "Total Disabled Mailboxes = $usersDisabled "
    write-host "Total Active Mailboxes = $($usersAll-$usersDisabled)"
}

function GetUsersCountGFI () {
    # Option 2
    $strFilter  = "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*))"
    $strFilter1 = "(&(objectClass=msExchPrivateMDB)(msExchOwningServer=CN=MAIL1,CN=Servers,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=LOCAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL))"
    $strFilter2 = "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))"
    $strFilter3 = "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(homeMDB=CN=MailboxDatabase1,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=LOCAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))"
    $strFilter4 = "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(homeMDB=CN=MailboxDatabase2,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=LOCAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))"  
    $strFilter5 = "(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(mail=*)(samAccountName=*)(!(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=549755813888)))"

        $objDomain = New-Object System.DirectoryServices.DirectoryEntry
        $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
        $objSearcher.SearchRoot = $objDomain
        $objSearcher.PageSize = 1000       
        $objSearcher.SearchScope = "Subtree"

        $objSearcher.Filter = $strFilter
        $colResultsOption = $objSearcher.FindAll()
        write-host "Total License Count needed according to GFI KB $($colResultsOption.Count)"

        $objSearcher.Filter = $strFilter1       
        $colResultsOption = $objSearcher.FindAll()
        write-host "Total License Count needed according to GFI Logs MAIL1 $($colResultsOption.Count)"

        $objSearcher.Filter = $strFilter2
        $colResultsOption = $objSearcher.FindAll()
        write-host "Total License Count needed according to GFI Logs MAIL1 Next Query $($colResultsOption.Count)"

        $objSearcher.Filter = $strFilter3
        $colResultsOption = $objSearcher.FindAll()
        write-host "Total License Count needed according to GFI Logs MAIL2 Query $($colResultsOption.Count)"
        
        $objSearcher.Filter = $strFilter4
        $colResultsOption = $objSearcher.FindAll()
        write-host "Total License Count needed according to GFI Logs MAIL2 Query $($colResultsOption.Count)"
        
        $objSearcher.Filter = $strFilter5
        $colResultsOption = $objSearcher.FindAll()
        write-host "Total License Count needed according to GFI Logs MAIL3 Query $($colResultsOption.Count)"

}

#ConnectExchange
GetUsersCount
GetUsersCountGFI

And the result:

Total Mailboxes = 1315
Total Disabled Mailboxes = 578 
Total Active Mailboxes = 737
Total License Count needed according to GFI KB 1102
Total License Count needed according to GFI Logs MAIL1 0
Total License Count needed according to GFI Logs MAIL1 Next Query 1050
Total License Count needed according to GFI Logs MAIL2 Query 366
Total License Count needed according to GFI Logs MAIL2 Query 362
Total License Count needed according to GFI Logs MAIL3 Query 1050

The total count of mailboxes should be 727 according to own GFI queries, yet it's quite different depending where you look. We've reported this to GFI as bug, but for now your results may vary depending on which server you ask for the license count.

This post was last modified on March 20, 2016 12:24

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Active Directory Replication Summary to your Email or Microsoft Teams

Active Directory replication is a critical process that ensures the consistent and up-to-date state of…

2 weeks ago

Syncing Global Address List (GAL) to personal contacts and between Office 365 tenants with PowerShell

Hey there! Today, I wanted to introduce you to one of the small but excellent…

5 months ago

Active Directory Health Check using Microsoft Entra Connect Health Service

Active Directory (AD) is crucial in managing identities and resources within an organization. Ensuring its…

7 months ago

Seamless HTML Report Creation: Harness the Power of Markdown with PSWriteHTML PowerShell Module

In today's digital age, the ability to create compelling and informative HTML reports and documents…

8 months ago

How to Efficiently Remove Comments from Your PowerShell Script

As part of my daily development, I create lots of code that I subsequently comment…

9 months ago

Unlocking PowerShell Magic: Different Approach to Creating ‘Empty’ PSCustomObjects

Today I saw an article from Christian Ritter, "PowerShell: Creating an "empty" PSCustomObject" on X…

9 months ago