This tutorial is about assigning Microsoft Office 365 Licenses through a Powershell Scipt in a Runbook in Azure Automation.

If you are managing an Office 365 tenant with Microsoft Office 365 E5, E5 or Azure AD Premium (P1) you are in the lucky situation licenses can be based on groups. But if you are using another product (eg. the Micrsoft Office 365 Business Suites) you only can assign license directly to users. https://docs.microsoft.com/en-us/azure/active-directory/users-groups-roles/licensing-group-advanced

See here the difference between a tenant with Office 365 E3 licenses and one with Office 365 Business Premium:

Tenant with Office 365 E3 licenses
Tenant with Office 365 Business Premium licenses

The solution is Azure Automation

The solution is to run a Powershell script through Azure Automation. This also can be run as a scheduled task on a Windows Server. But if you don’t have a server suitable for the task you can create a runbook through Azure Automation. That’s much cooler anyway!

Step 1. Create a License group for the users

First we need to create a group we want to assign licenses to:
– Go to https://admin.microsoft.com and logon with your admin credentials
– Click “Groups” -> “Add a group”
– Choose “Security Group” and click Next
– Enter a groupname and description, click Next
– Review your input and click “Create group”

If you have Azure ADConnect in place which synchronises your on-premises Active Directory to Office365 / Azure Active Directory it is also possible to make an Active Directory group for this purpose.

After you have created the group you might want to add some users to the group….

Step 2. Create an Azure subscription

If you already have an Azure subscription you can use that one. But if you don’t, you need to create one. Azure Automation is free to a certain point and number. But since there is a posibility you consume Azure credit you have to have a subscrition. Under normal circumstances you don’t have to worry this solutions costs money.

There is an option to create a free 12 month subscription and $200 free credit.
Go to https://portal.azure.com and start with a free trial.

Now you have to fill in your personal information and creditcard information. No worries, you won’t be charged unless you upgrade.

Your local Microsoft Partner can also provide you with an CSP subcription for which you will receive an invoice instead of credit card payment.

Step 3. Create a Resource group

Go to your subscription and create a resource group:
– Choose a suitable name
– Select the right region
– Click “Review + Create” and “Create”

Step 4. Create an Automation Account

Go back to the Azure portal and go to Automation Accounts

Click “Add”
1. Choose a suitable name
2. Select subscription
3. Select the previously created Resource Group
4. Hit “Create”

Step 5. Setup the credentials

Since the Automation needs credentials we need to add them to the automation account.
Upfront I created an license admin account throught the Office 365 Admin portal. The user needs the “User Admin” role. The “License Admin” role does not suffice.

Now go back to your Automation Account and and the create account as New Credential

Step 6. Add the MSOnline module to your automation account

Go to the Modules gallery in the automation account, search for “msonline” and select the module.

Click “Import” and “OK”

Now after a couple of minutes the module appears in the module list

Step 7. Create a Powershell Runbook

Now we are going to create a powershell runbook to execute the script from. Again in your Automation Account follow the instructions below.

Step 8. Determine the license you want to distribute

First we need the exact technical description of your license. Fire up a powershell prompt make sure you have the MSonline Modules installed. Connect by executing Connect-MsolService and enter Get-MsolAccountSku
In this case we need <yourtenantname>:O365_BUSINESS_PREMIUM

PS C:\Users\edwin> Connect-MsolService
PS C:\Users\edwin> Get-MsolAccountSku

AccountSkuId                           ActiveUnits WarningUnits ConsumedUnits
------------                           ----------- ------------ -------------
<yourtenantname>:O365_BUSINESS_PREMIUM 25          0            0

PS C:\Users\edwin>

In this case we see a license sku of Office 365 Business Premium which consists of several license plan options. If you do not want to give users all the options you can disable certain plans. First let’s tak a look at the plan options with Get-MsolAccountSku | select -ExpandProperty ServiceStatus

PS C:\Users\edwin> Get-MsolAccountSku | select -ExpandProperty ServiceStatus

