Office 365 Granular License Assignment via Powershell

I’ve spent the last day or so working on how to reliably assign licenses to Office 365 for  users with Powershell.  My particular test environment is an Office 365 for Education space but the procedure here should work for any Office 365 setup.

There are a few blog posts, magazine articles and forum threads about this process but they only provided pieces of the overall puzzle when I was working this out so this is my attempt to put it all together.

Office 365 Account SKUs and SKU ID’s:

With your Office 365 subscription, you are assigned one or more account SKU’s.  These appear to be a combination of your tenant ID and a plan identifier, where the plan identifier represents a bundle of services (which we’ll get to in a bit).  To see your assigned sku’s, run the cmdlet get-msolaccountsku, which lists the ones that are available to your tenant and also some statistics about usage.

Below is sample output when I run this cmdlet against an Office 365 tenant which has educational plan A2 assigned:

PS v3.0 C:\ > Get-MsolAccountSku

AccountSkuId                       ActiveUnits WarningUnits ConsumedUnits
------------                       ----------- ------------ -------------
myschool:STANDARDWOFFPACK_STUDENT  1000        0            1
myschool:STANDARDWOFFPACK_FACULTY  100         0            0
myschool:EXCHANGESTANDARD_ALUMNI   1000        0            0
myschool:EXCHANGESTANDARD_STUDENT  1000        0            208

My tenant has four account SKU ID’s present which can be assigned to users.  Each Sku ID represents a “bundle” of one or more licensed services such as Sharepoint, Exchange, Lync, the office web apps, etc.

To see the component services of these bundles, you need to crack open its SKU ID and look at the “service plans” that it contains.  It’s easiest to do this via a two-step process.

First, you use get-msolaccountsku to get the SkuPartNumber for each of the Sku ID’s.  In all my testing, the SkuPartNumber value has been the same as the AccountSkuId minus the domain identifier prefix on it but we should assume that the two can diverge in the future.

PS v3.0 C:\ > Get-MsolAccountSku | ft accountskuid,skupartnumber

AccountSkuId                             SkuPartNumber
------------                             -------------
myschool:STANDARDWOFFPACK_STUDENT        STANDARDWOFFPACK_STUDENT
myschool:STANDARDWOFFPACK_FACULTY        STANDARDWOFFPACK_FACULTY
myschool:EXCHANGESTANDARD_ALUMNI         EXCHANGESTANDARD_ALUMNI
myschool:EXCHANGESTANDARD_STUDENT        EXCHANGESTANDARD_STUDENT

Next, let’s open up STANDARDWOFFPACK_STUDENT to see what its component services are.

Note that we’re using the SkuPartNumber here and not the AccountSkuId, which looks similar.

PS v3.0 C:\ > $plans = Get-MsolAccountSku | Where {$_.SkuPartNumber -eq "STANDARDWOFFPACK_STUDENT"}
PS v3.0 C:\ > $plans.servicestatus

ServicePlan            ProvisioningStatus
-----------            ------------------
SHAREPOINTWAC_EDU      Success
MCOSTANDARD            Success
SHAREPOINTSTANDARD_EDU Success
EXCHANGE_S_STANDARD    Success

And there we see that STANDARDWOFFPACK_STUDENT has four component services:

  • SHAREPOINTWAC_EDU: (This is for the 365 web apps, not Sharepoint)
  • MCOSTANDARD (Office 365 Lync)
  • SHAREPOINTSTANDARD_EDU (Office 365 Sharepoint)
  • EXCHANGE_S_STANDARD (Exchange)

Now, how do we use this information to apply licenses to Office 365 users with Powershell?

The primary way that you apply user licenses for O365 with Powershell is with the set-msoluserlicense cmdlet.  You can either use it on its own and specify the UPN of the targeted user on the command line or pipe a user object to it with get-msoluser.

set-msoluserlicense has three parameters that I’m focusing on for the purposes of this post:

  • -AddLicenses <string[]>
  • -LicenseOptions <LicenseOption[]>
  • -Removelicenses <string[]>

