public/Enable-UcmO365Service.ps1

#PerformScriptSigning
Function Enable-UcmO365Service
{
    <#
            .SYNOPSIS
            Enables a Specified Service Plan in Office365
 
            .DESCRIPTION
            Cmdlet uses an existing Azure AD connection to filter through a users licences and enable the requested service.
            Handy if an administrator has disabled Skype for Business Online for example.
 
            .EXAMPLE
            PS> Enable-UcmO365Service -User 'button.mash@Contoso.com' -ServiceName 'MCOSTANDARD'
            Enables Skype for Business Online for the user Button Mash
 
            PS> Enable-UcmO365Service -UPN 'button.mash@contoso.com' -ServiceName 'TEAMS1'
            Enables Microsoft Teams for the user Button Mash
 
            PS> Enable-UcmO365Service -UPN 'button.mash@contoso.com' -ServiceName 'MCOPSTNEAU'
            Enables Telstra Calling (Australian version of Microsoft Calling) for the user Button Mash
 
            .PARAMETER UPN
            The users username in UPN format
 
            .PARAMETER ServiceName
            Office365 Service Plan you wish to enable
 
            .INPUTS
            This function accepts both parameter and pipline input
 
            .OUTPUT
            This Cmdet returns a PSCustomObject with multiple Keys to indicate status
            $Return.Status
            $Return.Message
 
            Return.Status can return one of three values
            "OK" : The Service Plan was enabled
            "Error" : The Service Plan was wasnt enabled, it may not have been found or there was an error setting the users attributes.
            "Unknown" : Cmdlet reached the end of the function 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.3
            Date: 13/07/2021
 
            .VERSION HISTORY
            1.3: Added a check and return if the service is already enabled instead of getting caught in the catchall error
            Updated Catchall error message
            Added MSOL cmdlet error catching and return
            Added a check to see if the Service Plan existss for that user
            Added a check to see if the user is licenced at all
 
            1.2: Fixed issue with random "-Message" messages being written to the pipeline
            Added check to only attempt to write the service changes if we actually changed something
            Added more infomative error messages when the cmdet fails to set a service
 
            1.1: Updated to "Ucm" naming convention
            Better inline documentation
 
            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
            Write-HTMLReport: https://github.com/Atreidae/UcmPsTools/blob/main/public/Write-HTMLReport.ps1 (optional)
 
            .REQUIRED PERMISSIONS
            'Office365 User Admin' or better
 
            .LINK
            https://www.UcMadScientist.com
            https://github.com/Atreidae/UcmPSTools
 
            .ACKNOWLEDGEMENTS
            Stack Overflow, disabling services: https://stackoverflow.com/questions/50492591/how-can-i-disable-and-enable-office-365-apps-for-all-users-at-once-using-powersh
            Alex Verboon, Powershell script to remove Office 365 Service Plans from a User: https://www.verboon.info/2015/12/powershell-script-to-remove-office-365-service-plans-from-a-user/
 
    #>
    
    [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 enable 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 enable, eg: 'MCOSTANDARD' for Skype Online")] [string]$ServiceName
    )


    #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
    $function = 'Enable-UcmO365Service'
    [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

    #Set a flag to track if we enabled the app or not
    $AppEnabled = $False
    #Set a flag to track if we found the app or not
    $servicePlanExists = $False

    #Check the user is even licenced in O365
    If ((Get-MsolUser -UserPrincipalName $UPN).isLicensed -ne $true)
    {
        Write-UcmLog -Message 'User does not have ANY valid O365 licence, abort' -Severity 3 -Component $function
        $Return.Status = 'Error'
        $Return.Message  = "User has no O365 licence, run 'Get-MsolUser -UserPrincipalName $Upn' for more info"
        Return $Return
    }


    #Get the user Licence details
    $LicenseDetails = (Get-MsolUser -UserPrincipalName $UPN).Licenses

    #Run through all the servicesplans on each licence, one licence at a time.
    ForEach ($License in $LicenseDetails) {
        Write-UcmLog -Message "Checking $($License.AccountSkuId) for $Servicename" -Severity 1 -Component $function

        #Find all the Disabled services and add them to an array, except for the requsted service. We need them for later
        $DisabledOptions = @()
        ForEach ($Service in $License.ServiceStatus)
        {
            #Check this service plan
            Write-UcmLog -Message "Checking $($Service.ServicePlan.ServiceName)" -Severity 1 -Component $function

            #check if we this is the plan and its already enabled
            If ($Service.ServicePlan.ServiceName -eq $ServiceName)
            {
            Write-UcmLog -Message "Found requested Serviceplan" -Severity 1 -Component $function
            $servicePlanExists = $True
            If ($Service.ProvisioningStatus -eq 'Success')
                {
                Write-UcmLog -Message "Service Plan is already enabled, returning" -Severity 1 -Component $function
                $Return.Status = 'OK'
                $Return.Message  = 'Already Enabled'
                Return $Return
                }
            }
            #Else check if the ServicePlan is disabled so we can track it (and enable it if needed)
            If ($Service.ProvisioningStatus -eq 'Disabled')
            {
                #The Service Is disabled, check to see if its the requested service
                Write-UcmLog -Message "Service Plan is currently Disabled" -Severity 1 -Component $function
                If ($Service.ServicePlan.ServiceName -eq $ServiceName)
                {
                    Write-UcmLog -Message "$Servicename Was disabled, Enabling" -Severity 2 -Component $function
                    $AppEnabled = $true
                }
                #Not the requested service, add it to the array
                Else
                {
                    Write-UcmLog -Message "$($Service.ServicePlan.ServiceName) is disabled, adding to Disabled Options" -Severity 1 -Component $function
                    $DisabledOptions += "$($Service.ServicePlan.ServiceName)"
                }
            }
            Else #$Service.ProvisioningStatus check
            {
                Write-UcmLog -Message "Service Plan is currently Enabled" -Severity 1 -Component $function
            }
        }
        #Did we change any services? if so. Set the licence options using the new list of disabled licences
        If ($AppEnabled -eq $true)
        {
            Try {
                Write-UcmLog -Message 'Setting Licence Options with the following Disabled Services' -Severity 1 -Component $function
                Write-UcmLog -Message "$DisabledOptions" -Severity 1 -Component $function

                #If there are zero options in the disabled list, dont use the -DisabledPlans flag.
                If ($DisabledOptions.count -eq 0)
                {
                    $LicenseOptions = New-MsolLicenseOptions -AccountSkuId $License.AccountSkuId
                }
                Else
                {
                    $LicenseOptions = New-MsolLicenseOptions -AccountSkuId $License.AccountSkuId -DisabledPlans $DisabledOptions
                }

                #Using the License Options attributes, set the users licence.
                Set-MsolUserLicense -UserPrincipalName $UPN -LicenseOptions $LicenseOptions

                #Reset the AppEnabled Flag
                $AppEnabled = $False

                #Set a flag so we can see it was changed
                $MadeChanges= $True


            }
            Catch #Something went wrong setting user licence
            {
                Write-UcmLog -Message 'Something went wrong assinging the licence' -Severity 3 -Component $function
                $AppEnabled = $false
                $MadeChanges= $False
                $Return.Status = 'Error'
                $Return.Message  = 'Error running New-MsolLicenseOptions or Set-MsolUserLicense'
                Return $Return
            }
        }
        #Otherwise we didnt change anything, no need to rewrite the licence
    } #Repeat for the next Licence

        #Does the service plan even exist? Abort if not
        If ($servicePlanExists -ne $true)
        {
        Write-UcmLog -Message "Could not locate $Servicename on user $UPN, make sure the appropriate licence is assigned" -Severity 3 -Component $function
        $Return.Status = 'Error'
        $Return.Message  = "Unable to locate $ServiceName"
        Return $Return
        }



    #Report on success/failure based on the $AppEnabled flag
    If ($MadeChanges){
        $Return.Status = 'OK'
        $Return.Message  = 'Enabled'
        Return $Return
    }
    Else{
        Write-UcmLog -Message "No Services were enabled, Either the service is provisioning, has an error, or it is not available with the current assigned licences" -Severity 3 -Component $function
        $Return.Status = 'Error'
        $Return.Message  = 'Service exists, but returned unknown state'
        Return $Return
    }
    #endregion FunctionWork

    #region FunctionReturn

    #Default Return Variable for UcmPsTools 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
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUigki/ZZjqG/aPAdRRYRYvpYX
# OpSggg4OMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B
# 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
# BgkqhkiG9w0BCQQxFgQUDhDAPMaem1wQqcAXYzCeAh3ENkcwDQYJKoZIhvcNAQEB
# BQAEggIAFfnq0f8T0GV7bz1TO1LTSQtuANTg2On+xyiH6xnQ+68PmzEIP8EjFsBe
# k0Uibeph6K4VFdScE7hJhl0sibaPtmYT4Hw4SCD32VBUplOlIUQoTH/wmZgm12aW
# u9A2nw5fyGklXN9HrW6a7gn8JAHjdbxm1QjBxGdDpa2v4IviIAG9VQq7ZAVT5+ZS
# U84cEGQcgbD4lKaFMWFfPK4vfUJzPYU2cBUnvlEXOSpDEFvRxr3RJGVNB8bnlQK8
# wZlDn5X28xC93MfEwinFqjDYH+gMwQOHPY24jAGNC9K8WpZ15pO+4Z21ovQ023Wz
# 6O+HFdHFKf8qyP1YHxsEY/otOfBKf+xkMJ5ZUO5ThdKSo9oPRzWlP6dqnwojyG4U
# I6URPmR2Omx102zizKsNrQww0kG0KkRT4+HN0eHL1CV+KPG761Tkqg1fV9T42lm+
# iBuV5Y5zEPP/rh+diwjRkGMBJ0Ar20VwM7Yg61Tq2vOe+f/eA0CVN6iMoTkR16PM
# G+vUqVp9S2i2uuglZywedSH0MQ9fSYchVYckbQYwcgJcqTJUNlPD7zB3VpHYu2R4
# yfCtn8cUJiJSWzvrPklRew36LwZKy3OjEznvrOrMdCeLjXBOavuF+FVwJe9jCeEz
# c0+RnnTLD6f4P2aPDAtZco7Xh7bXv7QT7Y2crAPEH0ZM6p2Qi3o=
# SIG # End signature block