Remove all licenses for a group of Office 365 users from CSV file

UPDATE: If you are reading this in 2023, be advised that Microsoft has since deprecated the cmdlets used in the sample below. Instead, use the Graph SDK for PowerShell: Remove all Office 365 licenses for a group of users from CSV file via Graph – Blog (michev.info)

A recent question over at the Spiceworks community asked for a PowerShell sample that will go over a list of Office 365 users imported via CSV file and remove all licenses for each user. Since this is a question I’ve seen asked previously, I decided to write a quick blog post about it and add some additional notes besides the actual code.

The first step is to make sure we have a proper input file. You can easily prepare such by using the Get-MsolUser cmdlet and filtering out users based on specific criteria, or you can just populate it manually via Excel. The important thing is that you have at least one column that designates users unambiguously. The UserPrincipalName or ObjectId properties should do. You can use other properties as necessary, but make sure to adjust the code below to account for that, as it expects to see a column named UserPrincipalName in the input CSV file.

Once we have the list of users, the task of removing licenses is a simple one. The only tricky part is that we actually need to have a list of licenses to remove, as there is no -RemoveAllLicenses switch or similar. Thus, for each user we will first run the Get-MsolUser cmdlet and gather the list of currently assigned SKUs and store them in the $SKUs variable. If said variable is empty, say because the user has no licenses assigned or no matching user was found, we skip to the next user. Then, for each individual license we can go ahead and run the Set-MsolUserLicense cmdlet.

One last remark is due here – licenses can also be assigned by using the group-based licensing feature. If that’s the case, using the Set-MsolUserLicense cmdlet will throw an error, so we can add a simple check in the script to avoid that. Instead, you should use the Azure AD blade in the Azure portal to adjust the group-based license.

Without further ado, here’s the code that does the trick. Make sure to update it to reflect the path to the CSV file and make sure that the CSV file has a column named UserPrincipalName (or adjust that in the code below):


$users = Import-Csv .\Users-to-disable.csv

foreach ($user in $users) {
Write-Verbose "Processing licenses for user $($user.UserPrincipalName)"
try { $user = Get-MsolUser -UserPrincipalName $user.UserPrincipalName -ErrorAction Stop }
catch { continue }

$SKUs = @($user.Licenses)
if (!$SKUs) { Write-Verbose "No Licenses found for user $($user.UserPrincipalName), skipping..." ; continue }

foreach ($SKU in $SKUs) {
if (($SKU.GroupsAssigningLicense.Guid -ieq $user.ObjectId.Guid) -or (!$SKU.GroupsAssigningLicense.Guid)) {
Write-Verbose "Removing license $($Sku.AccountSkuId) from user $($user.UserPrincipalName)"
Set-MsolUserLicense -UserPrincipalName $user.UserPrincipalName -RemoveLicenses $SKU.AccountSkuId
}
else {
Write-Verbose "License $($Sku.AccountSkuId) is assigned via Group, use the Azure AD blade to remove it!"
continue
}
}
}

Some final remarks. The script can produce some information output via the Write-Verbose statements, if you want to see this info make sure to set the value of the $VerbosePreference variable to “Continue”. If you want additional output, such as detailed logging or producing a list of users for which licenses could not be removed, feel free to adjust the code. And you might want to expand on the error handling of the script 🙂

UPDATE: the script had some logic issues which Tony Redmond was able to catch, so thank him for the updated version 🙂

16 thoughts on “Remove all licenses for a group of Office 365 users from CSV file

  1. Rick says:

    I am unable to remove the licenses. Is there something missing in the script? connect to account 365? Does anyone have an excel csv model?

    Reply
  2. Virgil says:

    I am trying to find one similar to this but have it also remove the disabled users from distribution lists as well. We have over 3,000 shared mailboxes and distribution lists so it would take forever for one person to go through each of those to find and remove a user.

    Reply
  3. Danny Roberson says:

    Would there be a way to make this attack on one type of directly assigned license?

    Reply
    1. Vasil Michev says:

      Sure, just put another check inside the

      foreach ($SKU in $SKUs)

      loop. For example:

      if ($SKU.AccountSkuId -ne “TENANT:LICENSE_YOU_WANT_REMOVED”) { continue }

      Reply
      1. Danny Roberson says:

        Awesome, so (bear with me I am a very novice powershell user so learning as I go) would need to replace the first if statement?:

        if (($SKU.GroupAssigningLicense.Guid -ieq $user.ObjectId.Guid) to:

        if (($SKU.AccountSKUId -ne “DOMAIN:STUDENTWOFFPACK_IW_STUDENT” -ieq $user.ObjectId.Guid)

        I used this to remove any license before and it worked beautifully, but now I fear I have to target just two license types.

        Reply
        1. Vasil Michev says:

          No need to replace anything, just add the additional line (insert it after the foreach statement, so line 12). That way the script will ignore any license apart from the one you specify in the if statement.

        2. Danny Roberson says:

          I got it to work! Woo hoo. It doesn’t seem to want to attack two licenses, just one at a time, but it does work only on the specified I ask for.

        3. B says:

          Hi Vasil,

          Just wanted to thank you for this, I was trying to figure out how to remove all licenses from a group of users and then add a specific licenses after all the licenses have been removed. I was able to built out and test a working script based on this blog.

          Best Regards!

          B

    1. Vasil Michev says:

      I’ll put together a version that uses Azure AD cmdlets when I get some free time…

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.