Office 365: Assign individual parts of licenses based on groups using PowerShell

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! 

The story continues… After numerous requests regarding handling parts of licenses in my older licensing scripts , I’ve now created an example on how to do this using security groups.

Please note that this is of course not a complete solution, just an example on how to incorporate this in to one of the earlier solutions created.

SCENARIO
In this example, I will have three groups each assigning different parts of an E3 License. One assiging the Full E3, one assigning Exchange Online and one assigning Lync Online + Office 365 ProPlus.
In order to assign them with PowerShell, we need the ServicePlan name of each part we want to assign. Those can be found with the following command for an E3-license:

(Get-MsolAccountSku | Where-Object {$_.SkuPartNumber -eq "ENTERPRISEPACK"}).ServiceStatus

2014-12-16_20-32-02
As seen above the names are pretty easy to recognise, if you are unsure which one is which, you will find a good translation table here.

Disabling plans with PowerShell requires you to to select the ones that you want to disable rather than the ones you want to enable, just like in the portal. In my example I’m choosing the parts I want to assign per group, which means I’m disabling all other parts than just the ones you want.
See the example below how I’ve configured my $Licenses hashtable for my scenario:

$Licenses = @{
                 'E3-ExchangeOnline' = @{
                          LicenseSKU = 'tenant:ENTERPRISEPACK'
                          EnabledPlans = 'EXCHANGE_S_ENTERPRISE'
                          Group = 'E3-ExchangeOnline-Users'
                        }
                 'E3-LyncO365ProPlus' = @{
                          LicenseSKU = 'tenant:ENTERPRISEPACK'
                          EnabledPlans = 'MCOSTANDARD','OFFICESUBSCRIPTION'
                          Group = 'E3-LyncO365ProPlus-Users'
                        }
                 'E3' = @{
                          LicenseSKU = 'tenant:ENTERPRISEPACK'
                          Group = 'E3-Users'
                        }
            }

RUNNING THE SCRIPT
After editing the $Licenses hashtable, $UsageLocation and tenant credentials, you’re ready to run the script as in the screenshot below.
2014-12-16_21-52-24

2014-12-16_21-40-47
A user that have been assigned licenses with the E3-LyncO365ProPlus-Users group in the example

LicenseO365Users.ps1

<# .SYNOPSIS     Script that assigns Office 365 licenses based on Group membership in AAD. .NOTES     Author: Johan Dahlbom      Blog: 365lab.net      Email: johan[at]dahlbom.eu      The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.     Requires PowerShell Version 3.0! #>

#Import Required PowerShell Modules
Import-Module MSOnline
#Office 365 Admin Credentials
$CloudUsername = 'admin@tenant.onmicrosoft.com'
$CloudPassword = ConvertTo-SecureString 'password' -AsPlainText -Force
$CloudCred = New-Object System.Management.Automation.PSCredential $CloudUsername, $CloudPassword
#Connect to Office 365
Connect-MsolService -Credential $CloudCred
$UsageLocation = 'SE'

$Licenses = @{
                 'E3-ExchangeOnline' = @{
                          LicenseSKU = 'tenant:ENTERPRISEPACK'
                          EnabledPlans = 'EXCHANGE_S_ENTERPRISE'
                          Group = 'E3-ExchangeOnline-Users'
                        }
                 'E3-LyncO365ProPlus' = @{
                          LicenseSKU = 'tenant:ENTERPRISEPACK'
                          EnabledPlans = 'MCOSTANDARD','OFFICESUBSCRIPTION'
                          Group = 'E3-LyncO365ProPlus-Users'
                        }
                 'E3' = @{
                          LicenseSKU = 'tenant:ENTERPRISEPACK'
                          Group = 'E3-Users'
                        }
            }

foreach ($license in $Licenses.Keys) {
    $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"

    #region Disable non specific plans
    $EnabledPlans = $Licenses[$license].EnabledPlans
    if ($EnabledPlans) {
        $LicenseOptionHt = @{
            AccountSkuId = $AccountSKU.AccountSkuId
            DisabledPlans =  (Compare-Object -ReferenceObject $AccountSKU.ServiceStatus.ServicePlan.ServiceName -DifferenceObject $EnabledPlans).InputObject
        }
        $LicenseOptions = New-MsolLicenseOptions @LicenseOptionHt
    }
    #endregion Disable non specific plans

    #Get all unlicensed group members - needs to be changed if a user should be able to have more than one license
    $GroupMembers = (Get-MsolGroupMember -GroupObjectId $GroupID -All | Where-Object {$_.IsLicensed -eq $false}).EmailAddress
    #Warn if not enough licenses are available
    if ($AccountSKU.ActiveUnits - $AccountSKU.consumedunits -lt $GroupMembers.Count) {
        Write-Warning 'Not enough licenses for all users, please remove user licenses or buy more licenses'
    }

    foreach ($User in $GroupMembers) {
        try {
            #Set UsageLocation
            Set-MsolUser -UserPrincipalName $User -UsageLocation $UsageLocation -ErrorAction Stop -WarningAction Stop
            $LicenseConfig = @{
                UserPrincipalName = $User
                AddLicenses = $AccountSKU.AccountSkuId
            }
            if ($EnabledPlans) {
                $LicenseConfig['LicenseOptions'] = $LicenseOptions
            }
            Set-MsolUserLicense @LicenseConfig -ErrorAction Stop -WarningAction Stop
            Write-Output "SUCCESS: licensed $User with $license"
        } catch {
            Write-Warning "Error when licensing $User`r`n$_"
        }
    }
 }

