Have had a few clients request scripts to automatically set assign licenses to users in Office 365 – Generally pretty simple. Recently I had a client ask to disable a particular service within a license – again, not all that difficult – unless you want to actually check if a license/service is already configured and not make any changes. Took a little while to work out, so figured I’d share it with those interested!
Just to set a license for a user is a pretty simple process – all you need is the license ‘SkuId’ value. To get a list of the ones available in your tenant, run: Get-MsolAccountSku. You’ll get a list of the available license SkuId’s and how many are active/consumed. In this article we’ll use an example SkuId of “Contoso:STANDARDWOFFPACK_IW_STUDENT”. Once you have the SkuId, all you need to run to assign the license is:
Set-MsolUser -UserPrincipalName user@contoso.com -UsageLocation AU
Set-MsolUserLicense -UserPrincipalName user@contoso.com -AddLicenses “Contoso:STANDARDWOFFPACK_IW_STUDENT”
Note: in order to assign a license, you also need to first assign a usage location to the user. If that’s already assigned, you can skip that line.
What if you didn’t want to have all the applications available for the user? For example, the above license includes Yammer Education. In this case, we need to create a ‘License Options’ object first.
$LicenseOption = New-MsolLicenseOptions -AccountSkuId “Contoso:STANDARDWOFFPACK_IW_STUDENT” -DisabledPlans YAMMER_EDU
Set-MsolUserLicense -UserPrincipalName user@contoso.com –LicenseOptions $LicenseOption
So where did we get the “YAMMER_EDU” from? You can list the available services for a license by running:
(Get-MsolAccountSku | where {$_.AccountSkuId -eq ‘Contoso:STANDARDWOFFPACK_IW_STUDENT’}).ServiceStatus
What if we wanted to disable multiple services in the License Option? The “-DisabledPlans” option accepts a comma-separated list. For example:
$LicenseOption = New-MsolLicenseOptions -AccountSkuId “Contoso:STANDARDWOFFPACK_IW_STUDENT” -DisabledPlans YAMMER_EDU, SWAY
Ok, so now we know how to get the available licenses and related services – as well as how to assign the license to the user. What if we wanted to check if a license is assigned to a user first? Personally, I’m not a huge fan of just re-stamping settings each time you run a script – so I thought I’d look into it. The easiest method I’ve found is to try bind to the license, then check if it’s $null or not:
$User = Get-MsolUser -UserPrincipalName user@contoso.com
$License = $User.Licenses | Where{$_.AccountSkuId -ieq “Contoso:STANDARDWOFFPACK_IW_STUDENT”}
If ($License) {Write-Host “Found License”} else { Write-Host “Didn’t Find License”}
From there we can do whatever we want – if the license is found and that’s all you care about, you can skip – otherwise you can use the other commands to set the license.
So what if we also want to make sure YAMMER_EDU is disabled as well? That’s a little trickier. First we need to bind to the license like we did above, then we need to check the status of the relevant ‘ServicePlan’.
$User = Get-MsolUser -UserPrincipalName user@contoso.com
$License = $User.Licenses | Where{$_.AccountSkuId -ieq “Contoso:STANDARDWOFFPACK_IW_STUDENT”}
If($License)
{
If($License.ServiceStatus | Where{$_.ServicePlan.ServiceName -ieq “YAMMER_EDU” -and $_.ProvisioningStatus -ine “Disabled”})
{
Write-Host “YAMMER_EDU isn’t disabled”
}
}
At this point it’s probably a good idea to talk about the structure of these objects – you may not need to know it, but for anyone trying to modify these commands it might be helpful.
- A ‘User’ object contains an attribute ‘Licenses’. This attribute is an array – as a user can have multiple licenses assigned.
- A ‘License’ object contains two attributes relevant to this script; ‘AccountSkuID’ and ‘ServiceStatus’
- AccountSkuId is the attribute that matches up with the AccountSkuId we’re using above
- ServiceStatus is another array – it contains an array of objects representing the individual services available in that license – and their status.
- The two attributes attached to a ‘ServiceStatus’ object that we care about are:
- ServicePlan.ServiceName – this is the name to match the service above (eg: YAMMER_EDU)
- ProvisioningStatus – this can be a bunch of values, but mostly ‘Success’, ‘Disabled’ or ‘PendingInput’. I’d assume there’s also ‘Provisioning’, but I’ve never seen it.
With this in mind, we can put together a script like the following – it reads the UPN and AccountSkuID from a CSV file, though you could use whatever source you like and update the script accordingly.
As with the previous scripts, you need the following:
- Powershell 3.0 or above
- Microsoft Online Services Sign-in Assistant (https://www.microsoft.com/en-us/download/details.aspx?id=41950)
- Windows Azure AD Module (http://go.microsoft.com/fwlink/p/?linkid=236297)
- A password stored in a secure file. In the script I use a file/key file pair – a good guide on this can be found here: http://www.adminarsenal.com/admin-arsenal-blog/secure-password-with-powershell-encrypting-credentials-part-2/
You’ll also need to update the 6 variables at the top of the script (paths, etc).
#Input File
$File = “D:\_Temp\ExchangeOnline\Source.csv”
#Log Variables
$LogFile = “D:\_Temp\ExchangeOnline\SetLicenses_$((Get-Date).ToString(“yyyyMMdd”)).log”
$AuditFile = “D:\_Temp\ExchangeOnline\SetLicenses_Audit.log”
#Credentials
$AdminUser = “admin@contoso.com”
$PasswordFile = “D:\_Temp\ExchangeOnline\EO_Password.txt”
$KeyFile = “D:\_Temp\ExchangeOnline\EO_AES.key”
Write-Output “$(Get-Date -format ‘G’) ========Script Started========” | Tee-Object $LogFile -Append
#Build the credentials object
Write-Output “$(Get-Date -format ‘G’) Creating credentials object” | Tee-Object $LogFile -Append
$key = Get-Content $KeyFile
$SecurePassword = Get-Content $PasswordFile | ConvertTo-SecureString -Key $key
$Creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AdminUser, $SecurePassword
#Import the MSOnline Module
IMport-Module MSOnline
#Connect to MSOnline
Write-Output “$(Get-Date -format ‘G’) Connecting to MSOnline” | Tee-Object $LogFile -Append
Connect-MsolService -Credential $Creds
#Grab the CSV contents
$CSV = Import-CSV $File
#Go through each entry
Foreach($Line in $CSV)
{
$samAccountName = $line.samAccountName
$UPN = $Line.UPN
$SKUID = $Line.license
Write-Output “$(Get-Date -format ‘G’) Processing User $UPN” | Tee-Object $LogFile -Append
#Make sure the user exists in MSOnline
If(Get-MsolUser -UserPrincipalName $UPN)
{
#Found in MSOnline. Put the user account into a variable
Write-Output “$(Get-Date -format ‘G’) – Located in MSOnline” | Tee-Object $LogFile -Append
$User = Get-MsolUser -UserPrincipalName $UPN
#Check the UsageLocation
If($User.UsageLocation -ine “AU”)
{
Write-Output “$(Get-Date -format ‘G’) – Location not set to AU. Updating…” | Tee-Object $LogFile -Append
#Update it
Set-MsolUser -UserPrincipalName $User.UserPrincipalName -UsageLocation AU
Write-Output “$(Get-Date -format ‘G’) $UPN Location set to AU” | Out-File $AuditFile -Append
}
#Check if the license is attached to the user
$SetLicense = $false
Write-Output “$(Get-Date -format ‘G’) – Checking for License: $SKUID” | Tee-Object $LogFile -Append
$License = $User.Licenses | Where{$_.AccountSkuId -ieq $SKUID}
If($License)
{
#License is attached. Check to make sure that any services to be disabled are actually disabled
Write-Output “$(Get-Date -format ‘G’) – License already attached. Checking if required services are disabled” | Tee-Object $LogFile -Append
If($License.ServiceStatus | Where{$_.ServicePlan.ServiceName -ieq “YAMMER_EDU” -and $_.ProvisioningStatus -ine “Disabled”})
{
Write-Output “$(Get-Date -format ‘G’) – YAMMER_EDU not disabled.” | Tee-Object $LogFile -Append
$SetLicense = $True
}
If($SetLicense){Write-Output “$(Get-Date -format ‘G’) – One or more services not disabled. License requires updating.” | Tee-Object $LogFile -Append}
}
Else
{
#License is not attached.
Write-Output “$(Get-Date -format ‘G’) – License is not attached. Will be attached.” | Tee-Object $LogFile -Append
$SetLicense = $True
}
If($SetLicense)
{
#License is not attached or not configured correctly. Build up the license with required options
$LicenseOption = New-MsolLicenseOptions -AccountSkuId $SKUID -DisabledPlans YAMMER_EDU
#Set the License
Write-Output “$(Get-Date -format ‘G’) – Setting/Updating license” | Tee-Object $LogFile -Append
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName –LicenseOptions $LicenseOption
Write-Output “$(Get-Date -format ‘G’) $UPN License set/updated for SkuId: $SKUID” | Out-File $AuditFile -Append
}
else
{
Write-Output “$(Get-Date -format ‘G’) – No changes to license required” | Tee-Object $LogFile -Append
}
# Clear loop variables for the next run
$samAccountName = $Null
$UPN = $Null
$SKUID = $Null
$User = $Null
$License = $Null
$SetLicense = $Null
$LicenseOption = $Null
}
else
{
Write-Output “$(Get-Date -format ‘G’) – Error: User not found in MSOnline” | Tee-Object $LogFile -Append
}
}
Write-Output “$(Get-Date -format ‘G’) ========Script Complete========” | Tee-Object $LogFile -Append