ServicePlan                     ProvisioningStatus
-----------                     ------------------
MICROSOFT_SEARCH                Success
WHITEBOARD_PLAN1                Success
MYANALYTICS_P2                  Success
DYN365BC_MS_INVOICING           Success
KAIZALA_O365_P2                 Success
STREAM_O365_SMB                 Success
Deskless                        Success
BPOS_S_TODO_1                   Success
MICROSOFTBOOKINGS               Success
FORMS_PLAN_E1                   Success
FLOW_O365_P1                    Success
POWERAPPS_O365_P1               Success
O365_SB_Relationship_Management Success
TEAMS1                          Success
PROJECTWORKMANAGEMENT           Success
SWAY                            Success
INTUNE_O365                     PendingActivation
SHAREPOINTWAC                   PendingInput
OFFICE_BUSINESS                 Success
YAMMER_ENTERPRISE               Success
EXCHANGE_S_STANDARD             Success
MCOSTANDARD                     Success
SHAREPOINTSTANDARD              PendingInput

In the left column you see the different service plans. You can see the reference at https://docs.microsoft.com/en-gb/azure/active-directory/users-groups-roles/licensing-service-plan-reference
In this example we are going to prevent the use of Microsoft Sway (SWAY) and Microsoft Kaizala Pro (KAIZALA_0365_P2)

Step 9. Create the powershell script in the Runbook

Now we have all the parts it’s time to create the script.
Copy and Past the script below in your favorite editor and change the following lines:
9) Change the license admin user to the one you created in your Automation Account (Step 5). Use the name and not the username (if different)
14) Change the AccountSku and Plans you optionally want to disable. If you do not want to disable any plans simply remove the -DisabledPlans parameter and its options.
18) Change the name. This is a just a label
19) Change the LicenseSKU to match line 14
20) Change the group to the one you created or selected in Step 1
24) Change the location where the users reside. NL for the Netherlands in my case.

<#  
.SYNOPSIS 
    Script that assigns Office 365 licenses based on Group membership..
.NOTES 
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.     
#>

#Office 365 Admin Credentials
$Credential = Get-AutomationPSCredential -Name "licenseadmin@xxxxxxx.onmicrosoft.com"

#Connect to Office 365 
Connect-MsolService -Credential $Credential

$LicenseOptions = New-MsolLicenseOptions -AccountSkuId "xxxxxxxxxx:O365_BUSINESS_PREMIUM" -DisabledPlans "SWAY","KAIZALA_O365_P2"

$Licenses = @{
            
                 'Office365BusinessPremium' = @{ 
                          LicenseSKU = 'xxxxxxxxxx:O365_BUSINESS_PREMIUM'
                          Group = 'LIC_UsersOffice365BusinessPremium'
                        }
            }
    
$UsageLocation = 'NL'
    
#Get all currently licensed users and put them in a custom object
$LicensedUserDetails = Get-MsolUser -All | Where-Object {$_.IsLicensed -eq 'True'} | ForEach-Object {
 [pscustomobject]@{
            UserPrincipalName = $_.UserPrincipalName
            License = $_.Licenses.AccountSkuId
            }
 }
    
#Create array for users to change or delete
$UsersToChangeOrDelete = @()
    
foreach ($license in $Licenses.Keys) {
      
  #Get current group name and ObjectID from Hashtable
  $GroupName = $Licenses[$license].Group
  $GroupID = (Get-MsolGroup -All | Where-Object {$_.DisplayName -eq $GroupName}).ObjectId
  $AccountSKU = Get-MsolAccountSku | Where-Object {$_.AccountSKUID -eq $Licenses[$license].LicenseSKU}
    
  Write-Output "Checking for unlicensed $license users in group $GroupName with ObjectGuid $GroupID..."
  #Get all members of the group in current scope
  $GroupMembers = (Get-MsolGroupMember -GroupObjectId $GroupID -All).EmailAddress
  #Get all already licensed users in current scope
  $ActiveUsers = ($LicensedUserDetails | Where-Object {$_.License -eq $licenses[$license].LicenseSKU}).UserPrincipalName 
  $UsersToHandle = ''
    
    if ($GroupMembers) {  
        if ($ActiveUsers) {  
            #Compare $Groupmembers and $Activeusers
            #Users which are in the group but not licensed, will be added
            #Users licensed, but not, will be evaluated for deletion or change of license 
            $UsersToHandle = Compare-Object -ReferenceObject $GroupMembers -DifferenceObject $ActiveUsers -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            $UsersToAdd = ($UsersToHandle | Where-Object {$_.SideIndicator -eq '<='}).InputObject
            $UsersToChangeOrDelete += ($UsersToHandle | Where-Object {$_.SideIndicator -eq '=>'}).InputObject  
        } else {  
            #No licenses currently assigned for the license in scope, assign licenses to all group members.
            $UsersToAdd = $GroupMembers
        }
    
    } else {  
      Write-Warning  "Group $GroupName is empty - will process removal or move of all users with license $($AccountSKU.AccountSkuId)"
      #If no users are a member in the group, add them for deletion or change of license.
      $UsersToChangeOrDelete += $ActiveUsers
    }
            
  #Check the amount of licenses left...
  if ($AccountSKU.ActiveUnits - $AccountSKU.consumedunits -lt $UsersToAdd.Count) { 
        Write-Warning 'Not enough licenses for all users, please remove user licenses or buy more licenses'
  }
           
     foreach ($User in $UsersToAdd){
    
        #Process all users for license assignment, if not already licensed with the SKU in order.  
          if ((Get-MsolUser -UserPrincipalName $User).Licenses.AccountSkuId -notcontains $AccountSku.AccountSkuId) {
            try {  
                  #Assign UsageLocation and License.
                  Set-MsolUser -UserPrincipalName $User -UsageLocation $UsageLocation -ErrorAction Stop -WarningAction Stop
                  Set-MsolUserLicense -UserPrincipalName $User -AddLicenses $AccountSKU.AccountSkuId -LicenseOptions $LicenseOptions -ErrorAction Stop -WarningAction Stop
                  Write-Output "SUCCESS: Licensed $User with $license"
            } catch {  
                  Write-Warning "Error when licensing $User"
    
            }
            
          }  
     }
}
    
