Scripting Office 365 licensing with disabled services

In the past I’ve had a few clients request scripts to automatically set/assign licenses to users in Office 365 – Generally pretty simple stuff. 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 correctly (and not make any changes if it is). Took a little while to work out, so figured I’d share the love!

Just to set a license for a user is a pretty simple process – all you need is the license ‘SkuId’ value of the relevant license. 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"

You’ll notice that the code above sets the location first – this is required, as you can’t apply a license without a location being set! 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.

Note: In order to run this script, you’ll need:

#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

 

Leave a Reply