ICT-Autopilot.ps1


<#PSScriptInfo
 
.VERSION 1.8
 
.GUID 1522f19a-667a-4638-8def-7d5590a61094
 
.AUTHOR a.twist@imperial.ac.uk
 
.COMPANYNAME Imperial College London
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


<#
 
.DESCRIPTION
 Checks if an ICT laptop is already enrolled in Autopilot
 
#>
 
#####
## Imperial College London
## -----------------------
## Enrolement script
##
#####
###

## This uses the microsoft script that's available here
## https://www.powershellgallery.com/packages/Get-WindowsAutoPilotInfo/3.5

function get-WindowsAutoPilotInfo
{
    
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
    [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True,Position=0)][alias("DNSHostName","ComputerName","Computer")] [String[]] $Name = @("localhost"),
    [Parameter(Mandatory=$False)] [String] $OutputFile = "", 
    [Parameter(Mandatory=$False)] [String] $GroupTag = "",
    [Parameter(Mandatory=$False)] [String] $AssignedUser = "",
    [Parameter(Mandatory=$False)] [Switch] $Append = $false,
    [Parameter(Mandatory=$False)] [System.Management.Automation.PSCredential] $Credential = $null,
    [Parameter(Mandatory=$False)] [Switch] $Partner = $false,
    [Parameter(Mandatory=$False)] [Switch] $Force = $false,
    [Parameter(Mandatory=$True,ParameterSetName = 'Online')] [Switch] $Online = $false,
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [String] $TenantId = "",
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [String] $AppId = "",
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [String] $AppSecret = "",
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [String] $AddToGroup = "",
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [String] $AssignedComputerName = "",
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [Switch] $Assign = $false, 
    [Parameter(Mandatory=$False,ParameterSetName = 'Online')] [Switch] $Reboot = $false
)

Begin
{
    # Initialize empty list
    $computers = @()

    # If online, make sure we are able to authenticate
    if ($Online) {

        # Get NuGet
        $provider = Get-PackageProvider NuGet -ErrorAction Ignore
        if (-not $provider) {
            Write-Host "Installing provider NuGet"
            Find-PackageProvider -Name NuGet -ForceBootstrap -IncludeDependencies
        }
        
        # Get WindowsAutopilotIntune module (and dependencies)
        $module = Import-Module WindowsAutopilotIntune -PassThru -ErrorAction Ignore
        if (-not $module) {
            Write-Host "Installing module WindowsAutopilotIntune"
            Install-Module WindowsAutopilotIntune -Force
        }
        Import-Module WindowsAutopilotIntune -Scope Global

        # Get Azure AD if needed
        if ($AddToGroup)
        {
            $module = Import-Module AzureAD -PassThru -ErrorAction Ignore
            if (-not $module)
            {
                Write-Host "Installing module AzureAD"
                Install-Module AzureAD -Force
            }
        }

        # Connect
        if ($AppId -ne "")
        {
            $graph = Connect-MSGraphApp -Tenant $TenantId -AppId $AppId -AppSecret $AppSecret
            Write-Host "... Connected to Intune tenant $TenantId using app-based authentication (Azure AD authentication not supported)"  -ForegroundColor "darkcyan"
        }
        else {
            $graph = Connect-MSGraph
            Write-Host "... Connected to Intune tenant $($graph.TenantId)"  -ForegroundColor "darkcyan"
            if ($AddToGroup)
            {
                $aadId = Connect-AzureAD -AccountId $graph.UPN
                Write-Host "... Connected to Azure AD tenant $($aadId.TenantId)"  -ForegroundColor "darkcyan"
            }
        }

        # Force the output to a file
        if ($OutputFile -eq "")
        {
            $OutputFile = "$($env:TEMP)\autopilot.csv"
        } 
    }
}

