Important note: The end of an era with licensing scripts is near⌠and the beginning of a new one with Azure AD Group Based Licensing is here. Group Based Licensing is now in preview and currently requires a paid Azure AD Subscription. Try it out and give Microsoft your feedback on how they can make it even better!Â
Using DirSync in combination with Office 365 / Windows Azure Active Directory is great.
It automates user creation and makes you able to master all user creation changes from on premises.
There is just one(or two) things you need to do manually, assign licenses…
This can be done both in the portal or with PowerShell.
Depending on how your license structure looks like and how large environment you have, you might want to automate this in a more convenient way. There is a Wiki article on TechNet with a few examples on how to automate it as well (both with Security Groups and AD attributes). http://social.technet.microsoft.com/wiki/contents/articles/15905.how-to-use-powershell-to-automatically-assign-licenses-to-your-office365-users.aspx
Until Microsoft has come up with an integrated solution for this in DirSync or something else, we have to stick with PowerShell…
The case:
In my case we want to assign Office 365 licenses based on a local AD attribute of your choice fully automated and minimal input.
We also want a bit of logging so we are able to find and fix errors as easy as possible.
In order to assign a license in Office 365, we need to assign two attributes on a user(of course the user must exist…):
UsageLocation and Licenses
Also, if a user has a valid license assigned, the boolean IsLicensed will be set to True.

The ‘Licenses’ attribute contains “tenantname:LICENSESKU”, which in my case above is “mstlegacy:ENTERPRISEPACK” for an E3 license.
So, when assigning a license with PowerShell we need to know the SKUID of the particular license we are using.
We can also disable specific parts of a license, for example SharePoint when we assign the license. More details about ‘manual’ PowerShell assignment of licenses in Office 365 you’ll find here.
Solution
My script activates Office 365 users based on the AD attribute of your choise.
It requires you to populated the AD attribute with a string that identifies the license type for the particular user.
Default AD attribute used in the script is employeeType.
Supported license types with AD attributes as below (attribute to the left):
E1
– Office 365 for Enterprises E1
E3
– Office 365 for Enterprises E3
K1
– Deskless user without Office
E2
– Deskless user with Office
A1S
– Office 365 for Education A1 (Students)
A2S
– Office 365 for Education A2 (Students)
A3S
– Office 365 for Education A3 (Students)
A1F
– Office 365 for Education A1 (Faculty)
A2F
– Office 365 for Education A2 (Faculty)
A3F
– Office 365 for Education A3 (Faculty)

In case the AD attribute Country is populated in your AD, it will automatically use that attribute to populate UsageLocation of the user in Office 365, otherwise it will default back to the parameter $DefaultCountry.
It will log all changes and errors to the logfile MSOL-UserActivation.log within the same folder you run the script.
Running the script
Prereqs:
Example – first time use:
As a preparation have to change the default value of $AdminUser parameter or use the parameter -AdminUser to an actual adminuser of your tenant.
On first use it’ll then ask you for the password and then store encrypted with DPAPI to a file in the same folder where you run the script. This so you can run without user interaction in the future.
.\ActivateMSOLUser.ps1 -AdminUser DirSync@mstlegacy.onmicrosoft.com

(Hopefully you will type in the correct password… đ )
After you’ve finished with the first time configuration, you are ready to actually start assigning licenses.
Example 1 – Activate all K1,K2 and E3 licenses with default AD attribute (employeeType)
.\ActivateMSOLUser.ps1 -AdminUser dirsync@mstlegacy.onmicrosoft.com -Licenses K1,K2,E3

As seen above, 2 unlicensed users were found in Office 365, but only one of them had the required local AD attribute (employeeType in this case), set to ‘E3’.
I also go two errors since I didn’t have any K1 or K2 licenses in my tenant.
Example 2 – Activate all E3 licenses with custom AD attribute (msDS-cloudExtensionAttribute1) and MasterOnPremise
.\ActivateMSOLUser.ps1 -AdminUser dirsync@mstlegacy.onmicrosoft.com -Licenses E3 -LicenseAttribute msDS-cloudExtensionAttribute1 -MasterOnPremise

In the above example we found one user to activate, but with the switch -MasterOnPremise we looked in to our local ad instead checking Office 365 for unlicensed users, and reported back to the attribute when the license was successfully assigned.
This can be useful if you for some reason have a lot of unlicensed users in Office 365 that you intend to keep that way.
Note: Since the -MasterOnPremise function writes back to your AD, the account that runs the script will in that case need write permissions to that AD attribute.

