Office 365 automated license management part 1

If you’ve been administering or enrolling tenants for Office 365, you’ve dealt with Licenses.

In Office 365 (or Azure and Intune!), various licensing plans are available and can be upgraded or downgraded at will quite flexibly.

However, the user interface for this is extremely limited when you start working with large numbers of users or start working with non O365 specific licenses such as EMS and there is no place or method to keep an amdministration or rulebase of who should have what license.

I’m writing a series of scripts to bulk automate user licensing in Office 365, and will share the source code with you. You can modify the code as you please, or ask me to do it for you. The code can be scheduled to run on any server with the Azure Active Directory Module and AD Management Shell installed to automatically license your users on a daily or custom basis.

The first use case

Contoso is an educational institution and wants to automate their license management in Office 365 due to frequent and large changes to their licensing. Students come and go, and many teachers work on a temporary basis. The users are all stored in Active Directory, but are maintained in and sourced from Magister. Since Magister is not the easiest source to script against with Powershell, our Locus of Control is Active Directory. Active Directory determines who gets which license.

Licenses available

  • 10.000 E1 for Education
  • 350 E3 for Education

Rules

1. All users may have an E1 license

2. If a user has the Active Directory Atrribute of employeeType set to ‘cao’ and is Enabled, he may receive a E3 license

3. If there are not enough E3 licenses to cover the result of rule 2, E3 licenses should be assigned on a first come first serve basis, the rest should get E1 as per rule 1

Now, to meet these rules, we will have to query Active Directory for all enabled users with the employeeType attribute set to cao and store these in an array. Since we’re working on a first come first serve basis, we will order this array by whenCreated.

Let’s look at our Active Directory query with these filters and sort it into a variable:

$users_e3 = Get-AdUser -Filter 'employeeType -eq "cao" -and Enabled -eq $True' -Properties * -ResultSetSize ($e3licenses.ActiveUnits-1) | Sort-Object -property whenCreated

These users MAY receive an E3 License. The -ResultSetSize parameter limits the number of users returned by the number of E3 licenses our Tenant has available. Sort-Object automatically sorts ascending, which covers the first come first serve rule.

Setting up the rules in Powershell is straightfoward:

get-msoluser | % {
    #Prep variables
    $upn = $_.UserPrincipalName
    $e3Allowed = $False
    $e3Found = $False
    $e1Found = $False
    $e3Added = $False

    #check if user is in list of E3 allowed users
    if($users_e3 | where-object{$_.UserPrincipalName -eq $upn}) {$e3Allowed = $True}

    #check licenses ready assigned
    foreach($lic in $_.Licenses){
        if($lic.AccountSkuID -like "*$($e3SkuId)*"){
            $e3Found = $True
        }
        if($lic.AccountSkuID -like "*$($e1SkuId)*"){
            $e1Found = $True
        }
    }
    #Remove E3 License if not allowed and currently assigned
    if($e3Found -eq $True -and $e3Allowed -ne $True){
            Set-MsolUserLicense -UserPrincipalName $upn -RemoveLicenses $e3licenses.AccountSkuId        
    }
    #Add E3 License if not assigned but allowed
    if($e3Found -eq $False -and $e3Allowed -eq $True){
            Set-MsolUserLicense -UserPrincipalName $upn -AddLicenses $e3licenses.AccountSkuId
            if($e1Found){
                Set-MsolUserLicense -UserPrincipalName $upn -RemoveLicenses $e1licenses.AccountSkuId
            }    
    }
    #Add E1 License if not present and not E3
    if($e1Found -eq $False -and $e3Allowed -ne $True){
            Set-MsolUserLicense -UserPrincipalName $upn -AddLicenses $e1licenses.AccountSkuId
    }
    
}

The full script, including connecting to O365 automatically, error trapping, logging etc:

Download: LicAdmin365_v0.1

Source:

########
#LicAdmin v0.1
#Copyright:     Free to use, please leave this header intact
#Author:        Jos Lieben (OGD)
#Company:       OGD (http://www.ogd.nl)
#Script help:   http://www.liebensraum.nl
#Purpose:       
########
#Requirements:
########
#MS Online Services Signin Assistant:
#https://www.microsoft.com/en-us/download/details.aspx?id=41950
#Azure AD Module (x64):
#http://social.technet.microsoft.com/wiki/contents/articles/28552.microsoft-azure-active-directory-powershell-module-version-release-history.aspx

$o365login     = "xxx@lieben.nu"           #Username of O365 Admin
$o365pw        = "MyPassword123"                          #Password of O365 Admin
$logfile       = ($env:APPDATA + "\LicAdmin365.log")	#Logfile in case of errors
$e3SkuId       = "ENTERPRISEPACK_FACULTY"               #SkuId of the E3 license, you can get a list of these with Get-MsolAccountSku
$e1SkuId       = "STANDARDWOFFPACK_STUDENT"             #SkuId of the E1 license, you can get a list of these with Get-MsolAccountSku

#Start script
ac $logfile "-----$(Get-Date) LicAdmin365 v0.1 $($env:COMPUTERNAME) Session log-----`n"

#build Credential Object
$secpasswd = ConvertTo-SecureString $o365pw -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential ($o365login, $secpasswd)