Process
{
    foreach ($comp in $Name)
    {
        $bad = $false

        # Get a CIM session
        if ($comp -eq "localhost") {
            $session = New-CimSession
        }
        else
        {
            $session = New-CimSession -ComputerName $comp -Credential $Credential
        }

        # Get the common properties.
        Write-Verbose "Checking $comp"
        $serial = (Get-CimInstance -CimSession $session -Class Win32_BIOS).SerialNumber

        # Get the hash (if available)
        $devDetail = (Get-CimInstance -CimSession $session -Namespace root/cimv2/mdm/dmmap -Class MDM_DevDetail_Ext01 -Filter "InstanceID='Ext' AND ParentID='./DevDetail'")
        if ($devDetail -and (-not $Force))
        {
            $hash = $devDetail.DeviceHardwareData
        }
        else
        {
            $bad = $true
            $hash = ""
        }

        # If the hash isn't available, get the make and model
        if ($bad -or $Force)
        {
            $cs = Get-CimInstance -CimSession $session -Class Win32_ComputerSystem
            $make = $cs.Manufacturer.Trim()
            $model = $cs.Model.Trim()
            if ($Partner)
            {
                $bad = $false
            }
        }
        else
        {
            $make = ""
            $model = ""
        }

        # Getting the PKID is generally problematic for anyone other than OEMs, so let's skip it here
        $product = ""

        # Depending on the format requested, create the necessary object
        if ($Partner)
        {
            # Create a pipeline object
            $c = New-Object psobject -Property @{
                "Device Serial Number" = $serial
                "Windows Product ID" = $product
                "Hardware Hash" = $hash
                "Manufacturer name" = $make
                "Device model" = $model
            }
            # From spec:
            # "Manufacturer Name" = $make
            # "Device Name" = $model

        }
        else
        {
            # Create a pipeline object
            $c = New-Object psobject -Property @{
                "Device Serial Number" = $serial
                "Windows Product ID" = $product
                "Hardware Hash" = $hash
            }
            
            if ($GroupTag -ne "")
            {
                Add-Member -InputObject $c -NotePropertyName "Group Tag" -NotePropertyValue $GroupTag
            }
            if ($AssignedUser -ne "")
            {
                Add-Member -InputObject $c -NotePropertyName "Assigned User" -NotePropertyValue $AssignedUser
            }
        }

        # Write the object to the pipeline or array
        if ($bad)
        {
            # Report an error when the hash isn't available
            Write-Error -Message "Unable to retrieve device hardware data (hash) from computer $comp" -Category DeviceError
        }
        elseif ($OutputFile -eq "")
        {
            $c
        }
        else
        {
            $computers += $c
            Write-Host "... Gathered details for device with serial number: $serial"   -ForegroundColor "darkcyan"
        }

        Remove-CimSession $session
    }
}