The first and last options relate to applying or removing all functions within a license “bundle” to a user and the middle one allows you to manage which services are enabled for a particular user.  In my testing, it appears that you must have an overall license “bundle” in place for a user (enabling all functions) before you can handle enabling/disabling the individual components.

The -AddLicenses and -RemoveLicenses parameters require a string value that must match one of the available AccountSkuId’s on your 365 tenant.

You can also specify licenses with the new-msoluser cmdlet via the -LicenseAssignment and -LicenseOptions parameters but I’m not going to discuss them here because they work in a similar way.

Here’s a one-liner to enable a single user for all available services within an AccountSkuId.  You do this by applying the AccountSkluID for the “bundle” that you want the user to get.

get-msoluser -UserPrincipalName first.last@myschool.edu | set-msoluserlicense -AddLicenses "myschool:STANDARDWOFFPACK_STUDENT"

And here’s how to verify that it worked by checking the ProvisioningStatus of the user’s licenses. Success == good, of course.

PS v3.0 C:\ > (get-msoluser -UserPrincipalName first.last@myschool.edu).licenses.servicestatus

ServicePlan              ProvisioningStatus
-----------              ------------------
SHAREPOINTWAC_EDU        Success
MCOSTANDARD              Success
SHAREPOINTSTANDARD_EDU   Success
EXCHANGE_S_STANDARD      Success

Disabling Specific Office 365 Functions for a User

Let’s say that my hypothetical customer isn’t ready to support Lync, so we want to shut that functionality off for our test user.  To do this, we need to create a LicenseOptions object which references the AccountSkluId for the “bundle” that the user is using for and also lists the Lync component on the DisabledPlans parameter.

First, we create a LicenseOptions object which has Lync disabled.  I’ll call it $nolync here.

PS v3.0 C:\ > $nolync = New-MsolLicenseOptions -AccountSkuId myschool:STANDARDWOFFPACK_STUDENT -DisabledPlans MCOSTANDARD

PS v3.0 C:\ > $nolync

ExtensionData     AccountSkuId                            DisabledServicePlans
-------------     ------------                            --------------------
                  Microsoft.Online.Administration.Acco... {MCOSTANDARD}

Next, we use set-msoluserlicense to apply the license options object that we just created to the user.  Everything that’s not listed in the DisabledServicePlans property will be ENABLED after applying this to the user, so “stacking” LicenseOptions calls against a user will result in a last-writer-wins scenario.

PS v3.0 C:\ > get-msoluser -UserPrincipalName first.last@myschool.edu | Set-MsolUserLicense -LicenseOptions $nolync 

PS v3.0 C:\ >

Now, we can check the servicestatus property of the user’s licenses to verify that Lync has been shut off:

PS v3.0 C:\ > (get-msoluser -UserPrincipalName first.last@myschool.edu).licenses.servicestatus 

ServicePlan            ProvisioningStatus 
-----------            ------------------ 
SHAREPOINTWAC_EDU      Success 
MCOSTANDARD            Disabled 
SHAREPOINTSTANDARD_EDU Success 
EXCHANGE_S_STANDARD    Success

IMPORTANT NOTE about SharePoint licensing with office 365.  I had a lot of trouble attempting to disable just SharePoint for users in my tenant – it failed every time and I couldn’t figure out why.  After significant head-scratching and web searches, I eventually found this forum thread, where a Microsoft support person mentions as an aside that Office Web Apps and SharePoint are co-dependent so you can’t disable SharePoint in Office 365 without also disabling the Office web apps.  While this solved my problem, the enforced linkage of the two is unfortunate as customers might want their users to have access to the office web apps (which IMO are very nice) but not SharePoint because of the support complexity that SharePoint brings.  I suspect this is because the Office web apps use Sharepoint for storage so this may be resolved when/if SkyDrive Pro arrives on the scene for 365. [ UPDATE 12/21/2012 :  This has been confirmed.  The Office web apps in Office 365 are configured to use a document library on a Sharepoint site as their storage instead of Skydrive like the free versions are. ]

