Public/Disable-MDSMsolLicenseServicePlan.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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