End
{
    if ($OutputFile -ne "")
    {
        if ($Append)
        {
            if (Test-Path $OutputFile)
            {
                $computers += Import-CSV -Path $OutputFile
            }
        }
        if ($Partner)
        {
            $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Manufacturer name", "Device model" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile
        }
        elseif ($AssignedUser -ne "")
        {
            $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Group Tag", "Assigned User" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile
        }
        elseif ($GroupTag -ne "")
        {
            $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Group Tag" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile
        }
        else
        {
            $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile
        }
    }
    if ($Online)
    {
        # Add the devices
        $importStart = Get-Date
        $imported = @()
        $computers | % {
            $imported += Add-AutopilotImportedDevice -serialNumber $_.'Device Serial Number' -hardwareIdentifier $_.'Hardware Hash' -groupTag $_.'Group Tag' -assignedUser $_.'Assigned User'
        }

        # Wait until the devices have been imported
        $processingCount = 1
        while ($processingCount -gt 0)
        {
            $current = @()
            $processingCount = 0
            $imported | % {
                $device = Get-AutopilotImportedDevice -id $_.id
                if ($device.state.deviceImportStatus -eq "unknown") {
                    $processingCount = $processingCount + 1
                }
                $current += $device
            }
            $deviceCount = $imported.Length
            Write-Host "... Waiting for $processingCount of $deviceCount to be imported"  -ForegroundColor "yellow"
            if ($processingCount -gt 0){
                Start-Sleep 30
            }
        }
        $importDuration = (Get-Date) - $importStart
        $importSeconds = [Math]::Ceiling($importDuration.TotalSeconds)
        $successCount = 0
        $current | % {
            Write-Host "... $($device.serialNumber): $($device.state.deviceImportStatus) $($device.state.deviceErrorCode) $($device.state.deviceErrorName)"  -ForegroundColor "darkcyan"
            if ($device.state.deviceImportStatus -eq "complete") {
                $successCount = $successCount + 1
            }
        }
        Write-Host "... $successCount devices imported successfully. Elapsed time to complete import: $importSeconds seconds"  -ForegroundColor "darkcyan"
        
        # Wait until the devices can be found in Intune (should sync automatically)
        $syncStart = Get-Date
        $processingCount = 1
        while ($processingCount -gt 0)
        {
            $autopilotDevices = @()
            $processingCount = 0
            $current | % {
                if ($device.state.deviceImportStatus -eq "complete") {
                    $device = Get-AutopilotDevice -id $_.state.deviceRegistrationId
                    if (-not $device) {
                        $processingCount = $processingCount + 1
                    }
                    $autopilotDevices += $device
                }    
            }
            $deviceCount = $autopilotDevices.Length
            Write-Host "... Waiting for $processingCount of $deviceCount to be synced"  -ForegroundColor "yellow"
            if ($processingCount -gt 0){
                Start-Sleep 30
            }
        }
        $syncDuration = (Get-Date) - $syncStart
        $syncSeconds = [Math]::Ceiling($syncDuration.TotalSeconds)
        Write-Host "... All devices synced. Elapsed time to complete sync: $syncSeconds seconds"  -ForegroundColor "darkcyan"

        # Add the device to the specified AAD group
        if ($AddToGroup)
        {
            $aadGroup = Get-AzureADGroup -Filter "DisplayName eq '$AddToGroup'"
            if ($aadGroup)
            {
                $autopilotDevices | % {
                    $aadDevice = Get-AzureADDevice -ObjectId "deviceid_$($_.azureActiveDirectoryDeviceId)"
                    if ($aadDevice) {
                        Write-Host "... Adding device $($_.serialNumber) to group $AddToGroup"  -ForegroundColor "darkcyan"
                        Add-AzureADGroupMember -ObjectId $aadGroup.ObjectId -RefObjectId $aadDevice.ObjectId
                    }
                    else {
                        Write-Error "Unable to find Azure AD device with ID $($_.azureActiveDirectoryDeviceId)"
                    }
                }
                Write-Host "...Added devices to group '$AddToGroup' ($($aadGroup.ObjectId))"  -ForegroundColor "darkcyan"
            }
            else {
                Write-Error "Unable to find group $AddToGroup"
            }
        }

        # Assign the computer name
        if ($AssignedComputerName -ne "")
        {
            $autopilotDevices | % {
                Set-AutopilotDevice -Id $_.Id -displayName $AssignedComputerName
            }
        }

        # Wait for assignment (if specified)
        if ($Assign)
        {
            $assignStart = Get-Date
            $processingCount = 1
            while ($processingCount -gt 0)
            {
                $processingCount = 0
                $autopilotDevices | % {
                    $device = Get-AutopilotDevice -id $_.id -Expand
                    if (-not ($device.deploymentProfileAssignmentStatus.StartsWith("assigned"))) {
                        $processingCount = $processingCount + 1
                    }
                }
                $deviceCount = $autopilotDevices.Length
                Write-Host "... Waiting for $processingCount of $deviceCount to be assigned"  -ForegroundColor "yellow"
                if ($processingCount -gt 0){
                    Start-Sleep 30
                }    
            }
            $assignDuration = (Get-Date) - $assignStart
            $assignSeconds = [Math]::Ceiling($assignDuration.TotalSeconds)
            Write-Host "...Profiles assigned to all devices. Elapsed time to complete assignment: $assignSeconds seconds"     -ForegroundColor "darkcyan"
            if ($Reboot)
            {
                Restart-Computer -Force
            }
        }
    }
}

}


clear
write-host "/---------------------------------------------------------\" -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host " This tool will check to see if a machine is enrolled in " -foreground "yellow" -nonewline; write-host "|" -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host " Autopilot and enrol if it is required " -foreground "yellow" -nonewline; write-host "|" -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host "---------------------------------------------------------" -foreground "darkcyan" -nonewline; write-host "|"  -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host " V1.8 " -foreground "blue" -nonewline; write-host "|"  -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host " Please contact EUC with any questions " -foreground "yellow" -nonewline; write-host "|"  -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host " e-mail us at: icteucteam@imperial.ac.uk " -foreground "yellow" -nonewline; write-host "|"  -foreground "darkcyan"
write-host "|" -foreground "darkcyan" -nonewline;  write-host " Or contact us on Teams " -foreground "yellow" -nonewline; write-host "|"  -foreground "darkcyan"
write-host "\---------------------------------------------------------/" -foreground "darkcyan"
write-host ""
write-host ""