#Load modules
$env:PSModulePath += ";C:\Windows\System32\WindowsPowerShell\v1.0\Modules\"
try{
    Import-Module msonline
}catch{
    ac $logfile "Critical error, unable to load Azure AD module, did you install it?"
    ac $logfile $error[0]
    Exit
}
try{
    Import-Module activedirectory
}catch{
    ac $logfile "Critical error, unable to load ActiveDirectory module, did you install it?"
    ac $logfile $error[0]
    Exit
}

#connect to MSOL
try{
    Connect-MsolService -Credential $Credentials
}catch{
    ac $logfile "Critical error, unable to connect to O365, check the credentials"
    ac $logfile $error[0]
    Exit
}

#store available licenses
$licenses = Get-MsolAccountSku

$e3licenses = $licenses | where-object {$_.AccountSkuId -like "*$($e3SkuId)*"}
$e1licenses = $licenses | where-object {$_.AccountSkuId -like "*$($e1SkuId)*"}

$e3licenses | Add-Member Available ($e3licenses.ActiveUnits - $e3licenses.ConsumedUnits)
$e1licenses | Add-Member Available ($e1licenses.ActiveUnits - $e1licenses.ConsumedUnits)

ac $logfile "Current licensing status:"
ac $logfile "E1 Licences used: $($e1licenses.ConsumedUnits) / $($e1licenses.ActiveUnits)"
ac $logfile "E3 Licences used: $($e3licenses.ConsumedUnits) / $($e3licenses.ActiveUnits)"

#get all active users with cao as attribute type, limit the returned amount by available licenses, ordered by date hired
try{
    $users_e3 = Get-AdUser -Filter 'employeeType -eq "cao" -and Enabled -eq $True' -Properties * -ResultSetSize ($e3licenses.ActiveUnits-1) | Sort-Object -property whenCreated
}catch{
    ac $logfile "Critical Error while attempting to query Active Directory"
    ac $logfile $error[0]
    Exit
}
if($users_e3.Count -lt 1){
    ac $logfile "Critical error: Active Directory query did not return any results!"
    ac $logfile $error[0]
    Exit
}

#loop over all users in Office 365
get-msoluser -all | % {
    #Prep variables
    $upn = $_.UserPrincipalName
    $e3Allowed = $False
    $e3Found = $False
    $e1Found = $False
    $e3Added = $False

    #check if user is in list of E3 allowed users
    if($users_e3 | where-object{$_.UserPrincipalName -eq $upn}) {$e3Allowed = $True}

    #check licenses ready assigned
    foreach($lic in $_.Licenses){
        if($lic.AccountSkuID -like "*$($e3SkuId)*"){
            $e3Found = $True
        }
        if($lic.AccountSkuID -like "*$($e1SkuId)*"){
            $e1Found = $True
        }
    }
    #Remove E3 License if not allowed and currently assigned
	if($e3Found -eq $True -and $e3Allowed -ne $True){
        try{
            Set-MsolUserLicense -UserPrincipalName $upn -RemoveLicenses $e3licenses.AccountSkuId
            ac $logfile "E3 license removed for $upn"
        }catch{
            ac $logfile "Unable to remove E3 license from $upn"
            ac $logfile $error[0]
        }
        
    }
    #Add E3 License if not assigned but allowed
    if($e3Found -eq $False -and $e3Allowed -eq $True){
        try{
            Set-MsolUserLicense -UserPrincipalName $upn -AddLicenses $e3licenses.AccountSkuId
            ac $logfile "E3 license added to $upn" 
            if($e1Found){
                Set-MsolUserLicense -UserPrincipalName $upn -RemoveLicenses $e1licenses.AccountSkuId
                ac $logfile "E1 license removed from $upn" 
            }
        }catch{
            ac $logfile "Unable to add E3 license to or remove E1 license from $upn"
            ac $logfile $error[0]
            $e3Allowed -eq $False
        }      
    }
    #Add E1 License if not present and not E3
    if($e1Found -eq $False -and $e3Allowed -ne $True){
        try{
            Set-MsolUserLicense -UserPrincipalName $upn -AddLicenses $e1licenses.AccountSkuId
            ac $logfile "E1 license added to $upn"
        }catch{
            ac $logfile "Unable to add E1 license to $upn"
            ac $logfile $error[0]
        }        
    }
    
}

#end script
ac $logfile "-----$(Get-Date) LicAdmin365 v0.1 $($env:COMPUTERNAME) Session END-----`n" 


Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
trackback

[…] script will comply with your Recipient settings in Exchange (email proxies). It should be easy to adjust the script to also include licensing, or anything else that is currently done manually. Let me know if you need any […]

Soren Isaksen
Soren Isaksen
5 years ago

Hi, Great writing. Really like that you took the business perspective and not just the tech side of things. I your given scenario I would like to ask. Lets say that management agree that E3 licenses can be added (one at the time) as needed. So Next time a user (#351) comes in we want to add one more license. Now if two other users step Down we want to cut back to the 350 and remove a license from the pool. So we are back paying only for the 350 E3 licenses. Is that possible to achive by code/Azure… Read more »