<#  M365 Analytics Log Collector #>
#Requires -RunAsAdministrator
#----------------------------------------------------------------------------------------------------------
#
#                                          Parameter Declarations
#
#----------------------------------------------------------------------------------------------------------

Param(
# File share to store logs, the maximum length is 130 since the script would create sub folders and files 
[Parameter(Position=1)]
[string]$LogPath,

# LogMode == 0 log to console only
# LogMode == 1 log to file and console
# LogMode == 2 log to file only
[Parameter(Position=2)]
[Int16]$LogMode = 1,

# CollectNetTrace == 0 to disable collect Net Trace, otherwise enable collect Net Trace.
[Parameter(Position=3)]
[Int16]$CollectNetTrace = 0,

# CollectUTCTrace == 0 to disable collect UTC Trace, otherwise enable collect UTC Trace.
[Parameter(Position=4)]
[Int16]$CollectUTCTrace = 0
)

#----------------------------------------------------------------------------------------------------------
#
#                                          Startup
#
#----------------------------------------------------------------------------------------------------------

# Make sure we are running x64 PS on 64-bit OS. If not then start a x64 process of PowerShell
$powerShellHome = $PSHOME.ToLower()
if ([System.Environment]::Is64BitOperatingSystem -eq $true)
{
    if ([System.Environment]::Is64BitProcess -eq $false)
    {
        Write-Verbose "Launching x64 PowerShell"
        $powerShellHome = $powerShellHome.Replace('syswow64','sysnative')
        &"$powerShellHome\powershell.exe" -ExecutionPolicy AllSigned -NonInteractive -NoProfile $myInvocation.Line
        exit $lastexistcode
    }
}

#----------------------------------------------------------------------------------------------------------
#
#                                          Parameter Intialization and Validation 
#
#----------------------------------------------------------------------------------------------------------

# Parameter: $LogPath
if([String]::IsNullOrEmpty($LogPath) -or [String]::IsNullOrWhiteSpace($LogPath))
{
    # Set to default value
    $LogPath = "$Env:SystemDrive\M365AnalyticsLogs"
}
else
{
    Write-Verbose "Validating path length no more than 130: $LogPath"
    $LogPath = $LogPath.Trim().TrimEnd('\')
    if($LogPath.Length -gt 130)
    {
        throw "Failed to validate the length of the given path: $LogPath"
    }

    # Validate parameter: $LogPath
    Write-Verbose "Validating path format: $LogPath"
    $validateResult = $false
    
    if((Test-Path $LogPath -IsValid) -eq $true)
    {
        $testSplitArray = $LogPath.Split(':')

        if($testSplitArray.Count -eq 1)
        {
            $validateResult = $true
        }
        elseif($testSplitArray.Count -eq 2)
        {
            $targetDrv = Get-PSDrive -Name $testSplitArray[0]   

            if($targetDrv -ne $null)
            {
                $fileDrv = Get-PSProvider -PSProvider FileSystem

                if($fileDrv -ne $null)
                {
                    if($fileDrv.Drives.Contains($targetDrv) -eq $true)
                    {
                         $validateResult = $true
                    }
                }
            }
        }
    }

    if($validateResult -eq $false)
    {
        throw "Failed to validate the format of the given path: $LogPath"
    }
}

Write-Verbose "Output Path = $LogPath"

# Parameter: $LogMode
Write-Verbose "Validating log mode(0|1|2): $LogMode"

if(($LogMode -ne 0) -and ($LogMode -ne 1) -and ($LogMode -ne 2))
{
    throw "Failed to validate the given log mode: $LogMode"
}

Write-Verbose "Log Mode = $LogMode"

# Parameter: $CollectNetTrace
if($CollectNetTrace -eq 0)
{
    Write-Verbose "Collect Net Trace = No"
}
else
{
    Write-Verbose "Collect Net Trace = Yes"
}

# Parameter: $CollectUTCTrace
if($CollectUTCTrace -eq 0)
{
    Write-Verbose "Collect UTC Trace = No"
}
else
{
    Write-Verbose "Collect UTC Trace = Yes"
}


#----------------------------------------------------------------------------------------------------------
#
#                                          Global Variables
#
#----------------------------------------------------------------------------------------------------------

# Temporary file to store providers
$global:tempProviderFile = [System.IO.Path]::GetTempFileName()

# Providers info
$global:providersText = @"
{43ac453b-97cd-4b51-4376-db7c9bb963ac}	0	255
{56DC463B-97E8-4B59-E836-AB7C9BB96301}	0	255
"@

# Script folder root
$global:scriptRoot = Split-Path -Path $MyInvocation.MyCommand.Path

# Set the exit code to the first exception exit code
$global:errorCode = [string]::Empty;

# Total error count while running the script
[int]$global:errorCount = 0;

# OS version
$global:osVersion = (Get-WmiObject Win32_OperatingSystem).Version

# OS name
$global:operatingSystemName = (Get-WmiObject Win32_OperatingSystem).Name

# OS Architecture
$global:osArchitecture = $ENV:Processor_Architecture

# Global utc trace name
$global:timeStart=Get-Date
$global:timeStartString=$global:timeStart.ToString("yy_MM_dd_HH_mm_ss")
$global:utcTraceName = "utctrace" + $global:timeStartString

#----------------------------------------------------------------------------------------------------------------
#
#                                                   Main
#
#----------------------------------------------------------------------------------------------------------------

$main = {
    Try
    {    
        # Initialize provider file
        InitializeProviderFile

        # Quit if System variable WINDIR is not set
        Try
        {
            $global:windir=[System.Environment]::ExpandEnvironmentVariables("%WINDIR%")
        }
        Catch
        {
            $exceptionDetails = "Exception: " + $_.Exception.Message + "HResult: " + $_.Exception.HResult
            Write-Error "Failure finding system variable WINDIR. $exceptionDetails Error 23"
            [System.Environment]::Exit(23)
        }
                
        # Get Sqm Machine Id
        Get-SqmID
        
        # Get ConfigMgr Client Version
        Get-CmClientVersion
        
        # Create the log file if logMode requires logging to file.
        CreateLogFile

        Log "Starting M365AnalyticsLogsCollector"
        Log "UTC DateTime: $global:utcDate"
        Log "OS: $global:osVersion"
        Log "Architecture: $global:osArchitecture"
        Log "Machine Sqm Id: $global:sClientId"
        Log "Configuration Manager Client Version: $global:smsClientVersion"

        # Sets VerboseMode to enable appraiser logging value to the registry
        SetAppraiserVerboseMode

        # Sets RequestAllAppraiserVersions key
        if($global:osBuildNumber -lt 10240)
        {
            SetRequestAllAppraiserVersions
        }

        if($CollectNetTrace -ne 0)
        {
            # Start Netsh trace
            StartNetworkTrace
        }

        if($CollectUTCTrace -ne 0)
        {
            #Start UTC trace
            StartUTCTrace
        }

        # restart Diagtrack service
        RestartDiagtrack

        # Run Connectivity Tool
        RunConnectivityDiagnosis

        # Run Census
        RunCensus

        if($CollectNetTrace -ne 0)
        {
            # Stop Netsh trace
            StopNetworkTrace
        }        

        if($CollectUTCTrace -ne 0)
        {
            # Stop UTC trace
            StopUTCTrace
        }        

        # Run Appraiser
        RunAppraiser

        # Collect the logs
        Try
        {
            Log "Running diagnose_internal to collect logs"
            DiagnoseInternal $global:logFolder
        }
        Catch
        {
            Log "diagnose_internal failed with unexpected exception" "Error" "37" "diagnose_internal" $_.Exception.HResult $_.Exception.Message
        }

        # Collect M365AHandler logs
        CollectM365AHandlerLog

        if($global:errorCount -eq 0)
        {
            Log "Script finished successfully"
            Exit(0)
        }
    }
    Catch
    {
        Log "Unexpected error occured while executing the script" "Error" "1" "UnExpectedException" $_.Exception.HResult $_.Exception.Message
        Log "Script failed" "Failure" "1" "ScriptEnd"
        [System.Environment]::Exit(1)
    }
    Finally
    {
        # Disable appriaser verbose mode after running the appriaser
        DisableAppraiserVerboseMode

        # Restart Diagtrack service
        RestartDiagtrack

        # Cleanup temporary file
        Remove-Item -Path $global:tempProviderFile
    }
}

#----------------------------------------------------------------------------------------------------------
#
#                                          Function Definitions
#
#----------------------------------------------------------------------------------------------------------

function InitializeProviderFile
{
    $global:providersText | Out-File $global:tempProviderFile -Append -Encoding ascii
}

function DiagnoseInternal($diaLogPath)
{
    if ((test-path $diaLogPath) -eq $false)
    {
        New-Item -ItemType Directory -Path $diaLogPath -Force | Out-Null
    }

    Get-WmiObject -Query 'select * from win32_quickfixengineering' | sort hotfixid | Out-File "$diaLogPath\installedKBs.txt" -Force

    ROBOCOPY "$env:windir\appcompat" "$diaLogPath\appcompat" *.* /E /XF *.hve* /R:1  | Out-Null

    regedit /e "$diaLogPath\RegAppCompatFlags.txt" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags"
    regedit /e "$diaLogPath\RegCensus.txt" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Census"
    regedit /e "$diaLogPath\RegSQM.txt" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SQMClient"
    regedit /e "$diaLogPath\RegDiagTrack.txt" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Diagnostics\DiagTrack"
    regedit /e "$diaLogPath\RegPoliciesDataCollection.txt" "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection"
    regedit /e "$diaLogPath\RegDataCollection.txt" "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DataCollection"
}

function Get-SqmID
{
    Try
    {
        $sqmID = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\SQMClient -Name MachineId).MachineId
        $global:sClientId = "s:" + $sqmID.Substring(1).Replace("}", "")
    }
    Catch
    {
        $hexHresult = "{0:X}" -f $_.Exception.HResult
        $exceptionMessage = $_.Exception.Message
        Write-Error "Get-SqmID failed with unexpected exception.`nException: $exceptionMessage HResult:  0x$hexHresult"
        [System.Environment]::Exit(38)
    }
}