## Checking TPM version
if ((Get-Tpm).ManufacturerVersionFull20) {
    $Tpm20 = -not (Get-Tpm).ManufacturerVersionFull20.Contains("not supported")
}
else {
    $Tpm20 = $false
}

Write-host "... Checking TPM version" -ForegroundColor "darkcyan"

if ($tpm20 -eq "true")
    {
        
        Write-host "... Laptop has" -ForegroundColor "darkcyan" -nonewline; write-host " TPM 2.0 " -ForegroundColor "Green" -nonewline; write-host "you may carry on" -ForegroundColor "darkcyan"
    }
else
    {
        Write-host "... This laptop does NOT have TPM 2.0, please upgrade the BIOS and start again" -ForegroundColor "red"
        Write-host "... Exiting script" -ForegroundColor "red"
        exit
    }

## Checking Windows Edition
$osedition = (Get-WmiObject -class Win32_OperatingSystem).Caption
Write-host "... Checking whether laptop is running Windows Enterprise or Pro" -ForegroundColor "darkcyan"
if ($osedition -like '*home*')
    {
        Write-host "... This machine is running $osedition, you need to be on Professional or Enterprise" -ForegroundColor "red"
        Write-host "... Please Upgrade and try again" -ForegroundColor "red"
        Write-host "... Exiting script" -ForegroundColor "red"
        exit
    }

else
    {
        Write-host "... This machine is running " -ForegroundColor "darkcyan" -nonewline; write-host "$osedition" -ForegroundColor "Green" -nonewline; write-host " which is supported by Autopilot" -ForegroundColor "darkcyan"
    }

## Checking Windows build is 1803 or higher
Write-host "... Checking whether Windows is 1803 or higher" -ForegroundColor "darkcyan"
$buildnum = (([Environment]::OSVersion).version).build
if ($buildnum -ge "17134")
    {
        Write-host "... The Windows build is " -ForegroundColor "darkcyan" -nonewline; write-host "$buildnum" -ForegroundColor "Green" -nonewline; write-host " which is supported by Autopilot" -ForegroundColor "darkcyan"
    }
else    
    {
        Write-host "... The Windows build is $buildnum which is not supported by Autopilot" -ForegroundColor "red"
        Write-host "... Please Upgrade Windows to a newer version to continue" -ForegroundColor "red"
        exit
    }


Write-host "... Getting local Serial number" -ForegroundColor "darkcyan"
$serial = Get-WmiObject win32_bios | select serialnumber -ExpandProperty serialnumber

$greenCheck = @{
    Object = [Char]8730
    ForegroundColor = 'Green'
    }

