public/Test-UcmO365ServicePlan.ps1

#PerformScriptSigning
Function Test-UcmO365ServicePlan
{
    <#
            .SYNOPSIS
            Checks the status of a specified Service Plan in Office365
 
            .DESCRIPTION
            Cmdlet uses an existing Azure AD connection to filter through a users licences and check if the requested Service Plan is enabled.
            Handy for checking if an administrator has disabled Skype for Business Online before attempting to migrate them from Skype4B on prem, for example.
 
            .PARAMETER UPN
            The users username in UPN format
 
            .PARAMETER ServiceName
            Office365 Service Plan you wish to check
 
            .EXAMPLE
            PS> Test-UcmO365ServicePlan -UPN 'button.mash@contoso.com' -ServiceName 'MCOSTANDARD'
            Enables a user for Skype for Business Online (Required to migrate to Teams)
 
            .INPUTS
            User UPN - Users username in UPN format
            ServiceName - Office365 Service Plan you want to check
 
            .OUTPUT
            This Cmdet returns a PSCustomObject with multiple Keys to indicate status
            $Return.Status
            $Return.Message
 
            Return.Status can return one of four values
            "OK" : Service Plan is Enabled
            "Warning" : Service Plan is Disabled
            "Error" : Service Plan in an unknown state, like PendingProvisioning
            "Unknown" : Cmdlet reached the end of the fucntion without returning anything, this shouldnt happen, if it does please log an issue on Github
 
            Return.Message returns descriptive text based on the outcome, mainly for logging or reporting
 
            .NOTES
            Version: 1.1
            Date: 03/04/2021
 
            .VERSION HISTORY
            1.1: Updated to "Ucm" naming convention
            Better inline documentation
            Updated PowerShell Verbage from Find to Test
 
            1.0: Initial Public Release
 
            .REQUIRED FUNCTIONS/MODULES
            Modules
            AzureAD (Install-Module AzureAD)
            MSOnline (Install-Module MSOnline)
            UcmPSTools (Install-Module UcmPsTools) Includes Cmdlets below.
 
            Cmdlets
            Write-UcmLog: https://github.com/Atreidae/UcmPsTools/blob/main/public/Write-UcmLog.ps1
            Test-UcmMSOLConnection https://github.com/Atreidae/UcmPsTools/blob/main/public/Test-UcmMSOLConnection.ps1
            New-UcmMSOLConnection https://github.com/Atreidae/UcmPsTools/blob/main/public/New-UcmMSOLConnection.ps1
 
            .REQUIRED PERMISIONS
            'Office365 User Admin' or better
 
            .LINK
            http://www.UcMadScientist.com
            https://github.com/Atreidae/UcmPsTools
 
            .ACKNOWLEDGEMENTS
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')] #todo, https://github.com/Atreidae/UcmPSTools/issues/23

    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=1,HelpMessage='The UPN of the user you wish to check the Service Plan on, eg: button.mash@contoso.com')] [string]$UPN,
        [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=2,HelpMessage="The name of the Office365 Service Plan you wish check the status of, eg: 'MCOSTANDARD' for Skype Online")] [string]$ServiceName
    )

    #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
    $function = 'Test-UcmO365ServicePlan'
    [hashtable]$Return = @{}
    $return.Function = $function
    $return.Status = 'Unknown'
    $return.Message = 'Function did not return a status message'

    # Log why we were called
    Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
    Write-UcmLog -Message 'Parameters' -Severity 1 -Component $function -LogOnly
    Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
    Write-UcmLog -Message 'Parameters Values' -Severity 1 -Component $function -LogOnly
    Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
    Write-UcmLog -Message 'Optional Arguments' -Severity 1 -Component $function -LogOnly
    Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

    #endregion FunctionSetup

    #region FunctionWork

    #Check to see if we are connected to MSOL
    $Test = (Test-UcmMSOLConnection -Reconnect)
    If ($Test.Status -ne "OK")
    {
        #MSOL check failed, return an error.
        Write-UcmLog -Message "Something went wrong checking $UPN's service plans" -Severity 3 -Component $function
        Write-UcmLog -Message "Test-UcmMSOLConnection could not locate an MSOL connection" -Severity 2 -Component $function
        $Return.Status = "Error"
        $Return.Message = "No MSOL Connection"
        Return $Return
    }

    #Set a flag to see if the licence is enabled
    $Enabled =$false

    #Get the users licences and enumerate through them looking for the relevant service plan
    $LicenseDetails = (Get-MsolUser -UserPrincipalName $UPN).Licenses

    ForEach ($License in $LicenseDetails)
    {
        #check the relevant service plans on this licence.
        ForEach ($ServiceStatus in $License.ServiceStatus)
        {
            #check to see if the service name matches the requested name and its status
            If ($ServiceStatus.ProvisioningStatus -eq 'Disabled' -and $_.ServicePlan.ServiceName -eq "$serviceName")
            {
                #Found the service plan, and its disabled, $Enabled is already $False, no need to set anything.
                Write-UcmLog -Message "$ServiceName Disabled" -Severity 2 -Component $function
            }
            Elseif ($ServiceStatus.ProvisioningStatus -eq 'Success' -and $_.ServicePlan.ServiceName -eq "$serviceName")
            {
                #Found the service plan, and its provisioned, Set the Enabled flag to true
                Write-UcmLog -Message "$ServiceName Enabled" -Severity 2 -Component $function
                $Enabled = $true
            }
            ElseIf ($ServiceStatus.ServicePlan.ServiceName -like "*$serviceName*")
            {
                #Found the service plan, and its not provisioned properly, this could be to a service issue or a multitude of other reasons (IE, MCOEV will error if the LineURI is already assigned). return an error
                Write-UcmLog -Message "$ServiceName is in unknown state , you may need to run '(Get-MsolUser -UserPrincipalName $UPN).Licenses' for more information" -Severity 3 -Component $function
                $Enabled = 'Error'
            }
        } #End Service Plan Status Loop

    }#End licence loop

    If ($enabled -eq 'Error')
    {
        #Encounted an error with Service Plan, return an error.
        Write-UcmLog -Message "$ServiceName is in unknown state" -Severity 2 -Component $function
        $Return.Status = 'ERROR'
        $Return.Message  = "$ServiceName is in unknown state, you may need to run '(Get-MsolUser -UserPrincipalName $UPN).Licenses' for more information"
        Return $Return
    }
    Elseif ($enabled -eq $true)
    {
        #The ServicePlan is enabled, return OK
        Write-UcmLog -Message "$ServiceName Enabled" -Severity 2 -Component $function
        $Return.Status = 'OK'
        $Return.Message  = "$ServiceName Enabled"
        Return $Return
    }
    ElseIf ($_.ServicePlan.ServiceName -like "*$serviceName*")
    {
        #The ServicePlan is disabled, return a warning
        Write-UcmLog -Message "$ServiceName Disabled" -Severity 2 -Component $function
        $Return.Status = 'Warning'
        $Return.Message  = "$ServiceName Disabled"
        Return $Return
    }
    #endregion FunctionWork

    #region FunctionReturn

    #Default Return Variable for my HTML Reporting Fucntion
    Write-UcmLog -Message "Reached end of $function without a Return Statement" -Severity 3 -Component $function
    $return.Status = 'Unknown'
    $return.Message = 'Function did not encounter return statement'
    Return $Return
    #endregion FunctionReturn
}
# SIG # Begin signature block
# MIIRwgYJKoZIhvcNAQcCoIIRszCCEa8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUPQ9BL4Tlr/CSXqC7peL49TRa
# +EOggg4OMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B
# AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk
# IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5
# WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP
# DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz
# ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq
# HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC
# fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD
# G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO
# kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD
# K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4
# R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN
# Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I
# U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA
# FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4
# oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv
# b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN
# AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT
# Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT
# auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9
# ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37
# 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0
# HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL
# 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1
# 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8
# M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF
# hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy
# 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHVjCC
# BT6gAwIBAgIQDyLHeeRvkUFg5QtSFTT8FjANBgkqhkiG9w0BAQsFADBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMB4XDTIzMDIyMzAwMDAwMFoXDTI2MDIyMzIzNTk1OVowXjELMAkGA1UEBhMC
# QVUxETAPBgNVBAgTCFZpY3RvcmlhMRAwDgYDVQQHEwdCZXJ3aWNrMRQwEgYDVQQK
# EwtKYW1lcyBBcmJlcjEUMBIGA1UEAxMLSmFtZXMgQXJiZXIwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQC47oExh25TrxvApIYdMRYvjOdZCb8WwgeTemm3
# ZY7BElIWu6+gzRGqQe8RFsN7oIgin5pvjTYIToxt1CCag2A5o8L0NtULmxJEegc+
# VaF24DZQqI4qGQGH/Qnglqys6+yPkwLnfeSxpeWe4u49HUUGDFIxHCh42MlCLp/f
# fHT49QhhpO+LyeLnDoUs6DmahyIb6NeE2cW5AYRXEesW7GRNfXzygBSlVWJOgvcy
# V5Y4IvAZVx2hKKMTjYFIz4/RYMg7fwYZEJ2LRJ/GnVazobKAvh6ZBet5KwVNI9EI
# 29DtWQyK/RoPOguTRcB5VuiZVlv0xjBYM7iJuH2Soa3StQYVxL/5gjZCC9WOs4NR
# EIGU3XmHoogFDvoT1vf1izMPFQzdZfgPvy/XXsbgTVo5ncesJ6WtZwqwCXG1K0XW
# IPZqTHolc1MyU6K1bEHO+7YWLpKgM9THl644G7PEhcKpNDsHlfvLVQdYhI55UJtc
# iyMrTw11CNECvk3GK1mrluvKsrxdaH6G3Sp9VVHRtef6OZ5SlzkM5ID4egB2bXRb
# R/69bEuZr5hhm+v2lBSWIbZj/Mva6i/a/TAvy4vvPLo3DRcASkYZDC4T8gDMzmpG
# Xs4jAc9sfTL9z+o5u1PLJHFGRjJ+Wa2CgSftCdbKLjn+AY9m8ipc8jmOBKNY9yGI
# pQWapQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0
# TkIwHQYDVR0OBBYEFOBsg1xudlbXVSql8pWbiHoTyZS/MA4GA1UdDwEB/wQEAwIH
# gDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRw
# Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hB
# Mzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcC
# ARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCB
# hDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUF
# BzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVk
# RzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAA
# MA0GCSqGSIb3DQEBCwUAA4ICAQBOh5vRXqTCQzv0T1F2EgDkq5VnljlHsa7Ov/d/
# lpOvr8NebBiatxbhfhPCnToY7BD2f7YVsUuQ+VDdcIYsskcU5spBHcFYidg2jGu4
# 59FGMaS765XStDwGGTN/360gEsNYSnKWYL4+8jYWHlzRO0jHloyWz+gF5dYWzdDJ
# u1dudLIJ0RgrEVJeLSgIBWygLL5EyIzOPlrxztsILMSbdPTQLeBIm7ipOk4EACx1
# hhBVUsUoCAlASH+yCKDU4v2HFd7SzrkRUrf7XJ2Na2YsiHjiTGqHIE86KyvxGDhT
# 3n2/jX23Nh/bkWHurHwTfaTCOQ44ZlAbnZQjBlmrFn5hPMXRpciiQFmrKTPD/nuo
# 9MVnCciHEpHJ63/JZNF/eno1122/wVkL7MuRlCVHN7L/wuNQxQk3ARdIju6OD/Gi
# Mwg0Qih6HVWJtkHK3ExoUKKKUZCOvIeHxzp+K6FWUupPZKUgWzn4AHMxm6zr+Sde
# laIAACqAkxYsDYKbM7WlNi3uIH2HeXqU9uSDt5tgPpImrog/ab4HrhpDfITRgT1c
# cxaWQezpJEPC+kqVD41T3wlEie1Qm4vYWg+oBVEMBxVLh6CYbeppCRTEXRGnAiCH
# /Ma1uwyWnNCWxrhd1uSi6sj4ISzgnFyGCvsI0gavKpS5AQhapJgk6/fULTFeS+Ee
# kRH9FDGCAx4wggMaAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2ln
# bmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDyLHeeRvkUFg5QtSFTT8FjAJ
# BgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAj
# BgkqhkiG9w0BCQQxFgQUSon13bKq5yeajFpfVpKJbX6iT8cwDQYJKoZIhvcNAQEB
# BQAEggIAGa1CHRdEVsW6kszPeaA5yjJpDEmU8EWtI8D1uYOUaGy9NifnA30FrDFX
# Jj+asWLVkeSqzHW91pE9LlvEJZUHIjbhTivq6i50UD79o/6tmUGFtgWT+TPfnqpJ
# Bb7vPQtaweagu4aX6ock+l2t83wDgxV+yDA9w49VgffL23X4SNAZnttaYBkSVWQS
# 30sI5U1fQrIwOpkLP77i0Zw0SHY73N3d+JEeMAgKYooVVhNOY9xlH+ZVw2ffN2tY
# iHfJceF9XXmw3vLWoVDsc9PFY2mDodaX6ABR7XcksqNlbeMzEHH+9xxtUKa3uaX5
# LoI1/2SDiIyuAxll8B2degDJggRLE0ycdh7rgFGVg6exgfitVIhW6yyujcx2Al4H
# hG+/cxYI1hDFsljTt/qZzUeUxOEUtWloz1GNzn/aU3gdu55ONM2g9Q6o53Qzsgx6
# GFfhZIUedkPF0RGMGJyNSRYeNrOk3ncU5EYIgezwr82m+83ay0778dwQJ799e1rg
# ERPadyx/ZXvd4FyFmzPE9bhUGdM1774s1Ypf/rD1eGIXs2AUdLvUHoekDevFKeQ9
# r6n7U7UosREi1ffA+CdOmdS1XmtLANrmY5hXp8LmVUXBtMpAyg9yNqcp93v9XDun
# 2Ls0d4K1K2HNpxOaU2cu1N76kIKOU0phYuW8op9s+dcO8eOSgDE=
# SIG # End signature block