Next Step
In order to make this fully automated, you will also need to schedule this as a task that runs (preferably) as often as your DirSync goes which by default is every 3 hours, an article on how to do that is here.
I’ve been running the script for a while and it works very well, of course some parts can be done more efficient. If you find any bugs or other issues, let me know!
The script can be downloaded from here, or cut’n’paste it from below…
Happy Licensing!
/Johan
ActivateMSOLUsers.ps1
<# .SYNOPSIS The script automatically assigns licenses in Office 365 based on AD-attributes of your choice. .DESCRIPTION Fetches your Office 365 tenant Account SKU's in order to assign licenses based on them. Sets mandatory attribute UsageLocation for the user in Office 365 based on local AD attribute Country (or default) If switch -MasterOnPremise has been used: Assigns Office 365 licenses to the user based on an AD-attribute and reports back to the same attribute if a license was successfully assigned. If not: Looks after unlicensed users in Office365, checks if the unlicensed user has the right attribute in ad to be verified, otherwise not. .PARAMETER MasterOnPremise Switch, If used, it Assigns Office 365 licenses to the user based on an AD-attribute and reports back to the same attribute if a license was successfully assigned. .PARAMETER Licenses Array, defines the licenses used and to activate, specific set of licenses supported. "K1","K2","E1","E3","A1S","A2S","A3S","A1F","A2F","A3F" .PARAMETER LicenseAttribute String, the attribute used on premise to identify licenses .PARAMETER AdminUser Adminuser in tenant .PARAMETER DefaultCountry Defaultcountry for users if not defined in active directory .NOTES File Name: ActivateMSOLUsers.ps1 Author : Johan Dahlbom, johan[at]dahlbom.eu The script are provided âAS ISâ with no guarantees, no warranties, and they confer no rights. #>
param(
[parameter(Mandatory=$false)]
[Switch]
$MasterOnPremise = $false,
[parameter(Mandatory=$false,HelpMessage="Please provide the SKU's you want to activate")]
[ValidateSet("K1","K2","E1","E3","A1S","A2S","A3S","A1F","A2F","A3F")]
[ValidateNotNullOrEmpty()]
[array]
$Licenses,
[parameter(Mandatory=$false)]
[string]
$LicenseAttribute = "employeeType",
[parameter(Mandatory=$false)]
[string]
$AdminUser = "admin@tenant.onmicrosoft.com",
[parameter(Mandatory=$false)]
[string]
$DefaultCountry = "SE"
)
#Define variables
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$Logfile = ($PSScriptRoot + "\MSOL-UserActivation.log")
$TimeDate = Get-Date -Format "yyyy-MM-dd-HH:mm"
$SupportedSKUs = @{
"K1" = "DESKLESSPACK"
"K2" = "DESKLESSWOFFPACK"
"E1" = "STANDARDPACK"
"E3" = "ENTERPRISEPACK"
"A1S" = "STANDARDPACK_STUDENT"
"A2S" = "STANDARDWOFFPACK_STUDENT"
"A3S" = "ENTERPRISEPACK_STUDENT"
"A1F" = "STANDARDPACK_FACULTY"
"A2F" = "STANDARDWOFFPACK_FACULTY"
"A3F" = "ENTERPRISEPACK_FACULTY"
}
################################################
##### Functions
#Function to Import Modules with error handling
Function Import-MyModule
{
Param([string]$name)
if(-not(Get-Module -name $name))
{
if(Get-Module -ListAvailable $name)
{
Import-Module -Name $name
Write-Host "Imported module $name" -ForegroundColor Yellow
}
else
{
Throw "Module $name is not installed. Exiting..."
}
}
else
{
Write-Host "Module $name is already loaded..." -ForegroundColor Yellow }
}
#Function to log to file and write output to host
Function LogWrite
{
Param ([string]$Logstring)
Add-Content $Logfile -value $logstring -ErrorAction Stop
Write-Host $logstring
}
#Function to activate your users based on ad attribute
Function Activate-MsolUsers
{
Param([string]$SKU)
begin {
#Set counter to 0
$i = 0
}
process {
#Catch and log errors
trap {
$ErrorText = $_.ToString()
LogWrite "Error: $_"
$error.Clear()
Continue
}
#If the switch -MasterOnPremise has been used, start processing licenses from the AD-attribute
if ($MasterOnPremise) {
$UsersToActivate = Get-ADUser -filter {$LicenseAttribute -eq $SKU} -Properties $LicenseAttribute,Country -ErrorAction Stop
if ($UsersToActivate)
{
$NumUsers = ($UsersToActivate | Measure-Object).Count
LogWrite "Info: $NumUsers user(s) to activate with $SKU"
foreach($user in $UsersToActivate) {
trap {
$ErrorText = $_.ToString()
LogWrite "Error: $_"
$error.Clear()
Continue
}
$UPN = $user.userprincipalname
$Country = $user.Country
LogWrite "Info: Trying to assign licenses to: $UPN"
if (!($country)) {
$Country = $DefaultCountry }
if ((Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop)) {
Set-MsolUser -UserPrincipalName $UPN -UsageLocation $country -Erroraction Stop
Set-MsolUserLicense -UserPrincipalName $UPN -AddLicenses $SKUID -Erroraction Stop
#Verify License Assignment
if (Get-MsolUser -UserPrincipalName $UPN | Where-Object {$_.IsLicensed -eq $true}) {
Set-ADUser $user -Replace @{$LicenseAttribute=$SKU+' - Licensed at ' + $TimeDate}
LogWrite "Info: $upn successfully licensed with $SKU"
$i++;
}
else
{
LogWrite "Error: Failed to license $UPN with $SKU, please do further troubleshooting"
}
}
}
}
}
#If no switch has been used, process users and licenses from MSOnline
else {
$UsersToActivate = Get-MsolUser -UnlicensedUsersOnly -All
if ($Userstoactivate)
{
$NumUsers = $UsersToActivate.Count
LogWrite "Info: $NumUsers unlicensed user(s) in tenant: $($SKUID.ToLower().Split(':')[0]).onmicrosoft.com"
foreach ($user in $UsersToActivate) {
trap {
$ErrorText = $_.ToString()
LogWrite "Error: $_"
$error.Clear()
Continue
}
$UPN = $user.UserPrincipalName
$ADUser = Get-Aduser -Filter {userPrincipalName -eq $UPN} -Properties $LicenseAttribute,Country -ErrorAction Stop
$Country = $ADUser.Country
if (!($Country)) {
$Country = $DefaultCountry
}
if ($ADUser.$LicenseAttribute -eq $SKU) {
LogWrite "Info: Trying to assign licenses to: $UPN"
Set-MsolUser -UserPrincipalName $UPN -UsageLocation $country -Erroraction Stop
Set-MsolUserLicense -UserPrincipalName $UPN -AddLicenses $SKUID -Erroraction Stop
#Verify License Assignment
if (Get-MsolUser -UserPrincipalName $UPN | Where-Object {$_.IsLicensed -eq $true}) {
LogWrite "Info: $upn successfully licensed with $SKU"
$i++;
}
else
{
LogWrite "Error: Failed to license $UPN with $SKU, please do further troubleshooting"
}
}
}
}
}
}
End{
LogWrite "Info: $i user(s) was activated with $license ($SKUID)"
}
}
################################################
#Import modules required for the script to run
Import-MyModule MsOnline
Import-MyModule ActiveDirectory
#Start logging and check logfile access
try
{
LogWrite -Logstring "**************************************************`r`nLicense activation job started at $timedate`r`n**************************************************"
}
catch
{
Throw "You don't have write permissions to $logfile, please start an elevated PowerShell prompt or change NTFS permissions"
}
#Connect to Azure Active Directory
try
{
$PasswordFile = ($PSScriptRoot + "\$adminuser.txt")
if (!(Test-Path -Path $passwordfile))
{
Write-Host "You don't have an admin password assigned for $adminuser, please provide the password followed with enter below:" -Foregroundcolor Yellow
Read-Host -assecurestring | convertfrom-securestring | out-file $passwordfile
}
$password = get-content $passwordfile | convertto-securestring -ErrorAction Stop
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $adminuser,$password -ErrorAction Stop
Connect-MsolService -credential $credentials -ErrorAction Stop
}
catch [system.exception]
{
$err = $_.Exception.Message
LogWrite "Error: Could not Connect to Office 365, please check connection, username and password.`r`nError: $err"
exit
}
if (!$licenses) {
LogWrite "Error: No licenses specified, please specify a supported license`r`nInfo: Supported licenses are: K1,K2,E1,E3,A1S,A2S,A3S,A1F,A2F,A3F!"
}
else
{
#Start processing license assignment
foreach($license in $Licenses) {
$SKUID = (Get-MsolAccountSku | Where-Object {$_.AccountSkuId -like "*:$($SupportedSKUs.Get_Item($license))"}).AccountSkuId
if ($SKUID)
{
Activate-MsolUsers -SKU $license
}
else
{
LogWrite "Error: No $license licenses in your tenant!"
}
}
}
LogWrite -Logstring "**************************************************"