$redCross = @{
        Object = [Char]120
        ForegroundColor = 'red'
        }


        write-host "... Installing Autopilot Powershell Module" -ForegroundColor "darkcyan"
        Install-Module -Name WindowsAutoPilotIntune -force
        write-host "... Connecting to Imperial College London Tennancy"  -ForegroundColor "darkcyan"
        write-host "... Prompting for credentials"  -ForegroundColor "darkcyan"
        
        connect-msgraph | out-null
        
        $deets = Get-AutopilotDevice -Serial $serial

        # Check to see if the machine is already in Intune
        write-host "... Checking to see if the machine is already in Intune, this may take a few seconds"  -ForegroundColor "darkcyan"
        $name = (Get-IntuneManagedDevice | Get-MSGraphAllPages | Where { $_.serialnumber -EQ $serial }).devicename
        $PotName = "IC-" + $serial

        if ($name -eq $potname)
            {
                write-host "This machine already exists in Intune as $name, therefore you should be rebuilding it using Autopilot reset in the console" -ForegroundColor "yellow"
                write-host -nonewline "Do you wish to stop here and continue in the console? (Y/N)" -foregroundcolor "yellow"
                $response = read-host
                if ( $response -ne "N" ) { exit }
            }
        elseif ($name -like "*DESKTOP-*")
            {
                Write-host "This machine is in Intune but has a DESKTOP- style name, it was probably enrolled by a user or is running a failed Autopilot build." -ForegroundColor "yellow"
                write-host "We recommend that you delete the entry in Intune first and then carry on with Autopilot" -ForegroundColor "yellow"
                write-host -nonewline "Are you ready to continue (Y/N) " -foregroundcolor "yellow"
                $response = read-host
                if ( $response -ne "Y" ) { exit }
            }

        elseif ($name -like "*LAPTOP*")
            {
                Write-host "This machine is in Intune but has a LAPTOP- style name, it was probably enrolled by a user or is running a failed Autopilot build." -ForegroundColor "yellow"
                write-host "We recommend that you delete the entry in Intune first and then carry on with Autopilot" -ForegroundColor "yellow"
                write-host -nonewline "Are you ready to continue (Y/N) " -foregroundcolor "yellow"
                $response = read-host
                if ( $response -ne "Y" ) { exit }
            }        

        elseif ($name -like "*TABLET*")
            {
                Write-host "This machine is in Intune but has a TABLET- style name, it was probably enrolled by a user or is running a failed Autopilot build." -ForegroundColor "yellow"
                write-host "We recommend that you delete the entry in Intune first and then carry on with Autopilot" -ForegroundColor "yellow"
                write-host -nonewline "Are you ready to continue (Y/N) " -foregroundcolor "yellow"
                $response = read-host
                if ( $response -ne "Y" ) { exit }
            }


        else
            {
                write-host "... This machine is not in Intune, Autopilot enrolment will now continue" -ForegroundColor "darkcyan"
            }



        if ($deets.enrollmentState -eq "enrolled")
            {
                $intune = Get-IntuneManagedDevice -filter ("serialnumber eq '$serial'")
                if ($intune -eq $null )
                    {
                        write-host "... $serial is already enrolled, you may carry on with the Autopilot Build " -ForegroundColor "green"
                    }
                else
                    {
                        write-host "... $serial is enrolled and has been built by Autopilot before, do NOT pre-provision this machine"  -foreground "red"
                        write-host "... Use Autopilot Reset in the Endpoint Manager Console"  -foreground "red"
                    }
            }
            elseif ($deets.enrollmentstate -eq "notcontacted")
                    {
                        $intune = Get-IntuneManagedDevice -filter ("serialnumber eq '$serial'")
                        if ($intune -eq $null )
                            {
                                write-host "... $serial is already enrolled, you may carry on with the Autopilot Build " -ForegroundColor "green"
                            }
                        else
                            {
                                write-host "... $serial is enrolled and has been built by Autopilot before, do NOT pre-provision this machine"  -foreground "red"
                                write-host "... Use Autopilot Reset in the Endpoint Manager Console"  -foreground "red"
                            }
                    }
        else 
            {
                write-host "... $serial is NOT enrolled, enrolling now"  -ForegroundColor "darkcyan"
                Get-WindowsAutopilotInfo -Online
                write-host "... Now wait for the profile to be applied, then you can build the machine"  -ForegroundColor "darkcyan"
                write-host "... Checking the assigned status of $serial every 30 seconds until the profile is assigned"  -ForegroundColor "darkcyan"
                write-host ""
                $importStart = Get-Date
                do 
                    {
                        start-Sleep 30
                        $deets = Get-AutopilotDevice -Serial $serial
                        if ($deets.deploymentProfileAssignmentStatus -eq "notassigned" )
                            {
                                write-host "... $serial is still unassigned"  -ForegroundColor "yellow"
                            }
                        elseif ($deets.deploymentProfileAssignmentStatus -eq "pending" )
                            {
                                write-host "... $serial is being assigned a profile"  -ForegroundColor "yellow"
                            }
                        elseif ($deets.deploymentProfileAssignmentStatus -eq "assignedUnkownSyncState")
                            {
                                write-host "... $serial now has a profile assigned" -ForegroundColor "darkcyan"
                                $importDuration = (Get-Date) - $importStart
                                $importSeconds = [Math]::Ceiling($importDuration.TotalSeconds)
                                write-host "... Elapsed time to complete import of $serial : $importSeconds seconds"   -ForegroundColor "darkcyan"
                                write-host "... You can now Autopilot this machine" -ForegroundColor "green"
                            }
                    }
                until ($deets.deploymentProfileAssignmentStatus -eq "assignedUnkownSyncState"  )
            }