function Get-CmClientVersion
{
    try {
        $propertyPath = "HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client"
        $propertyName = "SmsClientVersion"
        if (Test-Path -Path $propertyPath)
        {
            $property = Get-ItemProperty -Path $propertyPath -Name $propertyName -ErrorAction SilentlyContinue
            if ($null -eq $property)
            {
                Log "Get-CmClientVersion: Could not find registry value $propertyName at path $propertyPath" "Warning"
            }
            else {
                $global:smsClientVersion = $property.SmsClientVersion
            }
        }
        else 
        {
            Log "Get-CmClientVersion: Could not find registry key $propertyPath" "Warning"
        }
    }
    catch {
        Log "Get-CmClientVersion: Error getting $propertyName registry value at path $propertyPath" "Warning" $null "Get-CmClientVersion" $_.Exception.HResult $_.Exception.Message
    }    
}

function CreateLogFile
{
    Write-Verbose "Creating output folder"
    $timeStart=Get-Date
    $timeStartString=$timeStart.ToString("yy_MM_dd_HH_mm_ss")
    $logFolderName = "M365AnalyticsLogs_" + $timeStartString
    $global:logFolder = $logPath +"\"+$logFolderName

    Try
    {   
        $outputFolder = New-Item $global:logFolder -type directory
        Write-Host "Output folder created successfully: $outputFolder"
    }
    Catch
    {
        $hexHresult = "{0:X}" -f $_.Exception.HResult
        $exceptionMessage = $_.Exception.Message
        Write-Error "Could not create output folder at the given logPath: $LogPath`nException: $exceptionMessage HResult:  0x$hexHresult"
        [System.Environment]::Exit(28)
    }

    if($LogMode -ne 0)
    {
        Write-Verbose "Creating Log File"
        $fileName = $logFolderName+".txt"
        $global:logFile=$global:logFolder+"\"+$fileName

        Try
        {
            New-Item $global:logFile -type file | Out-Null
            Write-Verbose "Log File created successfully: $global:logFile"
        }
        Catch
        {
            $hexHresult = "{0:X}" -f $_.Exception.HResult
            $exceptionMessage = $_.Exception.Message
            Write-Error "Could not create log file at the given logPath: $LogPath`nException: $exceptionMessage HResult:  0x$hexHresult"
            [System.Environment]::Exit(28)
        }
    }
}