For now, however, if you want to disable SharePoint for your users, make sure you also disable the Office Web Apps or you’ll get an error when you try to apply the change.

This is also a convenient example of how to disable two Office 365 functions together – just provide their identifiers as a comma-separated string to the new-msollicenseoptions cmdlet.

PS v3.0 C:\ > $nosharepoint = New-MsolLicenseOptions -AccountSkuId myschool:STANDARDWOFFPACK_STUDENT -DisabledPlans SHAREPOINTWAC_EDU,SHAREPOINTSTANDARD_EDU

PS v3.0 C:\ > $nosharepoint

ExtensionData AccountSkuId                            DisabledServicePlans
------------- ------------                            --------------------
              Microsoft.Online.Administration.Acco... {SHAREPOINTWAC_EDU, SHAREPOINTSTANDA...

PS v3.0 C:\ > get-msoluser -UserPrincipalName first.last@myschool.edu | Set-MsolUserLicense -LicenseOptions $nosharepoint

PS v3.0 C:\ > (get-msoluser -UserPrincipalName first.last@myschool.edu).licenses.servicestatus

ServicePlan             ProvisioningStatus
-----------             ------------------
SHAREPOINTWAC_EDU       Disabled
MCOSTANDARD             Success
SHAREPOINTSTANDARD_EDU  Disabled
EXCHANGE_S_STANDARD     Success

If you want to strip all licenses from a user, use the -removelicenses parameter with the AccountSkuId string for the license bundle and they’re toast.  Note that this can deprovision mailboxes, etc so use this with care!

Lastly, a few miscellaneous observations and comments about the license-handling cmdlets and objects themselves that didn’t fit in elsewhere:

  • The overall license “bundle” is passed as a STRING to the -addlicenses and -removelicenses parameters to the set-msoluserlicense cmdlet.  However, the -licenseoptions parameter to the same cmdlet requires that you pass it an OBJECT of the type LicenseOption, which is helpfully returned by the new-licenseoption cmdlet.  It would make a lot more sense if all three parameters agreed on what sort of parameter they required.
  • If you want to give a new user a granular set of permissions, it appears that you need to first assign the overall license Sku to a user (which activates all functions) and then stamp it with a licenseoptions object which has the components that you don’t want the user to see listed as disabled.  You can’t just stamp an unlicensed user with a LicenseOptions object or the assignment will fail.
  • Additionally, according to a reply to this posted request for help, the -addlicenses and -licenseoptions parameters don’t like to be used together.  This appears to be my experience as well.
  • The -UnlicensedUsersOnly parameter to get-msoluser will (obviously) return only unlicensed users – this is handy.
  • Error reporting from assignment of licenses in 365 via Powershell is TERRIBLE.  The most common cause of problems is making a typo in the new-msollicenseoptions cmdlet since it doesn’t seem to perform any input validation.
Advertisements

Digital Glue?

I named this blog “Digital Glue” because that describes the aspect of technology that I enjoy the most quite well – making groups of systems work together in interesting ways.  My current employer, Oxford Computer Group NA, specializes in designing and implementing complex identity management solutions using Microsoft’s Forefront Identity Manager (FIM) and related technologies.  At the moment, most of my daily work is centered on migration of educational institutions from Microsoft’s Live@EDU platform to the newer Office 365 platform.

Advisory to readers – I tend to to back and make changes to posts rather than just stick them up and leave them alone.  If the changes relate to content rather than just tweaking the phrasing of a particular item, I’ll make a note that the post has been udpated and when.

Back on the intertubes!

I’m glad to have a WordPress site back online to share some of my discoveries.  I previously worked for a major university and maintained a professional blog there where I talked about the work that I and my group did (and occasionally threw in some other stuff, like my wife’s killer recipe for rum balls).

However, with my departure now getting smaller in the rear-view mirror, that blog is sure to be de-rezzed at some point so I’m going to use this one as a similar place to post things that I find interesting.  I’m also going to try to transfer some of the more interesting things from that blog to this one because I feel they are of interest to the wider world.