#Process users for change or deletion
if ($UsersToChangeOrDelete -ne $null) {
        foreach ($User in $UsersToChangeOrDelete) {
          if ($user -ne $null) {
    
            #Fetch users old license for later usage
            $OldLicense = ($LicensedUserDetails | Where-Object {$_.UserPrincipalName -eq $User}).License
                 
             #Loop through to check if the user group assignment has been changed, and put the old and the new license in a custom object.
             #Only one license group per user is currently supported.
             $ChangeLicense = $Licenses.Keys | ForEach-Object {
                  $GroupName = $Licenses[$_].Group
                  if (Get-MsolGroupMember -All -GroupObjectId (Get-MsolGroup -All | Where-Object {$_.DisplayName -eq $GroupName}).ObjectId | Where-Object {$_.EmailAddress -eq $User}) {
                     [pscustomobject]@{
                        OldLicense = $OldLicense
                        NewLicense = $Licenses[$_].LicenseSKU
                     }
                  } 
    
              }
    
              if ($ChangeLicense) {
                    #The user were assigned to another group, switch license to the new one.
                    try {  
                          Set-MsolUserLicense -UserPrincipalName $User -RemoveLicenses $ChangeLicense.OldLicense -AddLicenses $ChangeLicense.NewLicense -LicenseOptions $LicenseOptions -ErrorAction Stop -WarningAction Stop
                          Write-Output "SUCCESS: Changed license for user $User from $($ChangeLicense.OldLicense) to $($ChangeLicense.NewLicense)"
                    } catch {  
                          Write-Warning "Error when changing license on $User`r`n$_"
                    }
                      
              } else {  
                               
                    #The user is no longer a member of any license group, remove license
                    Write-Warning "$User is not a member of any group, license will be removed... "
                    try {  
                          Set-MsolUserLicense -UserPrincipalName $User -RemoveLicenses $OldLicense -ErrorAction Stop -WarningAction Stop
                          Write-Output "SUCCESS: Removed $OldLicense for $User"
                    } catch {  
                          Write-Warning "Error when removing license on user`r`n$_"
                    }
              }
         }
    }
}

After you edited the script you simply past it in your runbook and Click “Save”” and “Publish”.

Step 10. Test and Schedule the runbook

First we are going to test the correct working of our runbook/Script by hitting “Start”

  1. Hit “Refresh” a few times
  2. Wait for the script to be completed
  3. Click “All logs”
  4. See the licenses assigned to the users in the group.

Now go back to your Office 365 Portal and verify the users have the license assigned.

Once everything works go to Automation Account and let’s create a Schedule

After creating a schedule we have to link it to your Runbook. Go to the Runbook and click “Link to schedule”

Click Schedule, Select the schedule you created and click OK

Finished

Now you’ve done it. Users are getting a license assigned through Azure Automation. You’ve basically fixed Group Based licensing without having the licenses normally required for this task.
I hope you enjoyed reading and deploying…..

One Reply to “Assign Microsoft Office 365 Licenses through Azure Automation”

Leave a Reply