function StartNetworkTrace
{
    Try
    {
        Log "Start: StartNetworkTrace"
        netsh trace start capture=yes scenario=InternetClient provider=Microsoft-Windows-CAPI2 traceFile="$global:logFolder\nettrace.etl" | Out-Null
        Log "Passed: StartNetworkTrace"
    }
    Catch
    {
        Log "StartNetworkTrace failed with an unexpected exception." "Error" "2001" "StartNetworkTrace" $_.Exception.HResult $_.Exception.Message
    }
}

function StartUTCTrace
{
    Try
    {
        Log "Start: StartUTCTrace"
        
        $logmanFolder = $null
        if($global:osArchitecture.contains("64"))
        {
            $logmanFolder = "$global:windir\system32\"
        }
        else
        {
            $logmanFolder = "$global:windir\system32\"
        }

        & logman start $global:utcTraceName -pf "$global:tempProviderFile" -bs 32768 -nb 128 -ets -o "$global:logFolder\DAUTCtrace.etl" | Out-Null
                
        Log "Passed: StartUTCTrace"
    }
    Catch
    {
        Log "StartUTCTrace failed with an unexpected exception." "Error" "2003" "StartUTCTrace" $_.Exception.HResult $_.Exception.Message
    }   
}

function StopNetworkTrace
{
    Try
    {
        Log "Start: StopNetworkTrace"
        netsh trace stop | Out-Null
        Log "Passed: StopNetworkTrace"
    }
    Catch
    {
        Log "StopNetworkTrace failed with an unexpected exception." "Error" "2002" "StopNetworkTrace" $_.Exception.HResult $_.Exception.Message
    }
}

function StopUTCTrace
{
    Try
    {
        Log "Start: StopUTCTrace"

        $logmanFolder = $null
        if($global:osArchitecture.contains("64"))
        {
            $logmanFolder = "$global:windir\system32\"
        }
        else
        {
            $logmanFolder = "$global:windir\system32\"
        }

        & logman.exe stop $global:utcTraceName -ets | Out-Null

        Log "Passed: StopUTCTrace"
        Log "Collect DownloadedSettings to log folder"
        & takeown -f "$Env:ProgramData\Microsoft\Diagnosis\DownloadedSettings\*" | Out-Null
        & icacls "$Env:ProgramData\Microsoft\Diagnosis\DownloadedSettings\*" /grant administrators:f | Out-Null
        New-Item $global:logFolder\DownloadedSettings -type directory | Out-Null
        Copy-Item "$Env:ProgramData\Microsoft\Diagnosis\DownloadedSettings\*" -Destination $global:logFolder\DownloadedSettings | Out-Null
    }
    Catch
    {
        Log "StopUTCTrace failed with an unexpected exception." "Error" "2004" "StopUTCTrace" $_.Exception.HResult $_.Exception.Message
    } 
    
}

function RestartDiagtrack
{
    Log "Start: RestartDiagtrack"
    Try
    {
        & Net stop diagtrack | Out-Null
        & Reg add hklm\software\microsoft\windows\currentversion\diagnostics\diagtrack\testhooks /v ResetEventStore /t REG_DWORD /d 1 /f | Out-Null 
        & Net start diagtrack | Out-Null 
        & Reg delete hklm\software\microsoft\windows\currentversion\diagnostics\diagtrack\testhooks /v ResetEventStore /f | Out-Null
        Log "Passed: RestartDiagtrack"
    }    
    Catch
    {
        Log "RestartDiagtrack failed to execute - script will continue." "Warning" $null "RestartDiagtrack" $_.Exception.HResult $_.Exception.Message
        return
    }
}

function RunConnectivityDiagnosis
{
    Log "Start: RunConnectivityDiagnosis"
    Try
    {
        $propertyPath = "HKLM:\SOFTWARE\Microsoft\CCM\Logging\@Global"
        if(Test-Path -Path $propertyPath)
        {
            if ((Get-ItemProperty -Path $propertyPath -Name LogDirectory -ErrorAction SilentlyContinue) -eq $null)
            {
	        Log "Could not find registry key LogDirectory at path HKLM:\SOFTWARE\Microsoft\CCM\Logging\@Global"
            }
            else
            {
                Try
                {
                    $logDirectoryKeyM365 = Get-ItemProperty -Path $propertyPath -Name LogDirectory
                    $logDirectoryM365 = $logDirectoryKeyM365.LogDirectory
                    $connectivitydiagnosis = $logDirectoryM365.ToString().Replace("Logs", "settingsplugins\connectivitydiagnosis.exe")
                    
                    if((Test-Path -Path $connectivitydiagnosis) -eq $False)
                    {
                       $connectivitydiagnosis = $logDirectoryM365.ToString().Replace("Logs", "connectivitydiagnosis.exe") 
                    }

                    #Log $connectivitydiagnosis
                    #Log $logDirectoryM365
                    $currentDirectory = $global:scriptRoot
                    & cd $global:logFolder
                    & timeout 5 | Out-Null
                    & $connectivitydiagnosis -verbose > ConnectivityDiagnosis.txt 
                    & timeout 5 | Out-Null
                    & cd $currentDirectory
                    Log "Passed: RunConnectivityDiagnosis"
	        }
	        Catch
                {
		    Log "Error running RunConnectivityDiagnosis" "Warning" $null "RunConnectivityDiagnosis" $_.Exception.HResult $_.Exception.Message
                    return
	        }
            }
        }
    }
    Catch
    {
	Log "Error running RunConnectivityDiagnosis" "Warning" $null "RunConnectivityDiagnosis" $_.Exception.HResult $_.Exception.Message
    }
}

