Cmdlet/Update-MSOLUserLicensePlan.ps1

Function Update-MSOLUserLicensePlan {
    <#
  
    .SYNOPSIS
    UPDATES the current plan settings for an assigned SKU while maintaining existing plan settings.
 
    .DESCRIPTION
    Updates the current plan settings for an assigned SKU while maintaining existing plan settings.
 
    * UPDATES existing plan settings with the new enabled or disabled plans
    * All current plan setting are left in place.
    * To change plans without maininting the current state use Set-MSOLUserLicensePlan
    * Can specify plans to enable or disable
    * Logs all activity to a log file
     
    .PARAMETER Users
    Single UserPrincipalName, Comma seperated List, or Array of objects with UserPrincipalName property.
 
    .PARAMETER SKU
    SKU that should be modified.
 
    .PARAMETER PlansToDisable
    Comma seperated list of SKU plans to Disable.
 
    .PARAMETER PlansToEnable
    Comma seperated list of SKU plans to Enable.
 
    .PARAMETER LogFile
    File to log all actions taken by the function.
 
    .OUTPUTS
    Log file showing all actions taken by the function.
 
    .EXAMPLE
    Update-MSOLUserLicensePlan -Users $Promoted -Logfile C:\temp\add_license.log -SKU company:ENTERPRISEPACK -PlanstoEnable SWAY,TEAMS1,EXCHANGE_S_ENTERPRISE,YAMMER_ENTERPRISE,OFFICESUBSCRIPTION
 
    Adds the enabled Plans SWAY,TEAMS1,EXCHANGE_S_ENTERPRISE,YAMMER_ENTERPRISE,OFFICESUBSCRIPTION to the ENTERPRISEPACK for all users in $Promoted. Maintaining any existing plan settings.
     
    #>

    
    Param
    (
        [Parameter(Mandatory = $true)]
        [array]$Users,
        [Parameter(Mandatory = $true)]
        [string]$LogFile,
        [string]$SKU,
        [string[]]$PlansToDisable,
        [string[]]$PlansToEnable
    )
    
    # Takes in a list of plans and creates a new list based currently disabled plans + list
    Function Update-DisabledPlan {

        param 
        (
            [string]$SKU,
            [string[]]$PlansToDisable,
            [string]$MSOLUser
        )

        Write-Log "Determining Plans to Disable"

        # Null out our array
        [array]$CurrentDisabledPlans = $null
    
        # Get the License object
        $license = $MSOLUser.licenses | Where-Object { $_.accountskuid -eq $SKU }
    
        # Make sure we found the license on the user
        if ($null -eq $license) {
            Write-Log ("[ERROR] - Cannot find SKU " + $SKU + " assigned to " + $MSOLUser.UserPrincipalName)
            Write-Error -Message ("Cannot find SKU " + $SKU + " assigned to " + $MSOLUser.UserPrincipalName)
            
            $Output = "Error"
            Return $Output
        }
        else {

            # Get currently disabled plans
            [array]$CurrentDisabledPlans = ($license.servicestatus | Where-Object { $_.provisioningstatus -eq "disabled" })

            # Make sure we got back some disabled plans
            if ($CurrentDisabledPlans.Count -le 0) {
        
                Write-Log "No Currently Disabled Plans."

                # Just return the value of -planstodisable that was submitted
                [string[]]$Output = $PlansToDisable.ToUpper()
        
                Return $Output

            }
            # There are currently disabled plans we need to combine the current with the new
            else {                

                # Show the currently Disabled Plans and then determine the new list
                foreach ($plan in $CurrentDisabledPlans) {
                    [string[]]$CurrentDisabledPlansNames = $CurrentDisabledPlansNames + ($plan.serviceplan.servicename).toupper()
                }

                Write-Log ("Currently Disabled plans:" + $CurrentDisabledPlansNames)
    
                # Combine the two lists of disabled plans
                [string[]]$Output = $CurrentDisabledPlansNames + $PlansToDisable

                # Make all of them uppercase for comparison
                $Output = $Output.toupper()

                # Throw out all of the duplicates
                $Output = ($Output | Select-Object -Unique)

                Return $Output
            }
        }
    }

    # Takes in a list of plans and creates a new list based on currently enabled plans + list
    Function Update-EnabledPlan {
        param 
        (
            [string]$SKU,
            [string[]]$PlansToEnable,
            [string]$MSOLUser
        )

        Write-Log "Determining Plans to Enable"

        # Null out our values
        [string[]]$CurrentDisabledPlansNames = $null
        [array]$CurrentDisabledPlans = $null
    
        # Get the License object
        $license = $MSOLUser.licenses | Where-Object { $_.accountskuid -eq $SKU }
    
        # Make sure we found the license on the user
        if ($null -eq $license) {
            Write-Log ("[ERROR] - Cannot find SKU " + $SKU + " assigned to " + $MSOLUser.UserPrincipalName)
            Write-Error -Message ("Cannot find SKU " + $SKU + " assigned to " + $MSOLUser.UserPrincipalName)

            $Output = "Error"
            Return $Output
        }
        # If we do process it
        else {
            
            # Get currently disabled plans
            [array]$CurrentDisabledPlans = ($license.servicestatus | Where-Object { $_.provisioningstatus -eq "disabled" })
    
            # If there are no currently disabled plans then return null since there are no plans to update
            if ($null -eq $CurrentDisabledPlans) {
                Write-Log "No Currently Disabled Plans; No Plan updates needed."
                $Output = "Enabled"
                Return $Output
            }
            # Otherwise we need to show what is currently disabled and then calculate the new list to disable
            else {
                # Pull out the plan names
                foreach ($plan in $CurrentDisabledPlans) {
                    [string[]]$CurrentDisabledPlansNames = $CurrentDisabledPlansNames + ($plan.serviceplan.servicename).toupper()
                }

                Write-Log ("Currently Disabled plans:" + $CurrentDisabledPlansNames) 
    
                # Go thru each plan that needs to be enabled and remove it from the list of currently disabled plans
                foreach ($Plan in $PlansToEnable) {        
                    # Remove the plans we want to enable from the list of disabled plans
                    $CurrentDisabledPlansNames = ($CurrentDisabledPlansNames | Where-Object { $_ -ne $Plan })            
                }    

                # Make sure we havn't pulled out all disabled plans and if we have just return a null
                if ($null -eq $CurrentDisabledPlansNames) {
                    $Output = "Enabled"
                    Return $Output
                }
                # As long as we have at least one return that one
                else {
                    Return $CurrentDisabledPlansNames
                }
            }
        }
    }

    ### MAIN ###

    # Make sure we didn't get both enable and disable
    if (!([string]::IsNullOrEmpty($PlansToDisable)) -and !([string]::IsNullOrEmpty($PlansToEnable))) {
        Write-Log "[ERROR] - Cannot use both -PlansToDisable and -PlansToEnable at the same time"
        Write-Error "Cannot use both -PlansToDisable and -PlansToEnable at the same time" -ErrorAction Stop
    }

    # Make sure we have a valid log file path
    Test-LogPath -LogFile $LogFile

    # Make sure we have the connection to MSOL
    Test-MSOLServiceConnection

    # Make user our Users object is valid
    [array]$Users = Test-UserObject -ToTest $Users

    # If no value of SKU passed in then call Select-Sku to allow one to be picked
    if ([string]::IsNullOrEmpty($SKU)) {
        $SKU = Select-SKU -Message "Select SKU to be updated:"
    }
    # If a value has been passed in verify it
    else {
        $SKU = Select-SKU -SKUToCheck $SKU
    }

    # Testing the plan inputs to make sure they are valid
    if (!([string]::IsNullOrEmpty($PlansToDisable))) {
        Test-Plan -Sku $SKU -Plan $PlansToDisable
    }
    # If plans to enable has a value then we test them
    elseif (!([string]::IsNullOrEmpty($PlansToEnable))) {
        Test-Plan -Sku $SKU -Plan $PlansToEnable
    }
    # If neither has been provided then we don't need to do anything
    else { }

    # "Zero" out the user counter
    [int]$i = 1
    [int]$ErrorCount = 0
    
    # Update the Plans for the Users Passed in
    Foreach ($Account in $Users) {

        # For if we determine we need to skip processing the user
        $Skip = $false

        Write-Log ("==== Processing User " + $account.UserPrincipalName + " ====")

        # Get our user object
        $MSOLUser = Get-MsolUser -UserPrincipalName $account.UserPrincipalName -ErrorAction SilentlyContinue

        # Make sure we have a value for $MSOLUSer
        if ($null -eq $MSOLUser) {
            Write-Log ("[ERROR] - Unable to Find User" + $Account.UserPrincipalName)
            Write-Error ("Unable to Find User" + $Account.UserPrincipalName) -ErrorAction Continue
            [string[]]$CalculatedPlansToDisable = "Error"
        }
        elseif (!($MSOLUser.Licenses.AccountSkuId -contains $SKU)) {
            Write-log "[ERROR] - Unable to verify the SKU to update is assigned"
            Write-Log ("Assigned SKUs: " + $MSOLUser.Licenses.AccountSkuId)
            Write-Error "Unable to verify SKU is Assigned to User" -ErrorAction Continue
            [string[]]$CalculatedPlansToDisable = "Error"    
        }
        # If -planstodisable is set then read it in and set license options
        elseif (!([string]::IsNullOrEmpty($PlansToDisable))) {        
            # Get the license options
            [string[]]$CalculatedPlansToDisable = Update-DisabledPlan -SKU $SKU -Plan $PlansToDisable -MSOLUser $MSOLUser
        }
        # If -planstoenable has a value then we need to determine what plans to enable and set them
        elseif (!([string]::IsNullOrEmpty($PlansToEnable))) {
            # Get the disabled plans and License options
            [string[]]$CalculatedPlansToDisable = Update-EnabledPlan -SKU $SKU -Plan $PlansToEnable -MSOLUser $MSOLUser
        }
        # If Neither disable or enable were passed then we turn on all plans
        else {
            Write-Log ("Turning on all Plans for " + $Sku + " on user " + $Account.UserPrincipalName)
            [string[]]$CalculatedPlansToDisable = "Enabled"
        }

        # Set the License Options based ont he value of $CalculatedPlansToDisable

        switch ($CalculatedPlansToDisable[0]) {
            Error {
                Write-Log ("[ERROR] - Skipping User")
                $Skip = $true
                $ErrorCount++
            }
            Enabled {
                Write-Log ("Setting License Options to all Enabled")
                $LicenseOption = Set-LicenseOption -SKU $SKU
            }
            Default {
                Write-Log ("Setting License options for Plans")
                $LicenseOption = Set-LicenseOption -DisabledPlansArray $CalculatedPlansToDisable -SKU $SKU
            }
        }
        
        # If we determined no actions are needed then skip the user
        if ($Skip) { Write-Log "Skipping user" }
        # Process the user
        else {

            # Build and run our license set command
            [string]$Command = ("Set-MsolUserLicense -UserPrincipalName `"" + $Account.UserPrincipalName + "`" -LicenseOptions `$LicenseOption -ErrorAction Stop -ErrorVariable CatchError")
            Write-Log ("Running: " + $Command)
            # Try our command
            try { 
                Invoke-Expression $Command 
            }
            # If we have any error write out and stop
            catch { 
                Write-Log ("[ERROR] - " + $CatchError.ErrorRecord)
                Write-Error ("Failed to successfully add license to user " + $account.UserPrincipalName)
                $ErrorCount++
            }
        }    

        # Update the progress bar and increment our counter
        Update-Progress -CurrentCount $i -MaxCount $Users.Count -Message "Updating License Plan"

        # Update our user counter
        $i++
    }

    Write-Log ("Finished Adding SKU " + $SKU + " to " + $Users.Count + " Users.")
    If ($ErrorCount -gt 0) {
        Write-Log ($ErrorCount.ToString() + " ERRORS DURING PROCESSING PLEASE REVIEW ENTRIES WITH '[ERROR]' FOR MORE INFORMATION")
    }
}