# SIG # Begin signature block
# MIIRCAYJKoZIhvcNAQcCoIIQ+TCCEPUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUBS9jNL29YcFwlcQsyNu5n/TH
# lraggg47MIIG7zCCBNegAwIBAgITIQAAAAQ+jdT+OF0JbAAAAAAABDANBgkqhkiG
# 9w0BAQsFADCBzTExMC8GCSqGSIb3DQEJARYiaXQtc2VjdXJpdHktb2ZmaWNlckBp
# bXBlcmlhbC5hYy51azELMAkGA1UEBhMCVUsxDzANBgNVBAgTBkxvbmRvbjEPMA0G
# A1UEBxMGTG9uZG9uMSAwHgYDVQQKExdJbXBlcmlhbCBDb2xsZWdlIExvbmRvbjFH
# MEUGA1UEAxM+SW1wZXJpYWwgQ29sbGVnZSBMb25kb24gUm9vdCBTZWN1cmUgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcNMjAwMjA0MTQ1ODE5WhcNNDEwMjA0
# MTUwODE5WjB+MRIwEAYKCZImiZPyLGQBGRYCdWsxEjAQBgoJkiaJk/IsZAEZFgJh
# YzESMBAGCgmSJomT8ixkARkWAmljMUAwPgYDVQQDEzdJbXBlcmlhbCBDb2xsZWdl
# IFN1Ym9yZGluYXRlIENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMIICIjANBgkq
# hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwWXS9zPICV4jIdQq8gEWILjc77DGVFAX
# iQw5jnbpEbp0MRybyO7xhkgvPWjApnFBQVb2MLl38kXDGfFOkk6BxM8rdzbGk7dN
# 1+g5P1HMAxBEv7g3xwOMRhruXwmcFYhXO9aK5vm+LfwgB+7cI3Rald0xH88c/j2O
# 5oyXwipm8QKTq+uiQgc1hJicGTwpee+MgIKg4ErOGd7u6WworI4Q5ssTRLc4QsTN
# 3FsH/YoNcIH8YBOf6zsyyFVAp81wwtnpXbmb12t81xL8ilJCzId621nGmSaIieTG
# t5WO3lHb5hDAR6d7j5FGvilkG5NxLqxDcTcAZwzi47J4G17XLonzG2dis5gdbxuC
# csJtI15D4yIuelM0QL9tBv1rK9L3a9rNFw68PdKGLFdy45LxNKEPobW99x8akQF9
# BqkuiCQHPwy0J2IT8mGDFrwJOkQafuZH05oD9Ve0DSEmfkrb2ov3RJCtfMeoPXX8
# qqwciLbtqBPG7RdUkadvpa/57arpaaqpxmKbKWmQkkIVb70CIjoMsifZo8xOenvJ
# lW0PR1DklgBPYFyMVgkjkFBlfByI+RKl2LGHdhGkjYor5Vm+0BpuvmafhzmKq4hl
# t3x9w6ej3PG47hfRs2/x2rle98BXdEErbOYJQDiwC9zLWakbtXRbczJdpNRfcWOQ
# VRXfK/ohEAECAwEAAaOCARQwggEQMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQW
# BBSEUdMHUDoNmOjy1Qr0hY7LRkokGzAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMA
# QTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSyoWQR
# aDlAlbayM02OogP+NFWLajCBggYDVR0fBHsweTB3oHWgc4ZxaHR0cDovL2ljY2E0
# LmljLmFjLnVrL0NlcnRFbnJvbGwvSW1wZXJpYWwlMjBDb2xsZWdlJTIwTG9uZG9u
# JTIwUm9vdCUyMFNlY3VyZSUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAy
# MC5jcmwwDQYJKoZIhvcNAQELBQADggIBAJynGpiQjCg0dKkERvQ1cjFGykU5AyFL
# su+UShC4yXsGEu07OfkmY2d+75A8p6hXzcZW/KgpWQ39eaeKITi5q5ChdsXHHszW
# /JZPFaJk1cV/DwPSKKa2KJDG1TtwYUWrVFZMR+r3ns+If6f5IPJMIEU8MmBncAo0
# KaIIljwS8uRSFBW9XmaGCrxFeRs+n2a+sjf+NUXtriCgJz+8lNGzyP9Ge1/1IU8b
# ZJuiV1/DgHSwqMHfVDxYPYY5RDHVnWRlSPN6R+d40Vd+ZxWq1jMxUCuRJPjjCmL3
# j/SuoAtJit5r+6yisR4jobV8wtgeFq7zLGYPz96/3BdPN/HEmmSLyi0AjiRY23jY
# cDAlM+cAgNgoMyoHt77PVrkYMM8NI5GSu0PcRT+pkCEyzyGdaVWUZhzN5TmPyvrj
# mwQsKuOY3B1Eid5xv+NBZGnJmJQfhiHzdu7J1h+xoLy6ylf0dkoBl1lV9JUSv8bY
# bTJ3Ds+FjwH6B/6ns7Qwq89d6WnnQJDwfWDPvOC+7s8jOGaYReijxkNE/8GUK69b
# Iw6oyCHjfMiyPMhnIMRZJP6jUPzP5Ka2xFOEWDWivfAolRtJjQbqGLQiITDLzY23
# bKIhdKL4FdGNd8a5z6HIe/r6Zhn4E4RkrJ8JP3dzbcxwLWVxxLk+GD2G0PP+vFJ8
# r24tTZBnA3U8MIIHRDCCBSygAwIBAgITbQAC4ullHJjXjzOpdwAAAALi6TANBgkq
# hkiG9w0BAQsFADB+MRIwEAYKCZImiZPyLGQBGRYCdWsxEjAQBgoJkiaJk/IsZAEZ
# FgJhYzESMBAGCgmSJomT8ixkARkWAmljMUAwPgYDVQQDEzdJbXBlcmlhbCBDb2xs
# ZWdlIFN1Ym9yZGluYXRlIENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIx
# MDgxNzE1Mjc0M1oXDTI2MDgxNjE1Mjc0M1owfDEPMA0GA1UEBxMGTG9uZG9uMSAw
# HgYDVQQKExdJbXBlcmlhbCBDb2xsZWdlIExvbmRvbjEaMBgGA1UECxMRSUNUIEVV
# QyBDb2RlIFNpZ24xKzApBgkqhkiG9w0BCQEWHGphc29uLmJlbm5ldHRAaW1wZXJp
# YWwuYWMudWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIxO4kTEjy
# 59THp/+s5SzWc/GmI5U+EIiomLUl29lrQb5uKwxg771VbtAxDC7Cp/QMiVPWcZWg
# fuiU7ku2awzlYzi9VOMQO1vodpcBX3qHux1L3c2580l0N57qJnxMjHpcsbvnGcoM
# It9Etufkv1kLmTWiQOdFG1SRFKpF1hqvqjYHghThPQwz4ZdWn9m7+qWYJSrJ5SDp
# 4PredIgfJwcmbn14/wmihuXxAdxWd3gdgGPRiKdH2WeCTlVT9IBTu9gP5FBLhXUl
# Gg7lUkqVnJyxzlP6PhjvuVvOsSf+YfPTU/+cPfHBUAhMPw6KWcVCfZ46uvqSgBft
# zXvn6s6N8lpNAgMBAAGjggK7MIICtzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3
# FQiGwpoMgYTsC82RAYTl+DaHmsUBRYHk5RmF75EkAgFkAgEIMBMGA1UdJQQMMAoG
# CCsGAQUFBwMDMAsGA1UdDwQEAwIFoDAbBgkrBgEEAYI3FQoEDjAMMAoGCCsGAQUF
# BwMDMB0GA1UdDgQWBBQlMfwwz4lk5V87PMknQfO9ttwfHDAfBgNVHSMEGDAWgBSE
# UdMHUDoNmOjy1Qr0hY7LRkokGzCB/wYDVR0fBIH3MIH0MIHxoIHuoIHrhoHobGRh
# cDovLy9DTj1JbXBlcmlhbCUyMENvbGxlZ2UlMjBTdWJvcmRpbmF0ZSUyMENlcnRp
# ZmljYXRlJTIwQXV0aG9yaXR5JTIwLTAwNzQwLENOPWljY2E0LENOPUNEUCxDTj1Q
# dWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0
# aW9uLERDPWljLERDPWFjLERDPXVrP2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/
# YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludDCB9QYIKwYBBQUH
# AQEEgegwgeUwgeIGCCsGAQUFBzAChoHVbGRhcDovLy9DTj1JbXBlcmlhbCUyMENv
# bGxlZ2UlMjBTdWJvcmRpbmF0ZSUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIw
# LTAwNzQwLENOPUFJQSxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2
# aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPWljLERDPWFjLERDPXVrP2NBQ2VydGlm
# aWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0aG9yaXR5MA0G
# CSqGSIb3DQEBCwUAA4ICAQCPxZGIOolo569qE7sw2Kuf+wjYWN6XaedFn8ATJeJj
# IAZvUHhn8fDJrrdEfAsAEWUKVmh9wCJ8O93KmpoqDLw8gdhyz/I8eXssZkssonG5
# 1n5APZ9TlA2O2CIIGWNKtMFSjm+KUKmDTbGyq9xlwY6S3u376mKYoZJNoXHveyVP
# tRdvvAthN+XgFjbWaE5Y/xykOGI+CcMy+veL5/xEOStWRN6lFCuU1Mr3FiPy018o
# a9RUNouRvLV46pSBrDvuj0glOUFqjuqClZ/2qY0pCFZHtguV3VProVAJQIo9TjS4
# RcnQQ8oxMFXTU1bT4gWDw4pR6bZS3ozmGt3GFRDRZoI0Jly+ID1CCxEKFYOw1g1z
# jM+s/FdKfMDEbLZUVzzEPxCG45TDVI3PTPS/EZ8ySwyGq6qbwsK3HXPGCR1YA6zt
# K8Ua7qweTAC4jGIQSWv4RnZkZc/th0rHQEEa8MTKDIi+HrNzfAnXhqwJdgfimAc6
# Bl7BwGKtNRVMBxjoFrX3ruXXRi6FrTDPZ/f/0HXIu6T1KlKlWoFZj0++gROD1zFR
# UE9jUW6/Ug3hbGOiDDHf6uNCTrl7eK9BAhlkJQCO1/iJ/JwQz9+pkXD/rkODVssU
# c/XQxjRwjcnwWqsbU2hPGbfZuwAseQhgVlx//63swaKYxq6gDGuCySVBwenbvJ8Z
# VDGCAjcwggIzAgEBMIGVMH4xEjAQBgoJkiaJk/IsZAEZFgJ1azESMBAGCgmSJomT
# 8ixkARkWAmFjMRIwEAYKCZImiZPyLGQBGRYCaWMxQDA+BgNVBAMTN0ltcGVyaWFs
# IENvbGxlZ2UgU3Vib3JkaW5hdGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAC
# E20AAuLpZRyY148zqXcAAAAC4ukwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwx
# CjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGC
# NwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFMju75NmuefSrsSx
# hEcc3ty4ymxoMA0GCSqGSIb3DQEBAQUABIIBADKaRj/h1aMfJXAJKje2cYlqvyzV
# 609QswkcqnBvK1udx1PG1khsInL9ItU1qA72VxR3EbOOvK/ePFi23dLnMhzSsBRt
# h8ksgxLgnbIt3IFilJjBUwqX27EGVuL3R8G5ceJwaW2rm38PH0+2Omr+DbZnMfiM
# DJHyWYEiQnPIPjSIRrAPdiPntqvUskio0/0ispDfu0XQi4yY903j4qNooUQ0kJHh
# ANLFnkWrLQ6Z02Rqlm8cSzVDZpc5XzyfE2nV6rygIl6OZsJ6D9jT2DC+KRm5vg0K
# gMEoBmyqiDPcHpsBRxWALVcmSEzhsAN1vh/3020zUwEBrtrzWL+ae50MpWc=
# SIG # End signature block