function SetAppraiserVerboseMode
{
    Log "Start: SetAppraiserVerboseMode"
    Try
    {
        $vAppraiserPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Appraiser"
        Log "Enabling Appraiser logs for debugging by setting VerboseMode property to 1 at the registry key path: $vAppraiserPath"
        if ((Get-ItemProperty -Path $vAppraiserPath -Name VerboseMode -ErrorAction SilentlyContinue) -eq $null)
        {
	    Try
            {
		New-ItemProperty -Path $vAppraiserPath -Name VerboseMode -PropertyType DWord -Value 1 | Out-Null
	    }
	    Catch
            {
		Log "SetAppraiserVerboseMode failed to write the VerboseMode property at registry key $vAppraiserPath. This is not fatal, script will continue." "Warning" $null "SetAppraiserVerboseMode" $_.Exception.HResult $_.Exception.Message
                return
	    }
        }
        else
        {
	    Log "Appraiser verbose mode is already enabled"
        }

        Log "Enabling Appraiser logs for debugging by setting TestHooksEnabled property to 1 at the registry key path: $vAppraiserPath"
        if ((Get-ItemProperty -Path $vAppraiserPath -Name TestHooksEnabled -ErrorAction SilentlyContinue) -eq $null)
        {
	    Try
            {
		New-ItemProperty -Path $vAppraiserPath -Name TestHooksEnabled -PropertyType DWord -Value 1 | Out-Null
	    }
	    Catch
            {
		Log "SetAppraiserVerboseMode failed to write the TestHooksEnabled property at registry key $vAppraiserPath. This is not fatal, script will continue." "Warning" $null "SetAppraiserVerboseMode" $_.Exception.HResult $_.Exception.Message
                return
	    }
        }
        else
        {
	    Log "Appraiser TestHooksEnabled property is already set"
        }

        Log "Passed: SetAppraiserVerboseMode"
    }
    Catch
    {
	Log "SetAppraiserVerboseMode failed with unexpected exception. This is not fatal, script will continue." "Warning" $null "SetAppraiserVerboseMode" $_.Exception.HResult $_.Exception.Message
    }
}

function DisableAppraiserVerboseMode
{
    Log "Start: DisableAppraiserVerboseMode"
    Try
    {
        $vAppraiserPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Appraiser"
        if ((Get-ItemProperty -Path $vAppraiserPath -Name VerboseMode -ErrorAction SilentlyContinue) -ne $null)
        {
	    Try
            {
		Remove-ItemProperty -Path $vAppraiserPath -Name VerboseMode
	    }
	    Catch
            {
		Log "DisableAppraiserVerboseMode failed deleting VerboseMode property at registry key path: $vAppraiserPath. This is not fatal, script will continue." "Warning" $null "DisableAppraiserVerboseMode" $_.Exception.HResult $_.Exception.Message
	    }
        }
        else
        {
	    Log "Appraiser VerboseMode key already deleted"
        }

        Log "Passed: DisableAppraiserVerboseMode"
    }
    Catch
    {
	Log "DisableAppraiserVerboseMode failed with unexpected exception. This is not fatal, script will continue." "Warning" $null "DisableAppraiserVerboseMode" $_.Exception.HResult $_.Exception.Message
    }
}

function SetRequestAllAppraiserVersions
{
    Log "Start: SetRequestAllAppraiserVersions"
    Try
    {
        $propertyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection"
        $propertyGPOPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection"
        if(Test-Path -Path $propertyPath)
        {
            if ((Get-ItemProperty -Path $propertyPath -Name RequestAllAppraiserVersions -ErrorAction SilentlyContinue) -eq $null)
            {
	        Try
                {
		    New-ItemProperty -Path $propertyPath -Name RequestAllAppraiserVersions -PropertyType DWord -Value 1 | Out-Null
	        }
	        Catch
                {
		    Log "SetRequestAllAppraiserVersions failed setting RequestAllAppraiserVersions property at registry key path: $propertyPath" "Error" "20" "SetRequestAllAppraiserVersions" $_.Exception.HResult $_.Exception.Message
                    return
                }
            }
            else
            {
                Try
                {
		    Set-ItemProperty -Path $propertyPath -Name RequestAllAppraiserVersions -Value 1
	        }
	        Catch
                {
		    Log "SetRequestAllAppraiserVersions failed setting RequestAllAppraiserVersions property at registry key path: $propertyPath" "Error" "20" "SetRequestAllAppraiserVersions" $_.Exception.HResult $_.Exception.Message
                    return
	        }
            }
        }

        if(Test-Path -Path $propertyGPOPath)
        {
            if ((Get-ItemProperty -Path $propertyGPOPath -Name RequestAllAppraiserVersions -ErrorAction SilentlyContinue) -eq $null)
            {
	        Try
                {
		    New-ItemProperty -Path $propertyGPOPath -Name RequestAllAppraiserVersions -PropertyType DWord -Value 1 | Out-Null
	        }
	        Catch
                {
		    Log "SetRequestAllAppraiserVersions failed setting RequestAllAppraiserVersions property at registry key path: $propertyGPOPath" "Error" "20" "SetRequestAllAppraiserVersions" $_.Exception.HResult $_.Exception.Message
                    return
	        }
            }
            else
            {
                Try
                {
		    Set-ItemProperty -Path $propertyPath -Name RequestAllAppraiserVersions -Value 1
	        }
	        Catch
                {
		    Log "SetRequestAllAppraiserVersions failed setting RequestAllAppraiserVersions property at registry key path: $propertyGPOPath" "Error" "20" "SetRequestAllAppraiserVersions" $_.Exception.HResult $_.Exception.Message
                    return
	        }
            }
        }

        Log "Passed: SetRequestAllAppraiserVersions"
    }
    Catch
    {
	Log "SetRequestAllAppraiserVersions failed with unexpected exception." "Error" "21" "SetRequestAllAppraiserVersions" $_.Exception.HResult $_.Exception.Message
    }
}

