Public/Disable-MDSMsolLicenseServicePlan.ps1

function Disable-MDSMsolLicenseServicePlan {
<#
    .SYNOPSIS
    Disables service plans from MSOnline licenses applied to users.

    .DESCRIPTION
    Will disable one or more service plans from a user's MSOnline account or specific MSOnline license on an account.

    .EXAMPLE
    Disable-MDSMsolLicenseServicePlan -UserPrincipalName user@domain.com -ServicePlan Teams1

    Disable a single service plan for a single user

    .EXAMPLE
    Disable-MDSMsolLicenseServicePlan -UserPrincipalName user@domain.com -ServicePlan Teams1,YAMMER_ENTERPRISE

    Disable multiple service plans for a single user

    .EXAMPLE
    Disable-MDSMsolLicenseServicePlan -UserPrincipalName user@domain.com -ServicePlan Teams1 -AccountSkuID tenantname:ENTERPRISEPACK,tenantname:PROJECTESSENTIALS

    Disable a service plan across multiple AccountSkuIDs for a single user.

    .EXAMPLE
    Get-MsolUser -UserPrincipalName user@domain.com | Disable-MDSMsolLicenseServicePlan -ServicePlan Teams1

    Utilize Pipeline support with objects that have a UserPrincipalName property. Also accepts the Licenses property captured by Get-MsolUser.

    .NOTES
    Written by Rick A, April 2017

#>

    [CmdletBinding(SupportsShouldProcess=$True)]
    param(
        [Parameter(
            Mandatory=$True,
            ValueFromPipeline=$True,
            ValueFromPipelineByPropertyName=$True
        )]
        [string[]]$UserPrincipalName,

        [Parameter(
            Mandatory=$False,
            ValueFromPipelineByPropertyName=$True
        )]
        [object[]]$Licenses,

        [Parameter(Mandatory=$True)]
        [string[]]$ServicePlan,
        
        [Parameter(Mandatory=$False)]
        [string[]]$AccountSkuID
    )
    
    begin {}   
    process {
        ForEach ($UPN in $UserPrincipalName) {
            # Get the licenses and confirm the UserPrincipalName exists
            If (-not $Licenses) {
                Try {
                    Write-Verbose ("{0}: Licenses not provided. Querying MSOnline for licenses." -f $UPN )
                    [array]$Licenses = (Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop).Licenses
                }
                Catch {
                    $PSCmdlet.ThrowTerminatingError($PSItem)
                }
            }        

            # Confirm any license was found
            If ($Licenses.count -eq 0) {
                Write-Verbose ("{0}: User not licensed." -f $UPN )
                Continue
            }

            # If present target only the licenses specified
            If ($AccountSkuID) {
                [array]$Licenses = $Licenses | Where-Object {$AccountSkuID -contains $_.AccountSkuID}
                # Validate there are licenses to process
                If ($Null -eq $Licenses) {
                    Write-Verbose ("{0}: No licenses match the specified AccountSkuID(s)." -f $UPN )
                    Continue
                }
            }

            # Flatten the license details for the user only if the license contains a
            # provided service plan.
            [array]$LicenseCollection = ForEach ($License in $Licenses) {
                $ProcessLicense = $False
                ForEach ($Plan in $ServicePlan) {    
                    If ($License.ServiceStatus.ServicePlan.ServiceName -match $Plan) {
                        $ProcessLicense = $True
                    }
                }
                
                If ($ProcessLicense -eq $True) {
                    ForEach ($Status in $License.ServiceStatus) {
                        [pscustomobject] @{
                            ServiceName            = $Status.ServicePlan.ServiceName
                            ProvisioningStatus    = $Status.ProvisioningStatus
                            AccountSkuID        = $License.AccountSkuID
                        }
                    }
                }
            } # End license collection
            $Licenses = $Null

            # Report any service plans not assigned to the user.
            ForEach ($Plan in $ServicePlan) {
                $PlanProvisioningStatus = ($LicenseCollection | Where-Object {$_.ServiceName -eq $Plan}).ProvisioningStatus
                # Ensure the service plan was located in the license.
                If ($Null -eq $PlanProvisioningStatus) {
                    Write-Warning ("{0}: Service plan {1} not found." -f $UPN,$Plan)
                    Continue
                }
            }
            
            # Seperate the objects by AccountSkuID for license processing
            [array]$UpdateLicenses = ($LicenseCollection | Select-Object AccountSkuID -Unique).AccountSkuID
            # Go through the licenses to reapply the license with the updated disable options where necessary
            ForEach ($UpdateLicense in $UpdateLicenses) {
                Write-Verbose ("{0}: Processing license {1}." -f $UPN,$UpdateLicense)
                $CurrentLicense = $LicenseCollection | Where-Object {$_.AccountSkuId -match $UpdateLicense}

                $ConfirmedPlansToDisable = New-Object System.Collections.ArrayList
                ForEach ($Plan in $ServicePlan) {
                    # Get the current status of the specified service plan to be disabled
                    $PlanProvisioningStatus = ($CurrentLicense | Where-Object {$_.ServiceName -eq $Plan}).ProvisioningStatus
                    # Ensure the service plan was located in the license.
                    If ($Null -eq $PlanProvisioningStatus) {
                        # The user would have been notified previously of this scenario so we silently continue
                        Continue
                    }
                    
                    # Ensure the specified service plan is active
                    If ($PlanProvisioningStatus -ne "Disabled") {
                        [void]$ConfirmedPlansToDisable.Add($Plan)                
                    } # End If
                    Else {
                        Write-Warning ("{0}: The service plan {1} in license {2} is already disabled." -f $UPN,$Plan,$UpdateLicense)
                        Continue
                    } # End Else
                } # End ForEach
            
                # If the user didn't change...
                If (-not ($ConfirmedPlansToDisable)) {
                    Write-Warning ("{0}: No action was taken for license {1}." -f $UPN,$UpdateLicense)
                    Continue
                }

                # Get all currently disabled service plans including the service plan to be disabled
                $DisabledPlans = ($CurrentLicense | 
                    Where-Object {$_.ProvisioningStatus -eq "Disabled" -Or $ConfirmedPlansToDisable -contains $_.ServiceName}).ServiceName
                # Build the licensing options using the current AccountSkuID & collected service plans to disable
                $LicenseOptions = New-MsolLicenseOptions -AccountSkuId $UpdateLicense -DisabledPlans $DisabledPlans
                # Reapply the same license type leaving all previously disabled service plans disabled and adding
                # the specified service plan as a newly disabled service.
                If ($PSCmdlet.ShouldProcess($UPN,"Set-MsolUserLicense")) {
                    Try {
                        #Write-Warning "Set-MsolUserLicense is disabled. No action taken"
                        Set-MsolUserLicense -UserPrincipalName $UPN -LicenseOptions $LicenseOptions -ErrorAction Stop
                    }
                    Catch {
                        $PSCmdlet.ThrowTerminatingError($PSItem)
                    }
                }
            }
        }
    } # End Process
    end {}
} # End Function