Let me know if you have any questions or feedback!

/Johan

Advertisement

44 thoughts on “Office 365: Assign individual parts of licenses based on groups using PowerShell

  1. Bill Y

    Amazing work, thank you! How would I combine the “Assign individual parts” script with the advanced script that removes licenses?

    Reply
    1. Johan Dahlbom Post author

      Hi Jim,

      Sorry for the late reply. It is of course possible, but would require you to export a list of all users including individual licenses and compare them with the Groups etc. So a bit complicated, and therefore I am not sure if I will publish a full version with the 2 combined.

      Let me know if you have any other questions!
      /Johan

      Reply
  2. Jim Mikal

    Excellent work! The script runs perfectly.

    My question is how do I add a plan after the fact? Since the script looks for unlicensed users and they are already licensed it won’t attempt to add plans?

    Reply
  3. Pingback: Office 365: Assign licenses based on groups using PowerShell | Tailspintoys – 365lab.net

  4. Richard

    Hi Johan,
    first of all thank you very much for your blog.
    There are many amazing information.

    I have spent a lot time to try modify your code but I’m not good coder.

    I want create modifications
    – enable some options of E3 according to group membership
    – enable multiple SKU
    example: user is member of groups “O365-E3-OfficeProPlus”, “O365-E3-Lync”, “O365-E3-Sharepoint” and “O365-Visio ”
    and he will get only some options of E3SKU + VisioSKU

    Could you try modify your script or advise me how to modify it?

    Reply
  5. robdevereaux

    Hi Johan,

    Thanks for putting together these scripts. I have been testing this script and it works well, but as the others mentioned, it only applies to unlicensed users. If I move a user from one group to another, it doesn’t updated the assignment.

    If there is a way to combine the Enabled Plans functionality you have in this script with the change/update functionality in your Advanced version, I think that would solve some riddles.

    Thanks!
    -Rob

    Reply
      1. Carl

        HI Johan, do you have any thoughts\update examples from the above requests regarding either user is a member of multiple security groups for the different services or moving a user between security groups that have different services licensed? (examples listed by Richard and Rob above).

        Thanks
        -Carl

      2. Carlos Jenkins

        Hi Johan, Great job with those scripts, THANKS!!!
        As people has commented, they work great when users are unlicensed, but not if the users already have a license.

        I seen that you were working on that and I was wondering if you have any update. That will be awesome.

        Thanks

      3. Johan Dahlbom Post author

        Hi Carlos,

        Thanks for reading. Unfortunately I have been busy with lots of projects and have not had the time to publish this. I guess you have looked in to the script that adds/removes licenses that actually does this (but not with individual serviceparts).

        However – I will do my best to give you guys an example on how to make this possible with individual serviceparts as soon as possible.

        /Johan

      4. Carlos Jenkins

        Thanks for replying 🙂
        I was looking at this article :Office 365: Assign individual parts of licenses based on groups using PowerShell: have three groups each assigning different parts of an E3 License. One assiging the Full E3, one assigning Exchange Online and one assigning Lync Online + Office 365 ProPlus.
        https://365lab.net/2014/12/17/office-365-assign-individual-parts-of-licenses-based-on-groups-using-powershell/#comment-5256

        Basically I just need to update (to add) part of licenses to a group in which users have already something assigned.
        If you can point me to some direction, that will be great.

        Thanks for everything.

      5. Johan Dahlbom Post author

        Hi,

        If you check row 58, and remove “| Where-Object {$_.IsLicensed -eq $false}”, you will go through all users in the group, even if they already have licenses. That should work in your case. What would improve it even further is to put in an if statement in the foreach loop to check if the specific license/servicepart is present on the user. I will look in to a way to do it more efficient, but the above should solve your problem 🙂

      6. Carlos Jenkins

        I’ve tried that, but I get this error: “WARNING: Error when licensing User4@domain
        Unable to assign this license because it is invalid. Use the Get-MsolAccountSku cmdlet to retrieve a list of valid licenses.”

        If I remove the license for the user, the script works

      7. Carlos J

        I’m ready,. bring it on!..
        I thought I answered yesterday but it seems it didn’t go through.

        Thanks for all your help. So,, What do I do?

  6. Gary Adkins

    I would be interesting in obtaining your latest version of this script that Carlos mentioned about adding part of licenses to a group in which users have already something assigned.

    Gary

    Reply
      1. Carlos Jenkins

        Hi Johan, do we have any update :$. I know you’re busy and I don’t want to bother you too much.

      1. Gary Adkins

        Works as intended! Great job man!

        One question that’s probably independent of the scope of this script, down in the script you have a section stating:

        #Get all unlicensed group members – needs to be changed if a user should be able to have more than one license

        What would need to be changed if I wanted to apply a second license that didn’t need any modifications?

        Cheers

      2. Michel

        Johan,
        I have ran your beta version of the script, and works as I wanted it. Able to apply 2 licenses (tenant:STANDARDWOFFPACK_STUDENT and tenant:OFFICESUBSCRIPTION_STUDENT) to users.
        Thank you
        Great work!

      3. Kai

        Hi Johan,
        i works awesome.
        Any Idea with the Script for deactivate the “Enabled Plan” when i removed the User from the AD Group ?

        We will control all Modules over the AD Groups.

        In the another Script of this site, we can Only Control the complete Licence Plan

      4. Johan van Viegen

        Excellent script.
        I did however find an issue when the tenant had multiple SKU:s with in my case the MCOSTANDARD license available. Even if i had given the user a MCOSTANDARD licensen from the SKU i wanted, then the script failed to see it and tried to reapply a MCOSTANDARD license again.

        I think i have solved it by replacing this line which found MCOSTANDARD from two SKU:s:

        with this line which filters for the specified SKU:

        On the other hand, this now returns an “error” if the MCOSTANDARD license has been given to the user from another SKU…

  7. Tom

    Gary, Thanks for the script. I have one question, my script was running fine for months but now I am getting this error “At C:\Users\Administrator\Desktop\scripts\o365userlicense.ps1:51 char:61
    + DisabledPlans = (Compare-Object -ReferenceObject <<<< $AccountSK
    U.ServiceStatus.ServicePlan.ServiceName -DifferenceObject $EnabledPlans).InputO
    bject
    + CategoryInfo : InvalidData: (:) [Compare-Object], ParameterBind
    ingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,M
    icrosoft.PowerShell.Commands.CompareObjectCommand

    New-MsolLicenseOptions : A positional parameter cannot be found that accepts ar
    gument '$null'.

    My LicenseSKU is 'Tenant:STANDARDWOFFPACK_IW_FACULTy

    Any help would be appreciated.

    Reply
  8. Marc

    Hi Johan,

    What needs to be changed here?

    #Get all unlicensed group members – needs to be changed if a user should be able to have more than one license

    Reply
    1. Bryan

      Hi Johan,
      I Have the same question pretty much. For example, if the user has an EMS license assigned to him previously (a different AccountSkuID) the script doesn’t add the Office365 license to the user. It simply ignores adding the licens since it all ready has an EMS license. What modifications do we need to do to get the script to work in this scenario?

      Reply
    2. Marc

      Hey Johan,

      Just in case, I’m referring to your new script (which is working great) not your older one:
      http://pastebin.com/JHT170rm
      #Get all unlicensed group members – needs to be changed if a user should be able to have more than one license
      $GroupMembers = Get-MsolGroupMember -GroupObjectId $GroupID -All | Select-Object EmailAddress,@{Name=”Licenses”;Expression={(Get-MsolUser -UserPrincipalName $_.EmailAddress).Licenses }} | ForEach-Object -Process {

      Reply
  9. Antonio

    Hi Johan,

    Thank you for the scripts. They have been very helpful!

    One issue we’ve noticed intermittently is the “Get-MsolGroup -All | Where-Object {$_.DisplayName -eq $GroupName}” method call sometimes does not return your group. It just silently returns an empty object. We’ve seen this happen 3 times in the past year, which works out to 3 times in ~5000 “Get-MsolGroup -All | Where-Object {$_.DisplayName -eq $GroupName}” calls. Not sure if this is an issue with Azure or our connection to Azure… In any case, we have adjusted our script to handle empty results from the “Get-MsolGroup -All | Where-Object {$_.DisplayName -eq $GroupName}” call. We essentially stop the script if the group is ever not found.

    Reply
    1. Johan Dahlbom Post author

      Hi,

      I have later after this script was first developed changed the approach to “hard code” the guid of the group instead, to get rid of the issues with not finding the group. Look in to the Azure Automation post for that.

      /Johan

      Reply
  10. john sophy

    the first script works with nested group members. Why does the second script for just assigning certain services not work for nested. It seems that you took that part out. Do you have a version that assign individual parts of licenses to nested groups.

    Reply

Leave a Reply to Johan Dahlbom Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s