function RunAppraiser
{
    Try
    {
	Log "Start: RunAppraiser"
        Log "Attempting to run inventory...This may take a few minutes to complete, please do not cancel the script."

        do
        {
            CompatTelRunner.exe -m:appraiser.dll -f:DoScheduledTelemetryRun ent | out-null
            $appraiserLastExitCode = $LASTEXITCODE
            $appraiserLastExitCodeHex = "{0:X}" -f $appraiserLastExitCode

            if($appraiserLastExitCode -eq 0x80070021)
            {
                Log "RunAppraiser needs to run CompatTelRunner.exe, but it is already running. Waiting for 60 seconds before retry."
                Start-Sleep -Seconds 60
            }
            else
            {
                break
            }

            $NoOfAppraiserRetries = $NoOfAppraiserRetries - 1

        }While($NoOfAppraiserRetries -gt 0)

	if ($appraiserLastExitCode -ne 0x0)
        {
            if ($appraiserLastExitCode -lt 0x0)
            {
		Log "RunAppraiser failed. CompatTelRunner.exe exited with last error code: 0x$appraiserLastExitCodeHex."  "Error" "33" "RunAppraiser" "0x$appraiserLastExitCodeHex" "CompatTelRunner.exe returned with an error code."
            }
            else
            {
                Log "RunAppraiser succeeded with a return code: 0x$appraiserLastExitCodeHex."
            }
	    }
        else
        {
            Log "Passed: RunAppraiser"
	}
    }
    Catch
    {
        Log "RunAppraiser failed with unexpected exception." "Error" "22" "RunAppraiser" $_.Exception.HResult $_.Exception.Message
    }
}

function RunCensus
{
    Log "Start: RunCensus"
    Try
    {
        $censusRunRegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Census"

        if($(Test-Path $censusRunRegKey) -eq $false)
        {
	    New-Item -Path $censusRunRegKey -ItemType Key | Out-Null
        }

        # Turn Census FullSync mode on
        Log "Setting property: FullSync to value 1 at registry key path $censusRunRegKey to turn on Census FullSync mode"
        if ((Get-ItemProperty -Path $censusRunRegKey -Name FullSync -ErrorAction SilentlyContinue) -eq $null)
        {
	    New-ItemProperty -Path $censusRunRegKey -Name FullSync -PropertyType DWord -Value 1 | Out-Null
        }
        else
        {
            Set-ItemProperty -Path $censusRunRegKey -Name FullSync  -Value 1
        }


        # Run Census and validate the run
        # Census invocation commands are different for Windows 10 and Downlevel
        [int] $runCounterBefore = (Get-ItemProperty -Path $censusRunRegKey).RunCounter

        if($runCounterBefore -eq $null)
        {
            New-ItemProperty -Path $censusRunRegKey -Name RunCounter -PropertyType DWord -Value 0 | Out-Null
        }

        if($global:operatingSystemName.ToLower().Contains("windows 10"))
        {
            $censusExe = "$global:windir\system32\devicecensus.exe"
            if(Test-Path -Path $censusExe)
            {
                Log "Running $censusExe"
                & $censusExe | Out-Null
            }
            else
            {
                Log "$censusExe path not found" "Error" "52" "RunCensus"
                return
            }
        }
        else
        {
            CompatTelRunner.exe -m:generaltel.dll -f:DoCensusRun | Out-Null
        }

        [int] $runCounterAfter = (Get-ItemProperty -Path $censusRunRegKey).RunCounter
        $returnCode = (Get-ItemProperty -Path $censusRunRegKey).ReturnCode
        $startTime = Get-Date (Get-ItemProperty -Path $censusRunRegKey).StartTime
        $endTime = Get-Date (Get-ItemProperty -Path $censusRunRegKey).EndTime

        if($returnCode -eq 0)
        {
            if($runCounterAfter -gt $runCounterBefore -and $endTime -gt $startTime)
            {
                Log "Passed: RunCensus"
            }
            else
            {
                Log "Census did not run correctly. Registray data at $censusRunRegKey are: RunCounter Before trying to run Census:$runCounterBefore, RunCounter after trying to run Census:$runCounterAfter, ReturnCode:$returnCode, UTC StartTime:$startTime, UTC EndTime:$endTime" "Warning" $null "RunCensus"
            }
        }
        else
        {
            Log "Census returned a non zero ReturnCode:$returnCode" "Warning" $null "RunCensus"
        }

        # Turn Census FullSync mode off
        Log "Resetting property: FullSync to value 0 at registry key path $censusRunRegKey to turn off Census FullSync mode"
        Set-ItemProperty -Path $censusRunRegKey -Name FullSync  -Value 0

    }
    Catch
    {
        Log "RunCensus failed with unexpected exception" "Error" "51" "RunCensus" $_.Exception.HResult $_.Exception.Message
    }
}

function CollectM365AHandlerLog
{
    Log "Start: CollectM365AHandlerLog"
    Try
    {
        $propertyPath = "HKLM:\SOFTWARE\Microsoft\CCM\Logging\@Global"
        if(Test-Path -Path $propertyPath)
        {
            if ((Get-ItemProperty -Path $propertyPath -Name LogDirectory -ErrorAction SilentlyContinue) -eq $null)
            {
	        Log "Could not find registry key LogDirectory at path HKLM:\SOFTWARE\Microsoft\CCM\Logging\@Global"
            }
            else
            {
                Try
                {
		    $logDirectoryKeyM365 = Get-ItemProperty -Path $propertyPath -Name LogDirectory
                    $logDirectoryM365 = $logDirectoryKeyM365.LogDirectory
                    Copy-Item "$logDirectoryM365\M365AHandler.log" -Destination $global:logFolder | Out-Null
                    Log "Passed: CollectM365AHandlerLog"
	        }
	        Catch
                {
		    Log "Error getting logs at registry key LogDirectory at path HKLM:\SOFTWARE\Microsoft\CCM\Logging\@Global" "Warning" $null "CollectM365AHandlerLog" $_.Exception.HResult $_.Exception.Message
                    return
	        }
            }
        }
    }
    Catch
    {
	    Log "Error getting logs at registry key LogDirectory at path HKLM:\SOFTWARE\Microsoft\CCM\Logging\@Global" "Warning" $null "CollectM365AHandlerLog" $_.Exception.HResult $_.Exception.Message
    }

}

function Log($logMessage, $logLevel, $errorCode, $operation, $exceptionHresult, $exceptionMessage)
{
    $global:logDate = Get-Date -Format s
    $global:utcDate = ((Get-Date).ToUniversalTime()).ToString("yyyy-MM-ddTHH:mm:ssZ")
    $logMessageForAppInsights = $logMessage

    if(($logLevel -eq $null) -or ($logLevel -eq [string]::Empty))
    {
        $logLevel = "Info"
    }

    if($logLevel -eq "Error")
    {
        # check and update the errorCode (the script will exit with the first errorCode)
        if(($errorCode -ne $null) -and ($errorCode -ne [string]::Empty))
        {
            if(($global:errorCode -eq $null) -or ($global:errorCode -eq [string]::Empty))
            {
                $global:errorCode = $errorCode
            }

            $logMessage = "ErrorCode " + $errorCode + " : " + $logMessage
        }

        if($exceptionHresult -ne $null)
        {
             $logMessage = $logMessage + " HResult: " + $exceptionHresult
        }

        if($exceptionMessage -ne $null)
        {
            $logMessage = $logMessage + " ExceptionMessage: " + $exceptionMessage
        }

        $global:errorCount++
    }
    elseif($logLevel -eq "Exception")
    {
        if($exceptionHresult -ne $null)
        {
             $logMessage = $logMessage + " HResult: " + $exceptionHresult
        }

        if($exceptionMessage -ne $null)
        {
            $logMessage = $logMessage + " ExceptionMessage: " + $exceptionMessage
        }
    }
    elseif($logLevel -eq "Warning")
    {
        if($exceptionHresult -ne $null)
        {
             $logMessage = $logMessage + " HResult: " + $exceptionHresult
        }

        if($exceptionMessage -ne $null)
        {
            $logMessage = $logMessage + " ExceptionMessage: " + $exceptionMessage
        }
    }

    if ($LogMode -eq 0)
    {
        Try
        {
            WriteLogToConsole $logLevel $logMessage
        }
        Catch
        {
            # Error when logging to console
            $exceptionDetails = "Exception: " + $_.Exception.Message + "HResult: " + $_.Exception.HResult
            $message = "Error when logging to consloe."
            Write-Error "$message`n$exceptionDetails"
            SendEventToAppInsights "logging" $message "Failure" $global:utcDate "2" $_.Exception.HResult $_.Exception.Message
            [System.Environment]::Exit(2)
        }
    }
    elseif ($LogMode -eq 1)
    {
        Try
        {
            WriteLogToConsole $logLevel $logMessage
            Add-Content $global:logFile "$global:logDate : $logLevel : $logMessage"
        }
        Catch
        {
            # Error when logging to console and file
            $exceptionDetails = "Exception: " + $_.Exception.Message + "HResult: " + $_.Exception.HResult
            $message = "Error when logging to consloe and file."
            Write-Error "$message`n$exceptionDetails"
            SendEventToAppInsights "logging" $message "Failure" $global:utcDate "3" $_.Exception.HResult $_.Exception.Message
            [System.Environment]::Exit(3)
        }
    }
    elseif ($LogMode -eq 2)
    {
        Try
        {
            Add-Content $global:logFile "$global:logDate : $logLevel : $logMessage"
        }
        Catch
        {
            # Error when logging to file
            $exceptionDetails = "Exception: " + $_.Exception.Message + "HResult: " + $_.Exception.HResult
            $message = "Error when logging to file."
            Write-Error "$message`n$exceptionDetails"
            SendEventToAppInsights "logging" $message "Failure" $global:utcDate "4" $_.Exception.HResult $_.Exception.Message
            [System.Environment]::Exit(4)
        }
    }
    else
    {
        Try
        {
            WriteLogToConsole $logLevel $logMessage
            Add-Content $global:logFile "$global:logDate : $logLevel : $logMessage"
        }
        Catch
        {
            # Error when logging to console and file
            $exceptionDetails = "Exception: " + $_.Exception.Message + "HResult: " + $_.Exception.HResult
            $message = "Error when logging to consloe and file."
            Write-Error "$message`n$exceptionDetails"
            SendEventToAppInsights "logging" $message "Failure" $global:utcDate "5" $_.Exception.HResult $_.Exception.Message
            [System.Environment]::Exit(5)
        }
    }
}

function WriteLogToConsole($logLevel, $logMessage)
{
    switch ($logLevel)
    {
        "Error"   
            {    
                Write-Error "$global:logDate : $logMessage"; Break
            }
        "Exception"    
            {    
                Write-Error "$global:logDate : $logMessage"; Break
            }
        "Warning"    
            {    
                Write-Warning "$global:logDate : $logMessage"; Break
            }
        default     
            {    
                Write-Host "$global:logDate : $logMessage"; Break
            }
    }
}

# Calling the main function
&$main

# SIG # Begin signature block
# MIIoAgYJKoZIhvcNAQcCoIIn8zCCJ+8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBIE+lkqmSnrxsW
# 2upP9cl3n3z3Xa6n76xQ00V6M5n8n6CCDYUwggYDMIID66ADAgECAhMzAAADri01
# UchTj1UdAAAAAAOuMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwODU5WhcNMjQxMTE0MTkwODU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQD0IPymNjfDEKg+YyE6SjDvJwKW1+pieqTjAY0CnOHZ1Nj5irGjNZPMlQ4HfxXG
# yAVCZcEWE4x2sZgam872R1s0+TAelOtbqFmoW4suJHAYoTHhkznNVKpscm5fZ899
# QnReZv5WtWwbD8HAFXbPPStW2JKCqPcZ54Y6wbuWV9bKtKPImqbkMcTejTgEAj82
# 6GQc6/Th66Koka8cUIvz59e/IP04DGrh9wkq2jIFvQ8EDegw1B4KyJTIs76+hmpV
# M5SwBZjRs3liOQrierkNVo11WuujB3kBf2CbPoP9MlOyyezqkMIbTRj4OHeKlamd
# WaSFhwHLJRIQpfc8sLwOSIBBAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhx/vdKmXhwc4WiWXbsf0I53h8T8w
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMTgzNjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AGrJYDUS7s8o0yNprGXRXuAnRcHKxSjFmW4wclcUTYsQZkhnbMwthWM6cAYb/h2W
# 5GNKtlmj/y/CThe3y/o0EH2h+jwfU/9eJ0fK1ZO/2WD0xi777qU+a7l8KjMPdwjY
# 0tk9bYEGEZfYPRHy1AGPQVuZlG4i5ymJDsMrcIcqV8pxzsw/yk/O4y/nlOjHz4oV
# APU0br5t9tgD8E08GSDi3I6H57Ftod9w26h0MlQiOr10Xqhr5iPLS7SlQwj8HW37
# ybqsmjQpKhmWul6xiXSNGGm36GarHy4Q1egYlxhlUnk3ZKSr3QtWIo1GGL03hT57
# xzjL25fKiZQX/q+II8nuG5M0Qmjvl6Egltr4hZ3e3FQRzRHfLoNPq3ELpxbWdH8t
# Nuj0j/x9Crnfwbki8n57mJKI5JVWRWTSLmbTcDDLkTZlJLg9V1BIJwXGY3i2kR9i
# 5HsADL8YlW0gMWVSlKB1eiSlK6LmFi0rVH16dde+j5T/EaQtFz6qngN7d1lvO7uk
# 6rtX+MLKG4LDRsQgBTi6sIYiKntMjoYFHMPvI/OMUip5ljtLitVbkFGfagSqmbxK
# 7rJMhC8wiTzHanBg1Rrbff1niBbnFbbV4UDmYumjs1FIpFCazk6AADXxoKCo5TsO
# zSHqr9gHgGYQC2hMyX9MGLIpowYCURx3L7kUiGbOiMwaMIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGdMwghnPAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAOuLTVRyFOPVR0AAAAA
# A64wDQYJYIZIAWUDBAIBBQCggd4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMK3
# faqKQD4G7qVdjnlYCcOT7ptJNIWBvSzhUCNnblNkMHIGCisGAQQBgjcCAQwxZDBi
# oESAQgBEAGUAcwBrAHQAbwBwAEEAbgBhAGwAeQB0AGkAYwBzAEwAbwBnAHMAQwBv
# AGwAbABlAGMAdABvAHIALgBwAHMAMaEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAzypMT9O9UC3+hJ4aqyj63LA4eG1yjOHc/Y2J
# OtqortoqXycVSI8rjfLYH9xu84U77YschS9f5nPEGfXhgf2dcMNbpva0ANbOMytn
# rMKEhWgz8WZeWhiKOskP6rRil365jjrEzaw3xXQIsvoaEys5+WzKd/pVMTX3HMms
# vDemXn/JPnXLQGPovGw8md8AtyM+Zo53pvVpu6ZJf7czXQpRXeLz12QIE9m+uY78
# 1RfJFg2R2kn1zkAedPPsVL5szITAY8t8oUjZyEd71xTw22rkD6ak0rgcgPiBNTC9
# hINCJHV0pdcQs3wt4X02Mc1+X7au7GDhBuQy3HLAPNFzrOjeDqGCFy0wghcpBgor
# BgEEAYI3AwMBMYIXGTCCFxUGCSqGSIb3DQEHAqCCFwYwghcCAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCChl/7qbI6omZ+csG323sGjozGE77HHxLRe
# Xw+WY9dnOAIGZjOhcVduGBMyMDI0MDUwMjIwNTU1Mi40NTVaMASAAgH0oIHYpIHV
# MIHSMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsT
# HVRoYWxlcyBUU1MgRVNOOjJBRDQtNEI5Mi1GQTAxMSUwIwYDVQQDExxNaWNyb3Nv
# ZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIRfDCCBycwggUPoAMCAQICEzMAAAHenkie
# lp8oRD0AAQAAAd4wDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTAwHhcNMjMxMDEyMTkwNzEyWhcNMjUwMTEwMTkwNzEyWjCB0jELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjoyQUQ0LTRCOTItRkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALSB
# 9ByF9UIDhA6xFrOniw/xsDl8sSi9rOCOXSSO4VMQjnNGAo5VHx0iijMEMH9LY2SU
# IBkVQS0Ml6kR+TagkUPbaEpwjhQ1mprhRgJT/jlSnic42VDAo0en4JI6xnXoAoWo
# KySY8/ROIKdpphgI7OJb4XHk1P3sX2pNZ32LDY1ktchK1/hWyPlblaXAHRu0E3yn
# vwrS8/bcorANO6DjuysyS9zUmr+w3H3AEvSgs2ReuLj2pkBcfW1UPCFudLd7IPZ2
# RC4odQcEPnY12jypYPnS6yZAs0pLpq0KRFUyB1x6x6OU73sudiHON16mE0l6LLT9
# OmGo0S94Bxg3N/3aE6fUbnVoemVc7FkFLum8KkZcbQ7cOHSAWGJxdCvo5OtUtRdS
# qf85FklCXIIkg4sm7nM9TktUVfO0kp6kx7mysgD0Qrxx6/5oaqnwOTWLNzK+BCi1
# G7nUD1pteuXvQp8fE1KpTjnG/1OJeehwKNNPjGt98V0BmogZTe3SxBkOeOQyLA++
# 5Hyg/L68pe+DrZoZPXJaGU/iBiFmL+ul/Oi3d83zLAHlHQmH/VGNBfRwP+ixvqhy
# k/EebwuXVJY+rTyfbRfuh9n0AaMhhNxxg6tGKyZS4EAEiDxrF9mAZEy8e8rf6dlK
# IX5d3aQLo9fDda1ZTOw+XAcAvj2/N3DLVGZlHnHlAgMBAAGjggFJMIIBRTAdBgNV
# HQ4EFgQUazAmbxseaapgdxzK8Os+naPQEsgwHwYDVR0jBBgwFoAUn6cVXQBeYl2D
# 9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy
# MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1l
# LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQAD
# ggIBAOKUwHsXDacGOvUIgs5HDgPs0LZ1qyHS6C6wfKlLaD36tZfbWt1x+GMiazSu
# y+GsxiVHzkhMW+FqK8gruLQWN/sOCX+fGUgT9LT21cRIpcZj4/ZFIvwtkBcsCz1X
# EUsXYOSJUPitY7E8bbldmmhYZ29p+XQpIcsG/q+YjkqBW9mw0ru1MfxMTQs9MTDi
# D28gAVGrPA3NykiSChvdqS7VX+/LcEz9Ubzto/w28WA8HOCHqBTbDRHmiP7MIj+S
# QmI9VIayYsIGRjvelmNa0OvbU9CJSz/NfMEgf2NHMZUYW8KqWEjIjPfHIKxWlNMY
# huWfWRSHZCKyIANA0aJL4soHQtzzZ2MnNfjYY851wHYjGgwUj/hlLRgQO5S30Zx7
# 8GqBKfylp25aOWJ/qPhC+DXM2gXajIXbl+jpGcVANwtFFujCJRdZbeH1R+Q41Fjg
# Bg4m3OTFDGot5DSuVkQgjku7pOVPtldE46QlDg/2WhPpTQxXH64sP1GfkAwUtt6r
# rZM/PCwRG6girYmnTRLLsicBhoYLh+EEFjVviXAGTk6pnu8jx/4WPWu0jsz7yFzg
# 82/FMqCk9wK3LvyLAyDHN+FxbHAxtgwad7oLQPM0WGERdB1umPCIiYsSf/j79EqH
# doNwQYROVm+ZX10RX3n6bRmAnskeNhi0wnVaeVogLMdGD+nqMIIHcTCCBVmgAwIB
# AgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0
# IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1
# WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O
# 1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZn
# hUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t
# 1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxq
# D89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmP
# frVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSW
# rAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv
# 231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zb
# r17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYcten
# IPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQc
# xWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17a
# j54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQAB
# MCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQU
# n6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEw
# QTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9E
# b2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQB
# gjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/
# MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJ
# oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p
# Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB
# BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9v
# Q2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3h
# LB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x
# 5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74p
# y27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1A
# oL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbC
# HcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB
# 9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNt
# yo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3
# rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcV
# v7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A24
# 5oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lw
# Y1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAtgwggJBAgEBMIIBAKGB2KSB1TCB
# 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk
# TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjoyQUQ0LTRCOTItRkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAaKBSisy4y86pl8Xy
# 22CJZExE2vOggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOneH+MwIhgPMjAyNDA1MDIyMjIxMjNaGA8yMDI0MDUw
# MzIyMjEyM1oweDA+BgorBgEEAYRZCgQBMTAwLjAKAgUA6d4f4wIBADALAgEAAgMB
# HHgCAf8wBwIBAAICE/YwCgIFAOnfcWMCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYK
# KwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUF
# AAOBgQChqUVzyjSvZsd79JUJddw2P0vHrT2Azx/D5Nh33MUFAU0AUMRWx6pqbyHD
# 7Gf0OYljwMdX8TLfsorsODpvMVFyLQQhANBQMzScn6JVYUyJWGlXEFf3zEVmoXzf
# Y3wtQnrDdUVaeg9NoY9ttFqPtJamKUBRZnfPO8xtfvRBgfm8rjGCBA0wggQJAgEB
# MIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB3p5InpafKEQ9
# AAEAAAHeMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcN
# AQkQAQQwLwYJKoZIhvcNAQkEMSIEIEOH7oT0feHVIo2VpC5Q0ldGCYZmSyaDyjpn
# TKCg8LKyMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgjj4jnw3BXhAQSQJ/
# 5gtzIK0+cP1Ns/NS2A+OB3N+HXswgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt
# cCBQQ0EgMjAxMAITMwAAAd6eSJ6WnyhEPQABAAAB3jAiBCCqlMZRRFgGINs4+HhX
# dMQi8+rUCVp+3udHqe8NfDTWAjANBgkqhkiG9w0BAQsFAASCAgBGwl1+0uDZseka
# q/ceaXwJ8/fp/4Er6cxJrm7/+0ENEAkRzho/tajjVen5+RftqjYY7tdjWw01s1xw
# MEOik/nqqzCtEBqMZrGS1p67cnJBpUVpONW+inXHh7CPnOrZTQ/4olXp1UVWFczR
# rs3cFyOgrtRTL1a7/EprXVFjRWXRY+dHGho/5+C46g5rp4HR2h8ucIABrbrQ880H
# PC73+Nr7g30XgKkRkD2eUH/McZICsfi+7ONLvDMJQyUsZML6MwQtSm2jjWNe4gIw
# 2xy1RgfaJV5w0ve9hx1MU+WcjxX7Z3M1IaG+XGxf+4FpNBczzd0BY0dY5iOSCuqp
# aEDuu3DIZ9bLyN8VoFaI98plJkbba9J/nRnSRSTxbg6UoUQTiMBRLhfrEVbfGVw4
# u1ABt1mFaBX70UXhLZYYQxHG1GQnm6MSLklihtZu57yxY/VhyfH8Z3hRfAfxbyen
# HpCS5AmKJpHiRSeTC2bDlfaWGOFXwcAufxXvgu+EWNgukuXKLwtT8blZv/VxuXUs
# eq+YToI4oHcU9+hlFQ3+9pVH+xlppa8HJcIeqhY0WA9wMvIh4eIa97iAusIsiRh4
# xeeqxdppBFpIzQhRkK14sLggRZeJs7ayVQWTRbFyA5ZAxa9H+eJ3UA7WknMwh7g1
# vEFP+/1yvj66y7dD3Mv1nYRkpHizHQ==
# SIG # End signature block
