mirror of
https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
synced 2026-09-28 19:05:38 +02:00
2870 lines
119 KiB
PowerShell
2870 lines
119 KiB
PowerShell
<#PSScriptInfo
|
|
|
|
.VERSION 1.0
|
|
|
|
.GUID ab5a8b63-97d5-4b1a-a4ab-6dcedbab1eb7
|
|
|
|
.AUTHOR Petri.Paavola@yodamiitti.fi
|
|
|
|
.COMPANYNAME Yodamiitti Oy
|
|
|
|
.COPYRIGHT Petri.Paavola@yodamiitti.fi
|
|
|
|
.TAGS Intune Windows Autopilot
|
|
|
|
.LICENSEURI
|
|
|
|
.PROJECTURI https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
|
|
|
|
.ICONURI
|
|
|
|
.EXTERNALMODULEDEPENDENCIES
|
|
|
|
.REQUIREDSCRIPTS
|
|
|
|
.EXTERNALSCRIPTDEPENDENCIES
|
|
|
|
.RELEASENOTES
|
|
Version 1.0: Original published version
|
|
|
|
#>
|
|
|
|
<#
|
|
.Synopsis
|
|
This script analyzes Microsoft Intune Management Extension (IME) log(s) and creates timeline report from found actions.
|
|
|
|
.DESCRIPTION
|
|
This script analyzes Microsoft Intune Management Extension (IME) log(s) and creates timeline report from found actions.
|
|
|
|
Timeline report includes information about Intune Win32App, WinGetApp, Powershell scripts, Proactive Remedation scripts and custom Compliance Policy scripts events. Windows Autopilot ESP phases are also shown on timeline.
|
|
|
|
Script also includes really capable Log Viewer UI if scripts is started with parameter -ShowLogViewerUI
|
|
|
|
LogViewerUI (Out-GridView) looks a lot like cmtrace.exe tool but it is better because all found log actions are added to log for easier debugging.
|
|
|
|
LogViewerUI has good search and filtering capabilities. Try to filter known log entries in Timeline: Add criteria -> ProcessruntTime -> is not empty
|
|
|
|
Selecting last line (RELOAD) and OK will reload log file.
|
|
|
|
Script can merge multiple log files so especially in LogViewerUI you can see Powershell command outputs from AgentExecutor.log
|
|
|
|
Powershell command outputs and errors can be also shown in Timeline view with parameters -ShowStdOutInTimeline and -ShowErrorsInTimeline
|
|
This shows instantly what is problem in Powershell scripts
|
|
|
|
|
|
Author:
|
|
Petri.Paavola@yodamiitti.fi
|
|
Senior Modern Management Principal
|
|
Microsoft MVP - Windows and Devices for IT
|
|
|
|
2023-03-07
|
|
|
|
https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
|
|
|
|
.PARAMETER Online
|
|
Download Powershell, Proactive Remediation and custom Compliance policy scripts to get displayName to Timeline report
|
|
|
|
.PARAMETER ShowLogViewerUI
|
|
Shows graphical LogViewerUI where all log entries are easily browsed, searched and filtered
|
|
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -AllLogEntries -ShowAllTimelineEntries
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ShowLogViewerUI
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -Today
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -LogStartDateTime 5.3.2023 5.00:00 -LogEndDateTime 7.3.2023 23.00:00
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log"
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -LOGFile "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log"
|
|
.EXAMPLE
|
|
.\Get-IntuneManagementExtensionDiagnostics.ps1 -LogFilesFolder "C:\temp\MDMDiagReport"
|
|
|
|
.INPUTS
|
|
.OUTPUTS
|
|
None
|
|
.NOTES
|
|
.LINK
|
|
https://github.com/petripaavola
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$false,
|
|
HelpMessage = 'Enter Intune IME log file fullpath',
|
|
ValueFromPipeline=$true,
|
|
ValueFromPipelineByPropertyName=$true)]
|
|
[Alias("FullName")]
|
|
[String]$LOGFile = $null,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$Online,
|
|
[Parameter(Mandatory=$false,
|
|
HelpMessage = 'Enter Intune IME log files folder path',
|
|
ValueFromPipeline=$true,
|
|
ValueFromPipelineByPropertyName=$true)]
|
|
[String]$LogFilesFolder = $null,
|
|
[Parameter(Mandatory=$false,
|
|
HelpMessage = 'Enter Start DateTime for log entries (for example 1.3.2023 18:00:00)',
|
|
ValueFromPipeline=$true,
|
|
ValueFromPipelineByPropertyName=$true)]
|
|
$LogStartDateTime = $null,
|
|
[Parameter(Mandatory=$false,
|
|
HelpMessage = 'Enter End DateTime for log entries (for example 1.3.2023 19:00:00)',
|
|
ValueFromPipeline=$true,
|
|
ValueFromPipelineByPropertyName=$true)]
|
|
$LogEndDateTime = $null,
|
|
[Parameter(Mandatory=$false,
|
|
HelpMessage = 'Threshold seconds to highlight long running Powershell scripts',
|
|
ValueFromPipeline=$true,
|
|
ValueFromPipelineByPropertyName=$true)]
|
|
[int]$LongRunningPowershellNotifyThreshold = 180,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ShowLogViewerUI,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$AllLogEntries,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$Today,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ShowAllTimelineEntries,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ShowStdOutInTimeline,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ShowErrorsInTimeline,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ShowErrorsSummary,
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ConvertAllKnownGuidsToClearText
|
|
|
|
)
|
|
|
|
|
|
Write-Host "Get-IntuneManagementExtensionDiagnostics.ps1 v1.0" -ForegroundColor Cyan
|
|
Write-Host "Author: Petri.Paavola@yodamiitti.fi / Microsoft MVP - Windows and Devices for IT"
|
|
Write-Host ""
|
|
|
|
|
|
|
|
# Set variables if we are in Windows Autopilot ESP (Enrollment Status Page)
|
|
# Idea is that user can just run the script without checking Parameters first
|
|
if($env:UserName -eq 'defaultUser0') {
|
|
Write-Host "Detected running in Windows Autopilot Enrollment Status Page (ESP)" -ForegroundColor Yellow
|
|
|
|
#$LOGFile='C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log'
|
|
|
|
if((-not $LogFilesFolder) -or (-not $LOGFile)) {
|
|
Write-Host "Automatically configuring parameters"
|
|
Write-Host
|
|
|
|
$IntuneLogsPath = 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs'
|
|
if(Test-Path $IntuneLogsPath) {
|
|
$SelectedLogFiles = Get-ChildItem -Path $IntuneLogsPath -Filter *.log | Where-Object { ($_.Name -like 'IntuneManagementExtension*.log') -or ($_.Name -like 'AgentExecutor*.log') }
|
|
} else {
|
|
Write-Host "Log folder does not exist yet: $IntuneLogsPath"
|
|
Write-Host "Try again in a moment..." -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Exit 0
|
|
}
|
|
}
|
|
|
|
$AllLogEntries=$True
|
|
|
|
if(-not (Test-Path 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log')) {
|
|
Write-Host "Log file does not exist yet: C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log" -ForegroundColor Yellow
|
|
Write-Host "Try again in a moment..." -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Exit 0
|
|
}
|
|
}
|
|
|
|
|
|
# Work in Progress
|
|
<#
|
|
[Parameter(Mandatory=$false)]
|
|
[Switch]$ExportJson
|
|
#>
|
|
|
|
|
|
# Hashtable with ids and names
|
|
$IdHashtable = @{}
|
|
|
|
# Save timeline objects to this List
|
|
$observedTimeline = [System.Collections.Generic.List[PSObject]]@()
|
|
|
|
|
|
# This is aligned with Michael Niehaus's Get-AutopilotDiagnostics script just in case
|
|
# region Functions
|
|
Function RecordStatusToTimeline {
|
|
param
|
|
(
|
|
[Parameter(Mandatory=$true)] [String] $date,
|
|
[Parameter(Mandatory=$true)] [String] $status,
|
|
[Parameter(Mandatory=$false)] [String] $detail,
|
|
[Parameter(Mandatory=$false)] $seconds,
|
|
[Parameter(Mandatory=$false)] [String] $logEntry,
|
|
[Parameter(Mandatory=$false)] [String] $color
|
|
|
|
)
|
|
|
|
$observedTimeline.add([PSCustomObject]@{
|
|
'Date' = $date
|
|
'Status' = $status
|
|
'Detail' = $detail
|
|
'Seconds' = $seconds
|
|
'LogEntry' = $logEntry
|
|
'Color' = $color
|
|
})
|
|
}
|
|
# endregion Functions
|
|
|
|
|
|
Write-Host "Starting Get-IntuneManagementExtensionDiagnostics`n"
|
|
|
|
# If LogFilePath is not specified then show log files in Out-GridView
|
|
# from folder C:\ProgramData\Microsoft\intunemanagementextension\Logs
|
|
if($LOGFile) {
|
|
|
|
if(-not (Test-Path $LOGFile)) {
|
|
Write-Host "Log file does not exist: $LOGFile" -ForegroundColor Yellow
|
|
Write-Host "Script will exit" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Exit 0
|
|
}
|
|
$SelectedLogFiles = Get-ChildItem -Path $LOGFile
|
|
|
|
} else {
|
|
|
|
if($LogFilesFolder) {
|
|
# Sort files: new files first and IntuneManagementExtension before AgentExecutor
|
|
$LogFiles = Get-ChildItem -Path $LogFilesFolder -Filter *.log | Where-Object { ($_.Name -like 'IntuneManagementExtension*.log') -or ($_.Name -like 'AgentExecutor*.log') } | Sort-Object -Property Name -Descending | Sort-Object -Property LastWriteTime -Descending
|
|
} else {
|
|
# Sort files: new files first and IntuneManagementExtension before AgentExecutor
|
|
$LogFiles = Get-ChildItem -Path 'C:\ProgramData\Microsoft\intunemanagementextension\Logs' -Filter *.log | Where-Object { ($_.Name -like 'IntuneManagementExtension*.log') -or ($_.Name -like 'AgentExecutor*.log') } | Sort-Object -Property Name -Descending | Sort-Object -Property LastWriteTime -Descending
|
|
}
|
|
|
|
# Show log files in Out-GridView
|
|
# This variable is automatically configured in ESP
|
|
if(-not $SelectedLogFiles) {
|
|
$SelectedLogFiles = $LogFiles | Out-GridView -Title 'Select log file to show in Out-GridView from path C:\ProgramData\Microsoft\intunemanagementextension\Logs' -OutputMode Multiple
|
|
}
|
|
|
|
if(-not $SelectedLogFiles) {
|
|
Write-Host "No log file(s) selected. Script will exit!`n" -ForegroundColor Yellow
|
|
Exit 0
|
|
}
|
|
}
|
|
|
|
|
|
# Check whether we show time selection on Out-GridView or not
|
|
if(((-not $LogStartDateTime) -and (-not $LogEndDateTime)) -and (-not $AllLogEntries) -and (-not $Today)) {
|
|
|
|
$LogStartEndTimeOutGridviewEntries = [System.Collections.Generic.List[PSObject]]@()
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'All log entries';
|
|
'LogStartDateTimeObject' = Get-Date 1/1/1900;
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Current day from midnight';
|
|
'LogStartDateTimeObject' = Get-Date -Hour 0 -Minute 0 -Second 0;
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 5 minutes';
|
|
'LogStartDateTimeObject' = (Get-Date).AddMinutes(-5);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 15 minutes';
|
|
'LogStartDateTimeObject' = (Get-Date).AddMinutes(-15);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 30 minutes';
|
|
'LogStartDateTimeObject' = (Get-Date).AddMinutes(-30);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 1 hour';
|
|
'LogStartDateTimeObject' = (Get-Date).AddHours(-1);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 2 hours';
|
|
'LogStartDateTimeObject' = (Get-Date).AddHours(-2);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 6 hours';
|
|
'LogStartDateTimeObject' = (Get-Date).AddHours(-6);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'Last 24 hours';
|
|
'LogStartDateTimeObject' = (Get-Date).AddHours(-24);
|
|
'LogEndDateTimeObject' = (Get-Date).AddYears(1000);
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'First 30 minutes';
|
|
'LogStartDateTimeObject' = Get-Date 1/1/1900;
|
|
'LogEndDateTimeObject' = 'Analyzing DateTime later';
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'First 1 hour';
|
|
'LogStartDateTimeObject' = Get-Date 1/1/1900;
|
|
'LogEndDateTimeObject' = 'Analyzing DateTime later';
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'First 2 hours';
|
|
'LogStartDateTimeObject' = Get-Date 1/1/1900;
|
|
'LogEndDateTimeObject' = 'Analyzing DateTime later';
|
|
})
|
|
|
|
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
|
|
'Log Start and End time' = 'First 6 hours';
|
|
'LogStartDateTimeObject' = Get-Date 1/1/1900;
|
|
'LogEndDateTimeObject' = 'Analyzing DateTime later';
|
|
})
|
|
|
|
|
|
# Show predefined values in Out-GridView
|
|
$SelectedTimeFrameObject = $LogStartEndTimeOutGridviewEntries | Out-GridView -Title 'Select timeframe to show log entries' -OutputMode Multiple
|
|
|
|
if($SelectedTimeFrameObject) {
|
|
$LogStartDateTimeObject = $SelectedTimeFrameObject.LogStartDateTimeObject
|
|
$LogEndDateTimeObject = $SelectedTimeFrameObject.LogEndDateTimeObject
|
|
|
|
} else {
|
|
Write-Host "No timeframe selected." -ForegroundColor Red
|
|
Write-Host "Script will exit"
|
|
Exit 0
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
# Set default time which can be changed depenging on -Parameters
|
|
|
|
# Use StartTime from year 1900
|
|
$LogStartDateTimeObject = Get-Date 1/1/1900
|
|
|
|
# Use EndTime +1000 years from today
|
|
$LogEndDateTimeObject = (Get-Date).AddYears(1000)
|
|
|
|
if($Today) {
|
|
$LogStartDateTimeObject = Get-Date -Hour 0 -Minute 0 -Second 0
|
|
$LogEndDateTimeObject = (Get-Date).AddYears(1000)
|
|
}
|
|
|
|
if($AllLogEntries) {
|
|
# Use StartTime from year 1900
|
|
$LogStartDateTimeObject = Get-Date 1/1/1900
|
|
|
|
# Use EndTime +1000 years from today
|
|
$LogEndDateTimeObject = (Get-Date).AddYears(1000)
|
|
}
|
|
|
|
# Check $LogStartDateTime can be converted to Powershell DateTime object
|
|
if($LogStartDateTime) {
|
|
# Try converting given parameter to Powershell DateTime object
|
|
|
|
Try {
|
|
$LogStartDateTimeObject = Get-Date $LogStartDateTime
|
|
} catch {
|
|
Write-Host "Given parameter LogStartDateTime is not in valid DateTime format." -ForegroundColor Red
|
|
Write-Host "Script will exit"
|
|
Exit 0
|
|
}
|
|
}
|
|
|
|
# Check $LogEndDateTime can be converted to Powershell DateTime object
|
|
if($LogEndDateTime) {
|
|
# Try converting given parameter to Powershell DateTime object
|
|
|
|
Try {
|
|
$LogEndDateTimeObject = Get-Date $LogEndDateTime
|
|
} catch {
|
|
Write-Host "Given parameter LogEndDateTime is not in valid DateTime format." -ForegroundColor Red
|
|
Write-Host "Script will exit"
|
|
Exit 0
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Download Powershell scripts, Proactive Remediation scripts and custom Compliance Policy scripts
|
|
if ($Online) {
|
|
|
|
# Test if we are in Enrollment Status Page (ESP) phase
|
|
# Detect defaultuser0 loggedon
|
|
if($env:UserName -eq 'defaultUser0') {
|
|
|
|
# Make sure we can connect
|
|
$module = Import-Module Microsoft.Graph.Intune -PassThru -ErrorAction Ignore
|
|
if (-not $module) {
|
|
Write-Host "Installing module Microsoft.Graph.Intune"
|
|
Install-Module Microsoft.Graph.Intune -Force
|
|
}
|
|
Import-Module Microsoft.Graph.Intune
|
|
|
|
$MSGraphEnvironment = Connect-MSGraph
|
|
Write-Host "Connected to tenant $($graph.TenantId)"
|
|
|
|
|
|
} else {
|
|
Write-Host "Connecting to Intune using Powershell Intune-Module"
|
|
Update-MSGraphEnvironment -SchemaVersion 'beta'
|
|
$Success = $?
|
|
|
|
if (-not $Success) {
|
|
Write-Host "Failed to update MSGraph Environment schema to Beta!`n" -ForegroundColor Red
|
|
Write-Host "Make sure you have installed Intune Powershell module"
|
|
Write-Host "`nYou can install Intune module to your user account with command:"
|
|
Write-Host "Install-Module -Name Microsoft.Graph.Intune -Scope CurrentUser`n" -ForegroundColor Yellow
|
|
Write-Host "`nor you can install machine-wide Intune module with command:`nInstall-Module -Name Microsoft.Graph.Intune"
|
|
Write-Host "More information: https://github.com/microsoft/Intune-PowerShell-SDK"
|
|
Exit 1
|
|
}
|
|
|
|
$MSGraphEnvironment = Connect-MSGraph
|
|
$Success = $?
|
|
|
|
if ($Success -and $MSGraphEnvironment) {
|
|
$TenantId = $MSGraphEnvironment.tenantId
|
|
$AdminUserUPN = $MSGraphEnvironment.upn
|
|
|
|
Write-Host "Connected to tenant $TenantId as user $AdminUserUPN"
|
|
|
|
} else {
|
|
Write-Host "Could not connect to MSGraph!" -ForegroundColor Red
|
|
Write-Host "Will not download information from Microsoft Intune" -ForegroundColor Yellow
|
|
Write-Host "Script will exit."
|
|
Exit 0
|
|
}
|
|
}
|
|
|
|
if($MSGraphEnvironment) {
|
|
Write-Host "Download Intune Powershell scripts"
|
|
# Get PowerShell Scripts
|
|
$url = 'https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts'
|
|
$MSGraphRequest = Invoke-MSGraphRequest -Url $url -HttpMethod 'GET'
|
|
$Success = $?
|
|
|
|
if (-not ($Success)) {
|
|
Write-Error "Error downloading Intune Powershell scripts"
|
|
#return $null
|
|
} else {
|
|
$AllIntunePowershellScripts = Get-MSGraphAllPages -SearchResult $MSGraphRequest
|
|
$Success = $?
|
|
if($Success) {
|
|
Write-Host "Done" -ForegroundColor Green
|
|
|
|
# Add Name Property to object
|
|
$AllIntunePowershellScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name name -Value $_.displayName }
|
|
|
|
# Add all PowershellScripts to Hashtable
|
|
$AllIntunePowershellScripts | Foreach-Object { $id = $_.id; $value=$_; $IdHashtable["$id"] = $value }
|
|
} else {
|
|
Write-Error "Error downloading All Intune Powershell scripts"
|
|
}
|
|
}
|
|
|
|
Start-Sleep 1
|
|
|
|
Write-Host "Download Intune Proactive Remediations Scripts"
|
|
# Get Proactive Remediations Scripts
|
|
$url = 'https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts'
|
|
$MSGraphRequest = Invoke-MSGraphRequest -Url $url -HttpMethod 'GET'
|
|
$Success = $?
|
|
|
|
if (-not ($Success)) {
|
|
Write-Error "Error downloading Intune Proactive Remediations scripts"
|
|
#return $null
|
|
} else {
|
|
$AllIntuneProactiveRemediationsScripts = Get-MSGraphAllPages -SearchResult $MSGraphRequest
|
|
$Success = $?
|
|
if($Success) {
|
|
Write-Host "Done" -ForegroundColor Green
|
|
|
|
# Add Name Property to object
|
|
$AllIntuneProactiveRemediationsScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name name -Value $_.displayName }
|
|
|
|
# Add all PowershellScripts to Hashtable
|
|
$AllIntuneProactiveRemediationsScripts | Foreach-Object { $id = $_.id; $value=$_; $IdHashtable["$id"] = $value }
|
|
} else {
|
|
Write-Error "Error downloading All Intune Proactive Remediations scripts"
|
|
}
|
|
}
|
|
|
|
Start-Sleep 1
|
|
|
|
Write-Host "Download Intune Windows Device Compliance custom Scripts"
|
|
# Get Windows Device Compliance custom Scripts
|
|
$url = 'https://graph.microsoft.com/beta/deviceManagement/deviceComplianceScripts'
|
|
$MSGraphRequest = Invoke-MSGraphRequest -Url $url -HttpMethod 'GET'
|
|
$Success = $?
|
|
|
|
if (-not ($Success)) {
|
|
Write-Error "Error downloading Intune Windows custom Compliance scripts"
|
|
#return $null
|
|
} else {
|
|
$AllIntuneCustomComplianceScripts = Get-MSGraphAllPages -SearchResult $MSGraphRequest
|
|
$Success = $?
|
|
if($Success) {
|
|
Write-Host "Done" -ForegroundColor Green
|
|
|
|
# Add Name Property to object
|
|
$AllIntuneCustomComplianceScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name name -Value $_.displayName }
|
|
|
|
# Add all PowershellScripts to Hashtable
|
|
$AllIntuneCustomComplianceScripts | Foreach-Object { $id = $_.id; $value=$_; $IdHashtable["$id"] = $value }
|
|
} else {
|
|
Write-Error "Error downloading All Intune Windows custom Compliance scripts"
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host "Not connected to Microsoft Intune, skip downloading script names...." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
Write-Host ""
|
|
|
|
|
|
# Run Report and/or Out-GridView in loop as long as user selects last line which will reload log file
|
|
# Otherwise Out-GridView will exit if something else is selected than last line (reload line)
|
|
do {
|
|
|
|
# Create Generic list where log entry custom objects are added
|
|
$LogEntryList = [System.Collections.Generic.List[PSObject]]@()
|
|
|
|
# Go through all selected log file(s)
|
|
Foreach ($SelectedLogFile in $SelectedLogFiles) {
|
|
|
|
$LogFilePath = $SelectedLogFile.FullName
|
|
$LogFileName = $SelectedLogFile.Name
|
|
|
|
Write-Host "Processing file $LogFileName"
|
|
|
|
# Initialize variables
|
|
$LineNumber=1
|
|
$MultilineLogEntryStartsArrayIndex=0
|
|
$MultilineLogEntryStartFound=$False
|
|
|
|
$Log = Get-Content -Path $LogFilePath
|
|
|
|
# This matches for cmtrace type logs
|
|
# Test with https://regex101.com
|
|
# String: <![LOG[ExecutorLog AgentExecutor gets invoked]LOG]!><time="21:38:06.3814532" date="2-14-2023" component="AgentExecutor" context="" type="1" thread="1" file="">
|
|
|
|
# This matches single line full log entry
|
|
$SingleLineRegex = '^\<\!\[LOG\[(.*)]LOG\].*\<time="([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}).([0-9]{1,})".*date="([0-9]{1,2})-([0-9]{1,2})-([0-9]{4})" component="(.*?)" context="(.*?)" type="(.*?)" thread="(.*?)" file="(.*?)">$'
|
|
|
|
# Start of multiline log entry
|
|
$FirstLineOfMultiLineLogRegex = '^\<\!\[LOG\[(.*)$'
|
|
|
|
# End of multiline log entry
|
|
$LastLineOfMultiLineLogRegex = '^(.*)\]LOG\]\!>\<time="([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}).([0-9]{1,})".*date="([0-9]{1,2})-([0-9]{1,2})-([0-9]{4})" component="(.*?)" context="(.*?)" type="(.*?)" thread="(.*?)" file="(.*?)">$'
|
|
|
|
|
|
# Process each log file line one by one
|
|
Foreach ($CurrentLogEntry in $Log) {
|
|
|
|
# Get data from CurrentLogEntry
|
|
if($CurrentLogEntry -Match $SingleLineRegex) {
|
|
# This matches single line log entry
|
|
|
|
# Regex found match
|
|
$LogMessage = $Matches[1].Trim()
|
|
|
|
$Hour = $Matches[2]
|
|
$Minute = $Matches[3]
|
|
$Second = $Matches[4]
|
|
|
|
$MilliSecondFull = $Matches[5]
|
|
# Cut milliseconds to 0-999
|
|
# Time unit is so small that we don't even bother to round the value
|
|
$MilliSecond = $MilliSecondFull.Substring(0,3)
|
|
|
|
$Month = $Matches[6]
|
|
$Day = $Matches[7]
|
|
$Year = $Matches[8]
|
|
|
|
$Component = $Matches[9]
|
|
$Context = $Matches[10]
|
|
$Type = $Matches[11]
|
|
$Thread = $Matches[12]
|
|
$File = $Matches[13]
|
|
|
|
$Param = @{
|
|
Hour=$Hour
|
|
Minute=$Minute
|
|
Second=$Second
|
|
MilliSecond=$MilliSecond
|
|
Year=$Year
|
|
Month=$Month
|
|
Day=$Day
|
|
}
|
|
|
|
$LogEntryDateTime = Get-Date @Param
|
|
#Write-Host "DEBUG `$LogEntryDateTime: $LogEntryDateTime" -ForegroundColor Yellow
|
|
|
|
# This works for humans but does not sort
|
|
#$DateTimeToLogFile = "$($Hour):$($Minute):$($Second).$MilliSecondFull $Day/$Month/$Year"
|
|
|
|
# This does sorting right way
|
|
$DateTimeToLogFile = "$Year-$Month-$Day $($Hour):$($Minute):$($Second).$MilliSecondFull"
|
|
|
|
# Create Powershell custom object and add it to list
|
|
$LogEntryList.add([PSCustomObject]@{
|
|
'Index' = $null;
|
|
'LogEntryDateTime' = $LogEntryDateTime;
|
|
'FileName' = $LogFileName;
|
|
'Line' = $LineNumber;
|
|
'DateTime' = $DateTimeToLogFile;
|
|
'Multiline' = '';
|
|
'ProcessRunTime' = $ProcessRunTime;
|
|
'Message' = $LogMessage;
|
|
'Component' = $Component;
|
|
'Context' = $Context;
|
|
'Type' = $Type;
|
|
'Thread' = $Thread;
|
|
'File' = $File
|
|
})
|
|
|
|
} elseif ($CurrentLogEntry -Match $FirstLineOfMultiLineLogRegex) {
|
|
# This is start of multiline log entry
|
|
# Single line regex did not get results so we are dealing multiline case separately here
|
|
|
|
#Write-Host "DEBUG Start of multiline regex: $CurrentLogEntry" -ForegroundColor Yellow
|
|
|
|
$MultilineLogEntryStartFound=$True
|
|
|
|
# Regex found match
|
|
$LogMessage = $Matches[1].Trim()
|
|
|
|
$LogEntryDateTime = ''
|
|
$DateTimeToLogFile = ''
|
|
$Component = ''
|
|
$Context = ''
|
|
$Type = ''
|
|
$Thread = ''
|
|
$File = ''
|
|
|
|
# Create Powershell custom object and add it to list
|
|
$LogEntryList.add([PSCustomObject]@{
|
|
'Index' = $null;
|
|
'LogEntryDateTime' = $LogEntryDateTime;
|
|
'FileName' = $LogFileName;
|
|
'Line' = $LineNumber;
|
|
'DateTime' = $DateTimeToLogFile;
|
|
'Multiline' = '';
|
|
'ProcessRunTime' = '';
|
|
'Message' = $LogMessage;
|
|
'Component' = $Component;
|
|
'Context' = $Context;
|
|
'Type' = $Type;
|
|
'Thread' = $Thread;
|
|
'File' = $File
|
|
})
|
|
|
|
$MultilineLogEntryStartsArrayIndex = $LogEntryList.Count - 1
|
|
|
|
} elseif ($CurrentLogEntry -Match $LastLineOfMultiLineLogRegex) {
|
|
# This is end of multiline log entry
|
|
# Single line regex did not get results so we are dealing multiline case separately here
|
|
|
|
# Regex found match
|
|
$LogMessage = $Matches[1].Trim()
|
|
|
|
$Hour = $Matches[2]
|
|
$Minute = $Matches[3]
|
|
$Second = $Matches[4]
|
|
|
|
$MilliSecondFull = $Matches[5]
|
|
# Cut milliseconds to 0-999
|
|
# Time unit is so small that we don't even bother to round the value
|
|
$MilliSecond = $MilliSecondFull.Substring(0,3)
|
|
|
|
$Month = $Matches[6]
|
|
$Day = $Matches[7]
|
|
$Year = $Matches[8]
|
|
|
|
$Component = $Matches[9]
|
|
$Context = $Matches[10]
|
|
$Type = $Matches[11]
|
|
$Thread = $Matches[12]
|
|
$File = $Matches[13]
|
|
|
|
$Param = @{
|
|
Hour=$Hour
|
|
Minute=$Minute
|
|
Second=$Second
|
|
MilliSecond=$MilliSecond
|
|
Year=$Year
|
|
Month=$Month
|
|
Day=$Day
|
|
}
|
|
|
|
$LogEntryDateTime = Get-Date @Param
|
|
#Write-Host "DEBUG `$LogEntryDateTime: $LogEntryDateTime" -ForegroundColor Yellow
|
|
|
|
# This works for humans but does not sort
|
|
#$DateTimeToLogFile = "$($Hour):$($Minute):$($Second).$MilliSecondFull $Day/$Month/$Year"
|
|
|
|
# This does sorting right way
|
|
$DateTimeToLogFile = "$Year-$Month-$Day $($Hour):$($Minute):$($Second).$MilliSecondFull"
|
|
|
|
if($MultilineLogEntryStartFound) {
|
|
$Multiline = '---->'
|
|
} else {
|
|
$Multiline = ''
|
|
}
|
|
|
|
# Create Powershell custom object and add it to list
|
|
$LogEntryList.add([PSCustomObject]@{
|
|
'Index' = $null;
|
|
'LogEntryDateTime' = $LogEntryDateTime;
|
|
'FileName' = $LogFileName;
|
|
'Line' = $LineNumber;
|
|
'DateTime' = $DateTimeToLogFile;
|
|
'Multiline' = $Multiline;
|
|
'ProcessRunTime' = '';
|
|
'Message' = $LogMessage;
|
|
'Component' = $Component;
|
|
'Context' = $Context;
|
|
'Type' = $Type;
|
|
'Thread' = $Thread;
|
|
'File' = $File
|
|
})
|
|
|
|
# Process previous lines and add DateTime, Multiline, Component, Context, Type, Thread and File
|
|
# There can be corrupted log entries so there can be multiline entries without proper multiline start log entry
|
|
# So we will add information until we find log entry which has DateTime value
|
|
|
|
$MultilineLogEntryEndsArrayIndex = $LogEntryList.Count - 1
|
|
|
|
# Add data to starting multiline log entry object
|
|
# DateTime, Component, Context, Type, Thread, File information
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].LogEntryDateTime = $LogEntryDateTime
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].DateTime = $DateTimeToLogFile
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].Multiline = '<----'
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].Component = $Component
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].Context = $Context
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].Type = $Type
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].Thread = $Thread
|
|
$LogEntryList[$MultilineLogEntryStartsArrayIndex].File = $File
|
|
|
|
# Add data to multiline entries which are not end or start entries
|
|
#if($MultilineLogEntryEndsArrayIndex - $MultilineLogEntryStartsArrayIndex -gt 1) {
|
|
# Multiline entry is more than 2 lines
|
|
|
|
# Add DateTime, Component, Context, Type, Thread, File information
|
|
# to Multiline log entry from last to first
|
|
#for($i=$MultilineLogEntryEndsArrayIndex - 1; $i -gt $MultilineLogEntryStartsArrayIndex ; $i--) {
|
|
|
|
# Add information to previous log entries if they don't have DateTime-value
|
|
# Stop when we find DateTime-value
|
|
$i = $MultilineLogEntryEndsArrayIndex -1
|
|
While($LogEntryList[$i].DateTime -eq $null) {
|
|
#Write-Host "DEBUG: Multiline add data: `$MultilineLogEntryStartsArrayIndex=$MultilineLogEntryStartsArrayIndex `$MultilineLogEntryEndsArrayIndex=$MultilineLogEntryEndsArrayIndex `$i=$i `$Message=$($LogEntryList[$i].Message)"
|
|
|
|
$LogEntryList[$i].LogEntryDateTime = $LogEntryDateTime
|
|
$LogEntryList[$i].DateTime = $DateTimeToLogFile
|
|
$LogEntryList[$i].Multiline = ' --- '
|
|
$LogEntryList[$i].Component = $Component
|
|
$LogEntryList[$i].Context = $Context
|
|
$LogEntryList[$i].Type = $Type
|
|
$LogEntryList[$i].Thread = $Thread
|
|
$LogEntryList[$i].File = $File
|
|
|
|
$i--
|
|
}
|
|
|
|
} else {
|
|
# We didn't catch log entry with our regex
|
|
# This should be multiline log entry but not first or last line in that log entry
|
|
# This can also be some line that should be matched with (other) regex
|
|
|
|
#Write-Host "DEBUG: $CurrentLogEntry" -ForegroundColor Yellow
|
|
|
|
# Create Powershell custom object and add it to list
|
|
$LogEntryList.add([PSCustomObject]@{
|
|
'Index' = $null;
|
|
'LogEntryDateTime' = $null;
|
|
'FileName' = $LogFileName;
|
|
'Line' = $LineNumber;
|
|
'DateTime' = $null;
|
|
'Multiline' = '';
|
|
'ProcessRunTime' = $null;
|
|
'Message' = $CurrentLogEntry;
|
|
'Component' = '';
|
|
'Context' = '';
|
|
'Type' = '';
|
|
'Thread' = '';
|
|
'File' = ''
|
|
})
|
|
}
|
|
|
|
$LineNumber++
|
|
|
|
} # Foreach end single log file line by line foreach
|
|
|
|
} # Foreach end all log files
|
|
Write-Host ""
|
|
|
|
# Check if there was log entries/lines which were not detected
|
|
$DroppedLines = $LogEntryList | Where-Object DateTime -eq $null
|
|
if($DroppedLines) {
|
|
Write-Host "These lines were detected not to have right syntax so they were dropped" -ForegroundColor Yellow
|
|
Foreach($Line in $DroppedLines) {
|
|
Write-Host "$($Line.FileName) Line $($Line.Line) $($Line.Message)"
|
|
|
|
# FIXME Copy timestamp from previous log entry
|
|
|
|
}
|
|
Write-Host "`n"
|
|
}
|
|
|
|
|
|
# Take last $LogEntryDateTime and add 1 seconds to it
|
|
$ReloadDate = (Get-Date).AddHours(1)
|
|
$ReloadDateTime = "$($ReloadDate.Year)-$($ReloadDate.Month)-$($ReloadDate.Day) $($ReloadDate.TimeOfDay)"
|
|
|
|
# Add last entry which will reload log file if selected
|
|
$LogEntryList.add([PSCustomObject]@{
|
|
'Index' = $null;
|
|
'LogEntryDateTime' = $ReloadDate;
|
|
'FileName' = '';
|
|
'Line' = '';
|
|
'DateTime' = $ReloadDateTime;
|
|
'Multiline' = '';
|
|
'ProcessRunTime' = '';
|
|
'Message' = 'RELOAD LOG FILE(S). Select this line and OK button from right botton corner';
|
|
'Component' = '';
|
|
'Context' = '';
|
|
'Type' = '';
|
|
'Thread' = '';
|
|
'File' = ''
|
|
})
|
|
|
|
|
|
|
|
# Check if First x minutes/hours was selected
|
|
if($LogEndDateTimeObject -eq 'Analyzing DateTime later') {
|
|
# Sort log entries by DateTime and secondary by Line number
|
|
# Find first log entry with LogEntryDateTime value
|
|
# There is some rare case there could be log entry with empty value
|
|
#Foreach ($LogEntryObject in $LogEntryList | Sort-Object -Property DateTime, Line) {
|
|
Foreach ($LogEntryObject in $LogEntryList) {
|
|
Try {
|
|
$FirstLogEntryTimeDate = Get-Date $LogEntryObject.LogEntryDateTime
|
|
Break
|
|
} catch {
|
|
|
|
}
|
|
}
|
|
|
|
# And figure out LogEndDateTime based on first log entry timestamp
|
|
if($SelectedTimeFrameObject.'Log Start and End time' -eq 'First 30 minutes') {
|
|
$LogEndDateTimeObject = ($FirstLogEntryTimeDate).AddMinutes(30)
|
|
}
|
|
|
|
if($SelectedTimeFrameObject.'Log Start and End time' -eq 'First 1 hour') {
|
|
$LogEndDateTimeObject = ($FirstLogEntryTimeDate).AddHours(1)
|
|
}
|
|
|
|
if($SelectedTimeFrameObject.'Log Start and End time' -eq 'First 2 hours') {
|
|
$LogEndDateTimeObject = ($FirstLogEntryTimeDate).AddHours(2)
|
|
}
|
|
|
|
if($SelectedTimeFrameObject.'Log Start and End time' -eq 'First 6 hours') {
|
|
$LogEndDateTimeObject = ($FirstLogEntryTimeDate).AddHours(6)
|
|
}
|
|
|
|
Write-Host "Log entries End time: $LogEndDateTimeObject`n"
|
|
}
|
|
|
|
Write-Host "Creating report"
|
|
Write-Host "Log entries Start time: $LogStartDateTimeObject"
|
|
Write-Host "Log entries End time: $LogEndDateTimeObject`n"
|
|
|
|
|
|
# Filter and Sort objects
|
|
# We need to sort with DateTime AND Line
|
|
# because there can are multiple log entries with same timestamp
|
|
#$LogEntryList = $LogEntryList | Sort-Object -Property DateTime, Line
|
|
$LogEntryList = $LogEntryList | Where-Object { ($_.LogEntryDateTime -gt $LogStartDateTimeObject) -and ($_.LogEntryDateTime -lt $LogEndDateTimeObject) } | Sort-Object -Property DateTime, Line
|
|
|
|
# Add Index property value
|
|
# This is used for sorting when doing analyzing in Out-GridView
|
|
$index = 1
|
|
Foreach($LogEntry in $LogEntryList) {
|
|
|
|
# Use this to find out if we did catch all lines
|
|
# Look for the first object index and line numbers that if they match
|
|
#Write-host "$index $($LogEntry.Line) $($LogEntry.Message)"
|
|
|
|
# DateTime is empty if there was corrupted log entries
|
|
# We should set last known timestamp to those but this is just in case we still missed something
|
|
if($LogEntry.DateTime) {
|
|
$LogEntry.Index = $index
|
|
$index++
|
|
}
|
|
}
|
|
|
|
|
|
# region analyze log entries
|
|
##########################
|
|
# Analyze log entries
|
|
#
|
|
# Try to find end and start log entries for Powershell, Win32App and WinGetApp installations
|
|
# We go backwards because if there is process end log entry
|
|
# then there has to be start log entry (going upwards) for the same process (same thread in this case)
|
|
#
|
|
# Start process could be "orphan" if process is stopped without end log entry
|
|
# so it is hard to detect these cases (going from up to down)
|
|
|
|
$DetectedInEspPhase=$False
|
|
$skipDeviceStatusPage='NotFoundYet'
|
|
$skipUserStatusPage='NotFoundYet'
|
|
$skipDeviceStatusPageIndex=$null
|
|
$skipUserStatusPageIndex=$null
|
|
$NotInEspInSessionFound=$false
|
|
$UserESPFinished=$false
|
|
$UserESPFinishedNotFoundWarningMessageShown=$false
|
|
|
|
$PreviousLoggedOnUser='NotInitializedYet'
|
|
$LoggedOnUserDefaultUser0Shown=$false
|
|
|
|
$PowershellScriptStartLogEntryFound=$false
|
|
$PowershellScriptStartLogEntryIndex=$null
|
|
|
|
# Array
|
|
$PowershellScriptErrorLogs = @()
|
|
|
|
|
|
$i = 0
|
|
Do {
|
|
# Clear variables in every loop round
|
|
$LoggedOnUser=$false
|
|
|
|
#Write-Host "`$i=$i"
|
|
#Write-Host "$($LogEntryList[$i].Message)"
|
|
|
|
|
|
# region Find Username and UserSID
|
|
if($LogEntryList[$i].Message -Like '`[Win32App`] Expected usersid for session *') {
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[Win32App\] Expected usersid for session (.*) with name (.*) is (.*)$') {
|
|
$UserSession = $Matches[1]
|
|
$UserName = $Matches[2]
|
|
$UserSID = $Matches[3]
|
|
|
|
#Write-Host "DEBUG: $($LogEntryList[$i].Message)"
|
|
|
|
# Check if we have UserSID object in Hashtable UserId object
|
|
if($IdHashtable.ContainsKey($UserSId)) {
|
|
$IdHashtable[$UserSId].userSID = $UserSID
|
|
$IdHashtable[$UserSId].name = $UserName
|
|
$IdHashtable[$UserSId].displayName = $UserName
|
|
$IdHashtable[$UserSId].session = $UserSession
|
|
|
|
$userId = $IdHashtable[$UserSId].userId
|
|
if($userId) {
|
|
# Check if we have UserID object in Hashtable
|
|
if($IdHashtable.ContainsKey($UserId)) {
|
|
# Add Name-value. We don't even check existing value
|
|
# because we are already here.
|
|
$IdHashtable[$UserId].userSID = $UserSID
|
|
$IdHashtable[$UserId].name = $UserName
|
|
$IdHashtable[$UserId].displayName = $UserName
|
|
$IdHashtable[$UserId].session = $UserSession
|
|
}
|
|
}
|
|
} else {
|
|
# UserSID object does not exist in Hashtable
|
|
# Create object which we will add to Hashtable
|
|
$UserSIDCustomObjectProperties = @{
|
|
id = $null
|
|
userId = $null
|
|
userSID = $UserSID
|
|
name = $UserName
|
|
displayName = $UserName
|
|
session = $UserSession
|
|
}
|
|
$UserSIDCustomObject = New-Object -TypeName PSObject -Prop $UserSIDCustomObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($UserSId , $UserSIDCustomObject)
|
|
}
|
|
|
|
#Write-Host "DEBUG: We should have name Property now`n$($IdHashtable | ConvertTo-Json)"
|
|
}
|
|
|
|
# Cleanup variables
|
|
$UserSession = $null
|
|
$UserName = $null
|
|
$UserSID= $null
|
|
$UserSIDCustomObject = $null
|
|
}
|
|
# endregion Find UserName and UserSID
|
|
|
|
|
|
<# # This code works, but there was log entries with mismatched UserID and UserSID (from different users)
|
|
# so took another approach to map UserID, UserSID and UserName
|
|
|
|
|
|
# region Find UserId and UserSID
|
|
if($LogEntryList[$i].Message -Like '`[Win32App`] EspPreparation starts for userId: *') {
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[Win32App\] EspPreparation starts for userId: (.*) userSID: (.*)$') {
|
|
$UserId = $Matches[1]
|
|
$UserSID = $Matches[2]
|
|
|
|
Write-Host "DEBUG: $($LogEntryList[$i].Message)"
|
|
|
|
# Add information to $IdHashtable UserId key
|
|
if($IdHashtable.ContainsKey($UserId)) {
|
|
|
|
# Add UserID and UserSID -values. We don't even check existing value
|
|
# because we are already here.
|
|
$IdHashtable[$UserId].Id = $UserId
|
|
$IdHashtable[$UserId].userId = $UserId
|
|
$IdHashtable[$UserId].userSID = $UserSID
|
|
|
|
} else {
|
|
# Hashtable does NOT have UserId object
|
|
|
|
if($IdHashtable.ContainsKey($UserSId)) {
|
|
$UserName = $IdHashtable[$UserSId].name
|
|
$Session = $IdHashtable[$UserSId].session
|
|
} else {
|
|
$UserName = $UserId
|
|
$Session = $null
|
|
}
|
|
|
|
# Create object which we will add to Hashtable
|
|
$UserIdCustomObjectProperties = @{
|
|
id = $UserId
|
|
userId = $UserId
|
|
userSID = $UserSID
|
|
name = $UserName
|
|
session = $Session
|
|
}
|
|
|
|
$UserIdCustomObject = New-Object -TypeName PSObject -Prop $UserIdCustomObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($UserId , $UserIdCustomObject)
|
|
}
|
|
|
|
# Add information to $IdHashtable UserSID key
|
|
if($IdHashtable.ContainsKey($UserSID)) {
|
|
|
|
if($IdHashtable.ContainsKey($UserId)) {
|
|
$UserName = $IdHashtable[$UserId].name
|
|
$Session = $IdHashtable[$UserId].session
|
|
} else {
|
|
$UserName = $UserId
|
|
$Session = $null
|
|
}
|
|
|
|
# Add UserId-value. We don't even check existing value
|
|
# because we are already here.
|
|
$IdHashtable[$UserSId].id = $UserId
|
|
$IdHashtable[$UserSId].userId = $UserId
|
|
$IdHashtable[$UserSId].userSId = $UserSId
|
|
$IdHashtable[$UserSId].name = $UserName
|
|
$IdHashtable[$UserSId].session = $Session
|
|
|
|
} else {
|
|
# UserSID object does not exist in Hashtable
|
|
# Create object which we will add to Hashtable
|
|
$UserSIDCustomObjectProperties = @{
|
|
id = $UserId
|
|
userId = $UserId
|
|
userSID = $UserSID
|
|
name = $UserName
|
|
session = $Session
|
|
}
|
|
$UserSIDCustomObject = New-Object -TypeName PSObject -Prop $UserSIDCustomObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($UserSId , $UserSIDCustomObject)
|
|
}
|
|
}
|
|
|
|
Write-Host "DEBUG: We should have UserID and UserSID`n$($IdHashtable | ConvertTo-Json)"
|
|
|
|
# Cleanup variables
|
|
$UserIdCustomObject = $null
|
|
$UserSIDCustomObject = $null
|
|
$UserId = $null
|
|
$UserSID = $null
|
|
|
|
}
|
|
# endregion Find UserId and UserSID
|
|
#>
|
|
|
|
# region EMS Agent Started/Stopped/received shutdown signal
|
|
# Excluded -or ($LogEntryList[$i].Message -eq 'EMS Agent received shutdown signal')
|
|
if(($LogEntryList[$i].Message -eq 'EMS Agent Started') -or ($LogEntryList[$i].Message -eq 'EMS Agent Stopped')) {
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "$($LogEntryList[$i].Message)"
|
|
#Write-Host "$($LogEntryList[$i].DateTime) $($LogEntryList[$i].Message) ($($LogEntryList[$i].FileName) line $($LogEntryList[$i].Line))"
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status 'Info' -detail $LogEntryList[$i].Message -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
# endregion EMS Agent Started/Stopped/received shutdown signal
|
|
|
|
# region Find ESP SkipESPStatusPage(s)
|
|
if($skipDeviceStatusPage -eq 'NotFoundYet') {
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[Win32App\] CheckDeviceOnlyFromFirstSyncReg set skipDeviceStatusPage: (.*)$') {
|
|
$skipDeviceStatusPage = $Matches[1]
|
|
$LogEntryList[$i].ProcessRunTime = "Skip Device ESP: $skipDeviceStatusPage"
|
|
|
|
$skipDeviceStatusPageIndex=$i
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
|
|
# region Find ESP SkipESPStatusPage(s)
|
|
if($skipUserStatusPage -eq 'NotFoundYet') {
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[Win32App\] CheckDeviceOnlyFromFirstSyncReg set skipUserStatusPage: (.*)$') {
|
|
$skipUserStatusPage = $Matches[1]
|
|
$LogEntryList[$i].ProcessRunTime = "Skip User ESP : $skipUserStatusPage"
|
|
|
|
$skipUserStatusPageIndex=$i
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion Find ESP SkipESPStatusPage(s)
|
|
|
|
|
|
# region Find ESP (Enrollment Status Page) phases
|
|
if(-not $DetectedInEspPhase) {
|
|
if($LogEntryList[$i].Message -eq '[Win32App] In EspPhase: DeviceSetup') {
|
|
$DetectedInEspPhase = $True
|
|
|
|
# This is here because these lines exists later after ESP
|
|
# So only showing this information if we are in ESP
|
|
if($skipDeviceStatusPage -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$skipDeviceStatusPageIndex].DateTime -status "Info" -detail "Skip Device ESP: $skipDeviceStatusPage" -logEntry "Line $($LogEntryList[$skipDeviceStatusPageIndex].Line)" -color 'Yellow'
|
|
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$skipDeviceStatusPageIndex].DateTime -status "Info" -detail "Skip Device ESP: $skipDeviceStatusPage" -logEntry "Line $($LogEntryList[$skipDeviceStatusPageIndex].Line)"
|
|
}
|
|
|
|
# This is here because these lines exists later after ESP
|
|
# So only showing this information if we are in ESP
|
|
if($skipUserStatusPage -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$skipUserStatusPageIndex].DateTime -status "Info" -detail "Skip User ESP: $skipUserStatusPage" -logEntry "Line $($LogEntryList[$skipUserStatusPageIndex].Line)" -color 'Yellow'
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$skipUserStatusPageIndex].DateTime -status "Info" -detail "Skip User ESP: $skipUserStatusPage" -logEntry "Line $($LogEntryList[$skipUserStatusPageIndex].Line)"
|
|
}
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "[Win32App] In Device ESP phase detected"
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Info" -detail "######################## Device ESP phase ########################" -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion Find ESP (Enrollment Status Page) phases
|
|
|
|
# region User ESP finished
|
|
if(-not $UserESPFinished) {
|
|
if($LogEntryList[$i].Message -like '`[Win32App`] In EspPhase: AccountSetup. All Win32App has been added. Set SidecarReady: True*') {
|
|
$UserESPFinished = $True
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "User ESP finished successfully"
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Info" -detail "######################## User ESP finished ########################" -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion User ESP finished
|
|
|
|
|
|
# region NotInEsp in session
|
|
if(-not $NotInEspInSessionFound) {
|
|
if($LogEntryList[$i].Message -eq '[Win32App] The EspPhase: NotInEsp in session') {
|
|
$NotInEspInSessionFound = $True
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "[Win32App] The EspPhase: NotInEsp in session"
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Info" -detail "######################## Not in ESP detected ########################" -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion NotInEsp in session
|
|
|
|
# region NotInESP found but did not find User ESP Finished
|
|
# Show warning on this case. Did User ESP fail ???
|
|
if((-not $UserESPFinishedNotFoundWarningMessageShown) -and ($DetectedInEspPhase) -and ((-not $UserESPFinished) -and ($NotInEspInSessionFound))) {
|
|
$UserESPFinishedNotFoundWarningMessageShown=$True
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Warning" -detail "Did not detect User ESP finished. This is usually falsepositive warning." -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Yellow'
|
|
|
|
}
|
|
# endregion NotInESP found but did not find User ESP Finished
|
|
|
|
# region Find Logged on user
|
|
if($LogEntryList[$i].Message -Like 'After impersonation: *') {
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^After impersonation: (.*)$') {
|
|
$LoggedOnUser = $Matches[1]
|
|
|
|
#Write-Host "DEBUG: `$FirstLoggedOnUser`=$FirstLoggedOnUser"
|
|
# Skip if defaultuser0 found (we are in device ESP phase still)
|
|
if(-not $LoggedOnUserDefaultUser0Shown) {
|
|
if($LoggedOnUser -like '*\defaultuser0') {
|
|
$LoggedOnUserDefaultUser0Shown = $True
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "User Logon" -detail "Logged on user : $LoggedOnUser" -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Yellow'
|
|
|
|
# Set LoggedOnUser information to ProcessRuntime property
|
|
$LogEntryList[$i].ProcessRunTime = "Logged on user: $LoggedOnUser"
|
|
|
|
$i++
|
|
Continue
|
|
}
|
|
} elseif (($LoggedOnUser -notlike '*\defaultuser0') -and ($LoggedOnUser -ne $PreviousLoggedOnUser)) {
|
|
$PreviousLoggedOnUser=$LoggedOnUser
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "User Logon" -detail "Logged on user : $LoggedOnUser" -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Logged on user: $LoggedOnUser"
|
|
|
|
}
|
|
|
|
|
|
# Not sure if we want to run this everytime or not
|
|
# Now running it every time we find this log entry
|
|
if($True) {
|
|
# Try to find user session from previous log entry
|
|
if($LogEntryList[$i-1].Message -Match '^Starting impersonation, session id = (.*)$') {
|
|
$ImpersonationSessionId = $Matches[1]
|
|
|
|
#Write-Host "Found ImpersonationSessionId $ImpersonationSessionId"
|
|
|
|
# Try to find UserId from either of following lines
|
|
# [PowerShell] Processing user session 3, user id = cdc3177c-df70-45e3-a469-4ef1be2b8345
|
|
# [PowerShell] Get 3 policies for user cdc3177c-df70-45e3-a469-4ef1be2b8345 in session 3
|
|
$b = $i + 1
|
|
While(($LogEntryList[$b].Message -notlike 'After impersonation: *') -and ($b -lt $LogEntryList.Count)) {
|
|
#Write-Host "DEBUG: Processing line: $($LogEntryList[$b].Message)"
|
|
|
|
if(($LogEntryList[$i].Thread -eq $LogEntryList[$b].Thread) -and (($LogEntryList[$b].Message -Match '^\[PowerShell\] Processing user session (.*), user id = (.*)$') -or ($LogEntryList[$b].Message -Match '^\[PowerShell\] Get (.*) policies for user (.*) in session (.*)$') -or ($LogEntryList[$b].Message -Match '^\[Win32App\] Got (.*) Win32App(s) for user (.*) in session (.*)$'))) {
|
|
#Write-Host "DEBUG: Found matching line: $($LogEntryList[$b].Message)"
|
|
|
|
# Save to temporary variables because next -Match will overwrite existing $Matches -variable
|
|
$Matches1 = $Matches[1]
|
|
$Matches2 = $Matches[2]
|
|
|
|
# Either one is GUID so we need to test which $Matches variable is valid GUID and which is sessionId
|
|
if($Matches1 -Match '[[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}') {
|
|
$UserId = $Matches1
|
|
$UserSession = $Matches2
|
|
} elseif ($Matches2 -Match '[[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}') {
|
|
$UserSession = $Matches1
|
|
$UserId = $Matches2
|
|
} else {
|
|
# Something strange happened and we did not get GUID
|
|
# We should not be here
|
|
$UserSession = $null
|
|
$UserId = $null
|
|
}
|
|
|
|
#Write-Host "`$UserId=$UserId"
|
|
#Write-Host "`$UserSession=$UserSession"
|
|
#Write-Host "`$ImpersonationSessionId=$ImpersonationSessionId"
|
|
|
|
if($ImpersonationSessionId -eq $UserSession) {
|
|
# We are quite sure we have right id for UserName
|
|
|
|
# UserSID object does not exist in Hashtable
|
|
# Create object which we will add to Hashtable
|
|
if($IdHashtable.ContainsKey($UserId)) {
|
|
$IdHashtable[$UserId].id = $UserId
|
|
$IdHashtable[$UserId].userId = $UserId
|
|
$IdHashtable[$UserId].name = $LoggedOnUser
|
|
$IdHashtable[$UserId].displayName = $LoggedOnUser
|
|
$IdHashtable[$UserId].session = $UserSession
|
|
|
|
} else {
|
|
$UserIdCustomObjectProperties = @{
|
|
id = $UserId
|
|
userId = $UserId
|
|
userSID = $null
|
|
name = $LoggedOnUser
|
|
displayName = $LoggedOnUser
|
|
session = $UserSession
|
|
}
|
|
$UserIdCustomObject = New-Object -TypeName PSObject -Prop $UserIdCustomObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($UserId , $UserIdCustomObject)
|
|
}
|
|
|
|
#Write-Host "DEBUG: We should have UserID and Username`n$($IdHashtable | ConvertTo-Json)"
|
|
}
|
|
|
|
# Break out from While-loop
|
|
Break
|
|
}
|
|
$b++
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion Find Logged on user
|
|
|
|
|
|
# region Find Powershell Script Start log entry
|
|
if($LogEntryList[$i].Message -like '`[PowerShell`] Processing policy with id = *') {
|
|
if($LogEntryList[$i].Message -Match '^\[PowerShell\] Processing policy with id = (.*) for user (.*)$') {
|
|
$PowershellScriptPolicyIdStart = $Matches[1]
|
|
$PowershellScriptForUserIdStart = $Matches[2]
|
|
|
|
$PowershellScriptStartLogEntryIndex=$i
|
|
|
|
# This variables is probably not needed because
|
|
# we can detect end and start log entries separately
|
|
$PowershellScriptStartLogEntryFound=$True
|
|
|
|
if($PowershellScriptForUserIdStart -eq '00000000-0000-0000-0000-000000000000') {
|
|
$PowershellScriptForUserNameStart = 'System'
|
|
} else {
|
|
|
|
# Get real name if found from Hashtable
|
|
if($IdHashtable.ContainsKey($PowershellScriptForUserIdStart)) {
|
|
$Name = $IdHashtable[$PowershellScriptForUserIdStart].name
|
|
if($Name) {
|
|
$PowershellScriptForUserNameStart = $IdHashtable[$PowershellScriptForUserIdStart].name
|
|
} else {
|
|
$PowershellScriptForUserNameStart = $PowershellScriptForUserIdStart
|
|
}
|
|
} else {
|
|
$PowershellScriptForUserNameStart = $PowershellScriptForUserIdStart
|
|
}
|
|
}
|
|
|
|
# Get Powershell script name if parameter -Online was specified
|
|
if($IdHashtable.ContainsKey($PowershellScriptPolicyIdStart)) {
|
|
$PowershellScriptPolicyNameStart = $IdHashtable[$PowershellScriptPolicyIdStart].name
|
|
} else {
|
|
$PowershellScriptPolicyNameStart = $PowershellScriptPolicyIdStart
|
|
}
|
|
|
|
if($ShowAllTimelineEntries) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Process / Powershell" -detail "Powershell script : $PowershellScriptPolicyNameStart for user $PowershellScriptForUserNameStart" -logEntry "Line $($LogEntryList[$i].Line)"
|
|
}
|
|
|
|
# Set Powershell Script Start information to ProcessRuntime property
|
|
$LogEntryList[$i].ProcessRunTime = "Processing Powershell script ($PowershellScriptPolicyNameStart) for user $PowershellScriptForUserNameStart"
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion Find Powershell Script Start log entry
|
|
|
|
# region Find Powershell Script End log entry
|
|
if($LogEntryList[$i].Message -like '`[PowerShell`] User Id = *') {
|
|
if($LogEntryList[$i].Message -Match '^\[PowerShell\] User Id = (.*), Policy id = (.*), policy result = (.*)$') {
|
|
$PowershellScriptForUserIdEndProcess = $Matches[1]
|
|
$PowershellScriptPolicyIdEndProcess = $Matches[2]
|
|
$PowershellScriptPolicyResultEndProcess = $Matches[3]
|
|
|
|
# This variables is probably not needed because
|
|
# we can detect end and start log entries separately
|
|
$PowershellScriptEndLogEntryFound=$True
|
|
|
|
if($PowershellScriptForUserIdEndProcess -eq '00000000-0000-0000-0000-000000000000') {
|
|
$PowershellScriptForUserNameEndProcess = 'System'
|
|
} else {
|
|
$PowershellScriptForUserNameEndProcess = $PowershellScriptForUserIdEndProcess
|
|
|
|
# Get real name if found from Hashtable
|
|
if($IdHashtable.ContainsKey($PowershellScriptForUserIdEndProcess)) {
|
|
$Name = $IdHashtable[$PowershellScriptForUserIdEndProcess].name
|
|
if($Name) {
|
|
$PowershellScriptForUserNameEndProcess = $IdHashtable[$PowershellScriptForUserIdEndProcess].name
|
|
}
|
|
}
|
|
}
|
|
|
|
# Get Powershell script name if parameter -Online was specified
|
|
if($IdHashtable.ContainsKey($PowershellScriptPolicyIdEndProcess)) {
|
|
$PowershellScriptPolicyNameEnd = $IdHashtable[$PowershellScriptPolicyIdEndProcess].name
|
|
} else {
|
|
$PowershellScriptPolicyNameEnd = $PowershellScriptPolicyIdEndProcess
|
|
}
|
|
|
|
# Get Process runtime
|
|
if($PowershellScriptStartLogEntryIndex) {
|
|
$PowershellScriptStartTime = $LogEntryList[$PowershellScriptStartLogEntryIndex].LogEntryDateTime
|
|
$PowershellScriptEndTime = $LogEntryList[$i].LogEntryDateTime
|
|
|
|
$PowershellScriptRunTime = New-Timespan $PowershellScriptStartTime $PowershellScriptEndTime
|
|
$PowershellScriptRunTimeTotalSeconds = $PowershellScriptRunTime.TotalSeconds
|
|
|
|
|
|
} else {
|
|
$PowershellScriptRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
if($PowershellScriptPolicyResultEndProcess -eq 'Success') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / Powershell" -detail "Powershell script : $PowershellScriptPolicyNameEnd for user $PowershellScriptForUserNameEndProcess" -Seconds $PowershellScriptRunTimeTotalSeconds -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Green'
|
|
|
|
# Check if Powershell script runtime is longer than configured (default 180 seconds)
|
|
if($PowershellScriptRunTimeTotalSeconds -gt $LongRunningPowershellNotifyThreshold) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Warning" -detail " Long Powershell script runtime found" -Seconds $PowershellScriptRunTimeTotalSeconds -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Yellow'
|
|
}
|
|
|
|
# Set Powershell Script End information to ProcessRuntime property
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded Powershell script ($PowershellScriptPolicyNameEnd) for user $PowershellScriptForUserNameEndProcess (Runtime: $PowershellScriptRunTimeTotalSeconds seconds)"
|
|
|
|
} elseif ($PowershellScriptPolicyResultEndProcess -eq 'Failed') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / Powershell" -detail "Powershell script : $PowershellScriptPolicyNameEnd for user $PowershellScriptForUserNameEndProcess" -Seconds $PowershellScriptRunTimeTotalSeconds -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Red'
|
|
|
|
# Set Powershell Script End information to ProcessRuntime property
|
|
$LogEntryList[$i].ProcessRunTime = "Failed Powershell script ($PowershellScriptPolicyNameEnd) for user $PowershellScriptForUserNameEndProcess (Runtime: $PowershellScriptRunTimeTotalSeconds seconds)"
|
|
|
|
# Check if there is error message for failed Powershell script
|
|
|
|
# Check if next log entry exists
|
|
if($i + 1 -le $LogEntryList.Count - 1) {
|
|
if($LogEntryList[$i+1].Message -Match '^\[PowerShell\] Fail, the details are (.*)$') {
|
|
$PowershellFailJsonString = $Matches[1]
|
|
|
|
# Try to convert JsonString
|
|
Try {
|
|
$PowershellFailFromJson=$null
|
|
$PowershellFailFromJson = $PowershellFailJsonString | ConvertFrom-Json
|
|
if($ExecutionMsg=$PowershellFailFromJson.ExecutionMsg) {
|
|
if($ExecutionMsg) {
|
|
# This makes time line harder to read so moved Error message after timeline
|
|
#Write-Host "$($LogEntryList[$i+1].DateTime) Powershell script error message $ExecutionMsg ($($LogEntryList[$i].FileName) line $($LogEntryList[$i+1].Line))" -ForegroundColor Red
|
|
|
|
$PowershellScriptErrorLogs += "$($LogEntryList[$i].DateTime) End Powershell script ($PowershellScriptPolicyNameEnd) for user $PowershellScriptForUserNameEndProcess Success: $PowershellScriptPolicyResultEndProcess (Runtime: $PowershellScriptRunTimeTotalSeconds seconds) ($($LogEntryList[$i].FileName) line $($LogEntryList[$i].Line))"
|
|
$PowershellScriptErrorLogs += "$($LogEntryList[$i+1].DateTime) Powershell script error message ($($LogEntryList[$i].FileName) line $($LogEntryList[$i+1].Line))"
|
|
|
|
# Split Error Message by newline and enter error message line by line
|
|
$ExecutionMsg.Split("`n") | Foreach-Object {
|
|
$ErrorMessageLine = $_
|
|
#$PowershellScriptErrorLogs += "`t`t`t $ErrorMessageLine"
|
|
$PowershellScriptErrorLogs += "$ErrorMessageLine"
|
|
}
|
|
|
|
if($ShowErrorsInTimeline) {
|
|
$ExecutionMsg.Split("`n") | Foreach-Object {
|
|
$ErrorMessageLine = $_
|
|
#Write-Host "`t`t`t $ErrorMessageLine" -ForegroundColor Red
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "ErrorLog" -detail "$ErrorMessageLine" -logEntry "$($LogEntryList[$i].FileName) line $($LogEntryList[$i].Line)" -Color 'Red'
|
|
}
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Info" -detail "Errors found. Use Parameter -ShowErrorsInTimeline to show errors on Timeline" -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Yellow'
|
|
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
# Did not find Powershell error message
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Script ended" -detail "Powershell script $PowershellScriptPolicyNameEnd for user $PowershellScriptForUserNameEndProcess ($PowershellScriptRunTimeTotalSeconds seconds)" -logEntry "Line $($LogEntryList[$i].Line)" -Color 'Yellow'
|
|
}
|
|
#Write-Host ""
|
|
|
|
$PowershellScriptStartLogEntryIndex=$null
|
|
|
|
# Continue to next line in Do-while -loop
|
|
$i++
|
|
Continue
|
|
}
|
|
}
|
|
# endregion Find Powershell Script End log entry
|
|
|
|
|
|
<#
|
|
# Remove me from Production
|
|
# region Find Powershell processess
|
|
# Try to find Powershell process End log entry
|
|
if($LogEntryList[$i].Message -like '*execution is done*') {
|
|
$ProcessStartTime = $null
|
|
$ProcessEndTime = $LogEntryList[$i].LogEntryDateTime
|
|
$Thread = $LogEntryList[$i].Thread
|
|
Write-Verbose "Found Powershell process Ending message (Line $($LogEntryList[$i].Line) Thread $Thread): $($LogEntryList[$i].Message)"
|
|
|
|
# Try to find Powershell process Start log entry (upwards)
|
|
$b = $i - 1
|
|
for($b = $i - 1; $b -ge 0; $b-- ) {
|
|
if(($LogEntryList[$b].Message -like '*process id =*') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
Write-Verbose "Found Powershell process Starting message (Line $($LogEntryList[$b].Line) Thread $($LogEntryList[$b].Thread)): $($LogEntryList[$b].Message)"
|
|
$ProcessStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
$ProcessRunTime = New-Timespan $ProcessStartTime $ProcessEndTime
|
|
$ProcessRunTimeTotalSeconds = $ProcessRunTime.TotalSeconds
|
|
$ProcessRunTimeText = "{0,-16} {1,8:n1}" -f "End Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Set RunTime to Powershell script Install Start log entry
|
|
$LogEntryList[$b].ProcessRunTime = "{0,-16} {1,8:n1}" -f "Start Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$i].ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
|
|
# Show warning for long running Powershell script
|
|
# if($ProcessRunTimeTotalSeconds -ge $LongRunningPowershellNotifyThreshold) {
|
|
# Write-Host "$($LogEntryList[$b].DateTime) Long running ($ProcessRunTimeTotalSeconds seconds) Powershell script ($LogFileName line $($LogEntryList[$b].Line)): $($LogEntryList[$b].Message)" -ForegroundColor Yellow
|
|
# }
|
|
|
|
|
|
# Break out from for-loop
|
|
Break
|
|
}
|
|
} # For-loop end
|
|
Write-Verbose ""
|
|
|
|
# Cleanup variables
|
|
$ProcessStartTime = $null
|
|
$ProcessEndTime = $null
|
|
$Thread = $null
|
|
$ProcessRunTimeTotalSeconds = $null
|
|
$ProcessRunTimeText = $null
|
|
}
|
|
# endregion Find Powershell processess
|
|
# Remove me from Production
|
|
#>
|
|
|
|
###############################################
|
|
|
|
# region Find Proactive Remediation Detect script type - Proactive Remediation or custom Compliance
|
|
if($LogEntryList[$i].Message -like '`[HS`] ProcessScript PolicyId:*') {
|
|
# Proactive Remediation script = policyType 6
|
|
# custom Compliance script = policyType 8
|
|
|
|
#Write-Host "Proactive Remediation Detect script type found" -ForegroundColor Yellow
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[HS\] ProcessScript PolicyId: (.*) PolicyType: (.*)$') {
|
|
$PolicyId = $Matches[1]
|
|
$PolicyType = $Matches[2]
|
|
|
|
# Check if PolicyId is in Hashtable
|
|
if($IdHashtable.ContainsKey($PolicyId)) {
|
|
# This can exist if -Online parameter was used
|
|
|
|
if($IdHashtable[$PolicyId] | Get-Member policyType){
|
|
# policyType Property exists in Hashtable object
|
|
$IdHashtable[$PolicyId].policyType = $PolicyType
|
|
} else {
|
|
# policyType Property does NOT exist in Hashtable object
|
|
# Add new Property
|
|
$IdHashtable[$PolicyId] | Add-Member -MemberType noteProperty -Name policyType -Value $PolicyType
|
|
}
|
|
|
|
} else {
|
|
# Add new Hashtable id entry
|
|
$PolicyObjectProperties = @{
|
|
id = $PolicyId
|
|
policyType = $PolicyType
|
|
}
|
|
$PolicyCustomObject = New-Object -TypeName PSObject -Prop $PolicyObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($PolicyId , $PolicyCustomObject)
|
|
}
|
|
}
|
|
}
|
|
# endregion Find Proactive Remediation Detect script type - Proactive Remediation or custom Compliance
|
|
|
|
# region Try to find Proactive Detection script run
|
|
if($LogEntryList[$i].Message -like '`[HS`] the pre-rem*diation detection script compliance result for*') {
|
|
#Write-Host "Proactive Remediation or custom Compliance Script Detect found" -ForegroundColor Yellow
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[HS\] the pre-remdiation detection script compliance result for (.*) is (.*)$') {
|
|
$PolicyIdEnd = $Matches[1]
|
|
$PreRemediationDetectResult = $Matches[2]
|
|
|
|
# Use this PolicyId on next phase -> Remediate
|
|
# Because there we don't have 2 log entries with PolicyId
|
|
$ProactiveRemediationPolicyId = $PolicyIdStart
|
|
|
|
$ProcessStartTime = $null
|
|
$DetectionLogEntryEndTime = $LogEntryList[$i].LogEntryDateTime
|
|
$Thread = $LogEntryList[$i].Thread
|
|
|
|
# Try to find displayName
|
|
if($IdHashtable.ContainsKey($PolicyIdEnd)) {
|
|
if($IdHashtable[$PolicyIdEnd].displayName) {
|
|
$ProactiveRemediationScriptName = $IdHashtable[$PolicyIdEnd].displayName
|
|
} else {
|
|
$ProactiveRemediationScriptName = $PolicyIdEnd
|
|
}
|
|
}
|
|
|
|
# Try to find policyType
|
|
if($IdHashtable.ContainsKey($PolicyIdEnd)) {
|
|
$ProactiveRemediationScriptPolicyType = $IdHashtable[$PolicyIdEnd].policyType
|
|
}
|
|
|
|
if($ProactiveRemediationScriptPolicyType -eq 6) {
|
|
$ProactiveRemediationScriptPolicyTypeName = 'Proactive Remediation'
|
|
} elseif ($ProactiveRemediationScriptPolicyType -eq 8) {
|
|
$ProactiveRemediationScriptPolicyTypeName = 'Custom Compliance'
|
|
} else {
|
|
# We should never get here because we have seen polityType log entry before this log entry
|
|
# Proactive Remediation script type is unknown
|
|
$ProactiveRemediationScriptPolicyTypeName = 'Proactive Remediation script (unknown type)'
|
|
}
|
|
|
|
# Check if next log entry is either
|
|
# [HS] remediation is not optional, kick off remediation script
|
|
# [HS] no remediation script, skip remediation script
|
|
if(($i+1 -lt $LogEntryList.Count) -and (($LogEntryList[$i+1].Message -like '`[HS`] remediation is not optional, kick off remediation script') -or ($LogEntryList[$i+1].Message -like '`[HS`] no remediation script, skip remediation script'))) {
|
|
$RemediationDetectPostActionMessageIndex = $i + 1
|
|
} else {
|
|
$RemediationDetectPostActionMessageIndex = $null
|
|
}
|
|
|
|
Write-Verbose "Found $ProactiveRemediationScriptPolicyTypeName ($ProactiveRemediationScriptName) Detect Ending message ($($LogEntryList[$i].FileName) Line $($LogEntryList[$i].Line) Thread $Thread): $($LogEntryList[$i].Message)"
|
|
|
|
$ProactiveRemediationDetectStdOutput = $null
|
|
$ProactiveRemediationDetectStdOutIndex = $null
|
|
$ProactiveRemediationDetectErrorOutput = $null
|
|
$ProactiveRemediationDetectErrorOutputIndex = $null
|
|
|
|
$PowershellScriptStartTime = $null
|
|
$PowershellScriptEndTime = $null
|
|
$PowershellScriptStartIndex = $null
|
|
$PowershellScriptEndIndex = $null
|
|
|
|
$ProactiveRemediationDetectProcessId = $null
|
|
$ProcessRunTimeTotalSeconds = $null
|
|
$ProactiveRemediationDetectRunAs = 'N/A'
|
|
|
|
# Try to find Proactive Remediation Detect Start log entry (going upwards)
|
|
$b = $i - 1
|
|
for($b = $i - 1; $b -ge 0; $b-- ) {
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] std output =(.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationDetectStdOutput = ($Matches[1]).Trim()
|
|
$ProactiveRemediationDetectStdOutIndex = $b
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] err output =(.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationDetectErrorOutput = ($Matches[1]).Trim()
|
|
$ProactiveRemediationDetectErrorOutputIndex = $b
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^Powershell execution is done, exitCode = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationDetectExitCode = $Matches[1]
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$b].ProcessRunTime = "End Proactive Detect Powershell (ExitCode=$ProactiveRemediationDetectExitCode)"
|
|
|
|
$PowershellScriptEndTime = $LogEntryList[$b].LogEntryDateTime
|
|
$PowershellScriptEndIndex = $b
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^process id = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationDetectProcessId = $Matches[1]
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$b].ProcessRunTime = "Start Proactive Detect Powershell"
|
|
|
|
$PowershellScriptStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
|
|
# Calculate Powershell script runtime
|
|
if($PowershellScriptStartTime -and $PowershellScriptEndTime) {
|
|
$PowershellScriptRuntime = New-Timespan $PowershellScriptStartTime $PowershellScriptEndTime
|
|
$ProcessRunTimeTotalSeconds = $PowershellScriptRuntime.TotalSeconds
|
|
} else {
|
|
$ProcessRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
$PowershellScriptStartIndex = $b
|
|
|
|
# This if seems not to work
|
|
# Left here if it would be fixed at some time
|
|
if($ProactiveRemediationScriptPolicyType -eq 8) {
|
|
# Custom Compliance Script
|
|
|
|
# Set RunTime to Powershell script Start log entry
|
|
$LogEntryList[$b].ProcessRunTime = "{0,-16} {1,8:n1}" -f "Start Custom Compliance Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Add RunTime to Powershell script End log entry
|
|
$LogEntryList[$PowershellScriptEndIndex].ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Custom Compliance Powershell", $ProcessRunTimeTotalSeconds
|
|
} else {
|
|
# Proactive Remediation script
|
|
|
|
# Set RunTime to Powershell script Start log entry
|
|
$LogEntryList[$b].ProcessRunTime = "{0,-16} {1,8:n1}" -f "Start Proactive Detect Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Add RunTime to Powershell script End log entry
|
|
$LogEntryList[$PowershellScriptEndIndex].ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Proactive Detect Powershell", $ProcessRunTimeTotalSeconds
|
|
}
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] The policy needs be run as (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationDetectRunAs = $Matches[1]
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
# Custom Compliance policy name is shorter than Proactive Remediations
|
|
# Add padding so we can align texts
|
|
# And Yes, I should use -f formatting on these but maybe next time :)
|
|
if($ProactiveRemediationScriptPolicyType -eq 8) {
|
|
$Padding = ' '
|
|
} else {
|
|
$Padding = ''
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] Processing policy with id = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$PolicyIdStart = $Matches[1]
|
|
|
|
# $b is startIndex
|
|
|
|
if($PolicyIdStart -eq $PolicyIdEnd) {
|
|
# We found matching starting log entry
|
|
$DetectionLogEntryStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
if($DetectionLogEntryStartTime -and $DetectionLogEntryEndTime) {
|
|
$ProactiveDetectionRuntime = New-Timespan $DetectionLogEntryStartTime $DetectionLogEntryEndTime
|
|
$ProcessRunTimeTotalSeconds = $ProactiveDetectionRuntime.TotalSeconds
|
|
} else {
|
|
$ProcessRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
|
|
|
|
# Start message to timeline
|
|
if($ShowAllTimelineEntries) {
|
|
RecordStatusToTimeline -date $LogEntryList[$b].DateTime -status "Process / Detect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -logEntry "Line $($LogEntryList[$b].Line)"
|
|
}
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$b].ProcessRunTime = "Processing $ProactiveRemediationScriptPolicyTypeName Detect script ($ProactiveRemediationScriptName) (run as $ProactiveRemediationDetectRunAs)"
|
|
|
|
|
|
# End message to Timeline
|
|
if($PreRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / Detect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName Detect script ($ProactiveRemediationScriptName) (Runtime: $ProcessRunTimeTotalSeconds sec)"
|
|
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / Detect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed $ProactiveRemediationScriptPolicyTypeName Detect script ($ProactiveRemediationScriptName) (Runtime: $ProcessRunTimeTotalSeconds sec)"
|
|
}
|
|
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $ProactiveRemediationDetectStdOutIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationDetectStdOutIndex].DateTime -status "StdOut" -detail "$ProactiveRemediationDetectStdOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationDetectStdOutIndex].ProcessRunTime = "Powershell Detect StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $ProactiveRemediationDetectErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationDetectErrorOutputIndex].DateTime -status "ErrorLog" -detail "$ProactiveRemediationDetectErrorOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationDetectErrorOutputIndex].ProcessRunTime = "Powershell Detect ErrorLog"
|
|
|
|
}
|
|
|
|
# Post-End message to Timeline if exists
|
|
if($RemediationDetectPostActionMessageIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$RemediationDetectPostActionMessageIndex].DateTime -status "Info" -detail " $($LogEntryList[$RemediationDetectPostActionMessageIndex].Message)" -logEntry "Line $($LogEntryList[$RemediationDetectPostActionMessageIndex].Line)" -Color 'Yellow'
|
|
}
|
|
|
|
|
|
} else {
|
|
# We missed Proactive Remediation Detect start log entry
|
|
# We should never get here
|
|
|
|
if($PreRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / Detect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName Detect script ($ProactiveRemediationScriptName)"
|
|
|
|
} else {
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / Detect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed/NotCompliant $ProactiveRemediationScriptPolicyTypeName Detect script ($ProactiveRemediationScriptName)"
|
|
}
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $ProactiveRemediationDetectStdOutIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationDetectStdOutIndex].DateTime -status "StdOut" -detail "$ProactiveRemediationDetectStdOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationDetectStdOutIndex].ProcessRunTime = "Powershell Detect StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $ProactiveRemediationDetectErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationDetectErrorOutputIndex].DateTime -status "ErrorLog" -detail "$ProactiveRemediationDetectErrorOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationDetectErrorOutputIndex].ProcessRunTime = "Powershell Detect ErrorLog"
|
|
|
|
}
|
|
|
|
# Post-End message to Timeline if exists
|
|
if($RemediationDetectPostActionMessageIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$RemediationDetectPostActionMessageIndex].DateTime -status "Info" -detail " $($LogEntryList[$RemediationDetectPostActionMessageIndex].Message)" -logEntry "Line $($LogEntryList[$RemediationDetectPostActionMessageIndex].Line)" -Color 'Yellow'
|
|
}
|
|
|
|
Break
|
|
}
|
|
|
|
# We are done
|
|
Write-Host
|
|
|
|
# Break out from For-loop
|
|
Break
|
|
}
|
|
|
|
|
|
if($LogEntryList[$b - 1].Message -like '`[HS`] the pre-remdiation detection script compliance result for*') {
|
|
# We missed Proactive Remediation Detect start log entry
|
|
|
|
#Write-Host "Missed Proactive Remedation Detect script start log entry" -ForegroundColor Red
|
|
|
|
if($PreRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / Detect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName Detection script ($ProactiveRemediationScriptName)"
|
|
|
|
} else {
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / Not Compliant" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed/NotCompliant $ProactiveRemediationScriptPolicyTypeName Detection script ($ProactiveRemediationScriptName)"
|
|
}
|
|
|
|
Break
|
|
}
|
|
|
|
}
|
|
} # For-loop end
|
|
Write-Verbose ""
|
|
|
|
}
|
|
# endregion Try to find Proactive Detection script run
|
|
|
|
|
|
# region Try to find Proactive Remediate script run
|
|
if($LogEntryList[$i].Message -like '`[HS`] remediation script exit code is*') {
|
|
#Write-Host "DEBUG: Proactive Remediation Remediate Script found" -ForegroundColor Yellow
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '\[HS\] remediation script exit code is (.*)$') {
|
|
$RemediationScriptExitCode = $Matches[1]
|
|
|
|
#Write-Host "DEBUG: $($LogEntryList[$i].Message)"
|
|
|
|
$ProcessStartTime = $null
|
|
$RemediationScriptLogEntryEndTime = $LogEntryList[$i].LogEntryDateTime
|
|
$Thread = $LogEntryList[$i].Thread
|
|
|
|
|
|
$RemediationScriptStdOutput = $null
|
|
$RemediationScriptStdOutputIndex = $null
|
|
$RemediationScriptErrorOutput = $null
|
|
$RemediationScriptErrorOutputIndex = $null
|
|
|
|
$PowershellScriptStartTime = $null
|
|
$PowershellScriptEndTime = $null
|
|
$PowershellScriptStartIndex = $null
|
|
$PowershellScriptEndIndex = $null
|
|
|
|
$RemediationPowershellScriptExitCode = $null
|
|
|
|
$RemediationPowershellScriptProcessId = $null
|
|
$ProcessRunTimeTotalSeconds = $null
|
|
$RemediationScriptRunAs = 'N/A'
|
|
|
|
# Try to find Proactive Remediation Remediate Start log entry (going upwards)
|
|
$b = $i - 1
|
|
for($b = $i - 1; $b -ge 0; $b-- ) {
|
|
|
|
#Write-Host "DEBUG: $($LogEntryList[$b].Message)"
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] std output =(.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$RemediationScriptStdOutput = ($Matches[1]).Trim()
|
|
$RemediationScriptStdOutputIndex = $b
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] err output =(.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$RemediationScriptErrorOutput = ($Matches[1]).Trim()
|
|
$RemediationScriptErrorOutputIndex = $b
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^Powershell execution is done, exitCode = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$RemediationPowershellScriptExitCode = $Matches[1]
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$b].ProcessRunTime = "End Proactive Remediate Powershell (ExitCode=$RemediationPowershellScriptExitCode)"
|
|
|
|
$PowershellScriptEndTime = $LogEntryList[$b].LogEntryDateTime
|
|
$PowershellScriptEndIndex = $b
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^process id = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$RemediationPowershellScriptProcessId = $Matches[1]
|
|
|
|
$PowershellScriptStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
|
|
# Calculate Powershell script runtime
|
|
if($PowershellScriptStartTime -and $PowershellScriptEndTime) {
|
|
$PowershellScriptRuntime = New-Timespan $PowershellScriptStartTime $PowershellScriptEndTime
|
|
$ProcessRunTimeTotalSeconds = $PowershellScriptRuntime.TotalSeconds
|
|
} else {
|
|
$ProcessRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
$PowershellScriptStartIndex = $b
|
|
|
|
# Set RunTime to Powershell script Start log entry
|
|
$LogEntryList[$b].ProcessRunTime = "{0,-16} {1,8:n1}" -f "Start Proactive Remediate Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Add RunTime to Powershell script End log entry
|
|
$LogEntryList[$PowershellScriptEndIndex].ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Proactive Remediate Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] The policy needs be run as (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$RemediationScriptRunAs = $Matches[1]
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] Processing policy with id = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$PolicyIdStart = $Matches[1]
|
|
|
|
# $b is startIndex
|
|
|
|
# Try to find displayName
|
|
if($IdHashtable.ContainsKey($PolicyIdStart)) {
|
|
if($IdHashtable[$PolicyIdStart].displayName) {
|
|
$ProactiveRemediationScriptName = $IdHashtable[$PolicyIdStart].displayName
|
|
} else {
|
|
$ProactiveRemediationScriptName = $PolicyIdStart
|
|
}
|
|
}
|
|
|
|
|
|
$RemediationScriptStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
if($RemediationScriptStartTime -and $RemediationScriptLogEntryEndTime) {
|
|
$ProactiveRemediateRuntime = New-Timespan $RemediationScriptStartTime $RemediationScriptLogEntryEndTime
|
|
$ProcessRunTimeTotalSeconds = $ProactiveDetectionRuntime.TotalSeconds
|
|
} else {
|
|
$ProcessRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
#Write-Host "DEBUG: `$ProcessRunTimeTotalSeconds=$ProcessRunTimeTotalSeconds"
|
|
|
|
$Padding = ''
|
|
|
|
# Start message to Timeline
|
|
if($ShowAllTimelineEntries) {
|
|
RecordStatusToTimeline -date $LogEntryList[$b].DateTime -status "Process / Remediate" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName (run as $RemediationScriptRunAs)" -logEntry "Line $($LogEntryList[$b].Line)"
|
|
}
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$b].ProcessRunTime = "Processing $ProactiveRemediationScriptPolicyTypeName Remediate script ($ProactiveRemediationScriptName) (run as $RemediationScriptRunAs)"
|
|
|
|
# End message to Timeline
|
|
if($RemediationScriptExitCode -eq 0) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / Remediate" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName Detection script ($ProactiveRemediationScriptName) (Runtime: $ProcessRunTimeTotalSeconds sec)"
|
|
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / Remediate" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Red' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed $ProactiveRemediationScriptPolicyTypeName Remediate script ($ProactiveRemediationScriptName) (Runtime: $ProcessRunTimeTotalSeconds sec)"
|
|
}
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $RemediationScriptStdOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$RemediationScriptStdOutputIndex].DateTime -status "StdOut" -detail "$RemediationScriptStdOutput" -logEntry "Line $($LogEntryList[$RemediationScriptStdOutputIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$RemediationScriptStdOutputIndex].ProcessRunTime = "Powershell Remediate StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $RemediationScriptErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$RemediationScriptErrorOutputIndex].DateTime -status "ErrorLog" -detail "$RemediationScriptErrorOutput" -logEntry "Line $($LogEntryList[$RemediationScriptErrorOutputIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$RemediationScriptErrorOutput].ProcessRunTime = "Powershell Remediate ErrorLog"
|
|
|
|
}
|
|
|
|
# Break out from For-loop
|
|
Break
|
|
}
|
|
|
|
} # For-loop end
|
|
|
|
|
|
if($LogEntryList[$b - 1].Message -like '`[HS`] the pre-remdiation detection script compliance result for*') {
|
|
# We missed Proactive Remediation Detect start log entry
|
|
|
|
#Write-Host "Missed Proactive Remedation Detect script start log entry" -ForegroundColor Red
|
|
|
|
if($PreRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Succeeded" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName Detection script ($ProactiveRemediationScriptName)"
|
|
|
|
} else {
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / Not Compliant" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed/NotCompliant $ProactiveRemediationScriptPolicyTypeName Detection script ($ProactiveRemediationScriptName)"
|
|
}
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $RemediationScriptStdOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$RemediationScriptStdOutputIndex].DateTime -status "StdOut" -detail "$RemediationScriptStdOutput" -logEntry "Line $($LogEntryList[$RemediationScriptStdOutputIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$RemediationScriptStdOutputIndex].ProcessRunTime = "Powershell Remediate StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $RemediationScriptErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$RemediationScriptErrorOutputIndex].DateTime -status "ErrorLog" -detail "$RemediationScriptErrorOutput" -logEntry "Line $($LogEntryList[$RemediationScriptErrorOutputIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$RemediationScriptErrorOutput].ProcessRunTime = "Powershell Remediate ErrorLog"
|
|
|
|
}
|
|
|
|
Break
|
|
}
|
|
}
|
|
}
|
|
# endregion Try to find Proactive Remediate script run
|
|
|
|
# region Try to find Proactive PostDetection script run
|
|
if($LogEntryList[$i].Message -like '`[HS`] the post detection script result for *') {
|
|
#Write-Host "Proactive Remediation PostDetect found" -ForegroundColor Yellow
|
|
|
|
$Matches=$null
|
|
if($LogEntryList[$i].Message -Match '^\[HS\] the post detection script result for (.*) is (.*)$') {
|
|
$PolicyIdEnd = $Matches[1]
|
|
$PostRemediationDetectResult = $Matches[2]
|
|
|
|
$ProcessStartTime = $null
|
|
$DetectionLogEntryEndTime = $LogEntryList[$i].LogEntryDateTime
|
|
$Thread = $LogEntryList[$i].Thread
|
|
|
|
# Try to find displayName
|
|
if($IdHashtable.ContainsKey($PolicyIdEnd)) {
|
|
if($IdHashtable[$PolicyIdEnd].displayName) {
|
|
$ProactiveRemediationScriptName = $IdHashtable[$PolicyIdEnd].displayName
|
|
} else {
|
|
$ProactiveRemediationScriptName = $PolicyIdEnd
|
|
}
|
|
}
|
|
|
|
# Try to find policyType
|
|
if($IdHashtable.ContainsKey($PolicyIdEnd)) {
|
|
$ProactiveRemediationScriptPolicyType = $IdHashtable[$PolicyIdEnd].policyType
|
|
}
|
|
|
|
if($ProactiveRemediationScriptPolicyType -eq 6) {
|
|
$ProactiveRemediationScriptPolicyTypeName = 'Proactive Remediation'
|
|
} elseif ($ProactiveRemediationScriptPolicyType -eq 8) {
|
|
$ProactiveRemediationScriptPolicyTypeName = 'Custom Compliance'
|
|
} else {
|
|
# We should never get here because we have seen polityType log entry before this log entry
|
|
# Proactive Remediation script type is unknown
|
|
$ProactiveRemediationScriptPolicyTypeName = 'Proactive Remediation script (unknown type)'
|
|
}
|
|
|
|
Write-Verbose "Found $ProactiveRemediationScriptPolicyTypeName ($ProactiveRemediationScriptName) Detect Ending message ($($LogEntryList[$i].FileName) Line $($LogEntryList[$i].Line) Thread $Thread): $($LogEntryList[$i].Message)"
|
|
|
|
$ProactiveRemediationPostDetectStdOutput = $null
|
|
$ProactiveRemediationPostDetectStdOutIndex = $null
|
|
$ProactiveRemediationPostDetectErrorOutput = $null
|
|
$ProactiveRemediationPostDetectErrorOutputIndex = $null
|
|
|
|
$PowershellScriptStartTime = $null
|
|
$PowershellScriptEndTime = $null
|
|
$PowershellScriptStartIndex = $null
|
|
$PowershellScriptEndIndex = $null
|
|
|
|
$ProactiveRemediationPostDetectProcessId = $null
|
|
$ProcessRunTimeTotalSeconds = $null
|
|
$ProactiveRemediationPostDetectRunAs = 'N/A'
|
|
|
|
# Try to find Proactive Remediation Detect Start log entry (going upwards)
|
|
$b = $i - 1
|
|
for($b = $i - 1; $b -ge 0; $b-- ) {
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] std output =(.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationPostDetectStdOutput = ($Matches[1]).Trim()
|
|
$ProactiveRemediationPostDetectStdOutIndex = $b
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] err output =(.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationPostDetectErrorOutput = ($Matches[1]).Trim()
|
|
$ProactiveRemediationPostDetectErrorOutputIndex = $b
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^Powershell execution is done, exitCode = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationDetectExitCode = $Matches[1]
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$b].ProcessRunTime = "End Proactive PostDetect Powershell (ExitCode=$ProactiveRemediationDetectExitCode)"
|
|
|
|
$PowershellScriptEndTime = $LogEntryList[$b].LogEntryDateTime
|
|
$PowershellScriptEndIndex = $b
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^process id = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationPostDetectProcessId = $Matches[1]
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$b].ProcessRunTime = "Start Proactive PostDetect Powershell"
|
|
|
|
$PowershellScriptStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
|
|
# Calculate Powershell script runtime
|
|
if($PowershellScriptStartTime -and $PowershellScriptEndTime) {
|
|
$PowershellScriptRuntime = New-Timespan $PowershellScriptStartTime $PowershellScriptEndTime
|
|
$ProcessRunTimeTotalSeconds = $PowershellScriptRuntime.TotalSeconds
|
|
} else {
|
|
$ProcessRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
$PowershellScriptStartIndex = $b
|
|
|
|
# Set RunTime to Powershell script Start log entry
|
|
$LogEntryList[$b].ProcessRunTime = "{0,-16} {1,8:n1}" -f "Start Proactive PostDetect Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Add RunTime to Powershell script End log entry
|
|
$LogEntryList[$PowershellScriptEndIndex].ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Proactive PostDetect Powershell", $ProcessRunTimeTotalSeconds
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] The policy needs be run as (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$ProactiveRemediationPostDetectRunAs = $Matches[1]
|
|
|
|
# Continue to previous log entry
|
|
Continue
|
|
}
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[HS\] Processing policy with id = (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$PolicyIdStart = $Matches[1]
|
|
|
|
# $b is startIndex
|
|
|
|
if($PolicyIdStart -eq $PolicyIdEnd) {
|
|
# We found matching starting log entry
|
|
$DetectionLogEntryStartTime = $LogEntryList[$b].LogEntryDateTime
|
|
if($DetectionLogEntryStartTime -and $DetectionLogEntryEndTime) {
|
|
$ProactiveDetectionRuntime = New-Timespan $DetectionLogEntryStartTime $DetectionLogEntryEndTime
|
|
$ProcessRunTimeTotalSeconds = $ProactiveDetectionRuntime.TotalSeconds
|
|
} else {
|
|
$ProcessRunTimeTotalSeconds = 'N/A'
|
|
}
|
|
|
|
$Padding = ''
|
|
|
|
# Start message to Timeline
|
|
if($ShowAllTimelineEntries) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$b].DateTime -status "Process / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName (run as $ProactiveRemediationPostDetectRunAs)" -logEntry "Line $($LogEntryList[$b].Line)"
|
|
}
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$b].ProcessRunTime = "Processing $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName) (run as $ProactiveRemediationPostDetectRunAs)"
|
|
|
|
|
|
# End message to Timeline
|
|
if($PostRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName) (Runtime: $ProcessRunTimeTotalSeconds sec)"
|
|
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName) (Runtime: $ProcessRunTimeTotalSeconds sec)"
|
|
}
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $ProactiveRemediationPostDetectStdOutIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].DateTime -status "StdOut" -detail "$ProactiveRemediationPostDetectStdOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].ProcessRunTime = "Powershell PostDetect StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $ProactiveRemediationPostDetectErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationPostDetectErrorOutputIndex].DateTime -status "ErrorLog" -detail "$ProactiveRemediationPostDetectErrorOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationPostDetectErrorOutputIndex].ProcessRunTime = "Powershell PostDetect ErrorLog"
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
# We missed Proactive Remediation Detect start log entry
|
|
# We should never get here
|
|
|
|
$Padding = ''
|
|
if($PostRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName)"
|
|
|
|
} else {
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed/NotCompliant $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName)"
|
|
}
|
|
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $ProactiveRemediationPostDetectStdOutIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].DateTime -status "StdOut" -detail "$ProactiveRemediationPostDetectStdOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].ProcessRunTime = "Powershell PostDetect StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $ProactiveRemediationPostDetectErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationPostDetectErrorOutputIndex].DateTime -status "ErrorLog" -detail "$ProactiveRemediationPostDetectErrorOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationPostDetectErrorOutputIndex].ProcessRunTime = "Powershell PostDetect ErrorLog"
|
|
|
|
}
|
|
|
|
Break
|
|
}
|
|
|
|
# We are done
|
|
Write-Host
|
|
|
|
# Break out from For-loop
|
|
Break
|
|
}
|
|
|
|
$Padding = ''
|
|
if($LogEntryList[$b - 1].Message -like '`[HS`] the pre-remdiation detection script compliance result for*') {
|
|
# We missed Proactive Remediation Detect start log entry
|
|
|
|
#Write-Host "Missed Proactive Remedation Detect script start log entry" -ForegroundColor Red
|
|
|
|
if($PostRemediationDetectResult -eq 'True') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Success / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Green' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
# Set RunTime log entry
|
|
$LogEntryList[$i].ProcessRunTime = "Succeeded $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName)"
|
|
|
|
} else {
|
|
RecordStatusToTimeline -date $LogEntryList[$i].DateTime -status "Failed / PostDetect" -detail "$ProactiveRemediationScriptPolicyTypeName $($Padding): $ProactiveRemediationScriptName" -Color 'Yellow' -logEntry "Line $($LogEntryList[$i].Line)"
|
|
|
|
$LogEntryList[$i].ProcessRunTime = "Failed/NotCompliant $ProactiveRemediationScriptPolicyTypeName PostDetect script ($ProactiveRemediationScriptName)"
|
|
}
|
|
|
|
# Script StdOut to Timeline if configured and if exists
|
|
if($ShowStdOutInTimeline -and $ProactiveRemediationPostDetectStdOutIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].DateTime -status "StdOut" -detail "$ProactiveRemediationPostDetectStdOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].ProcessRunTime = "Powershell PostDetect StdOut"
|
|
}
|
|
|
|
# Script Error to Timeline if configured and if exists
|
|
if($ShowErrorsInTimeline -and $ProactiveRemediationPostDetectErrorOutputIndex) {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$ProactiveRemediationPostDetectErrorOutputIndex].DateTime -status "ErrorLog" -detail "$ProactiveRemediationPostDetectErrorOutput" -logEntry "Line $($LogEntryList[$ProactiveRemediationPostDetectStdOutIndex].Line)" -Color 'Yellow'
|
|
|
|
$LogEntryList[$ProactiveRemediationPostDetectErrorOutputIndex].ProcessRunTime = "Powershell PostDetect ErrorLog"
|
|
|
|
}
|
|
|
|
Break
|
|
}
|
|
|
|
}
|
|
} # For-loop end
|
|
Write-Verbose ""
|
|
|
|
}
|
|
# endregion Try to find Proactive PostDetection script run
|
|
|
|
|
|
|
|
###############################################
|
|
|
|
# region Find Win32App installations
|
|
# Check if this is ending message for Win32 application installation
|
|
if($LogEntryList[$i].Message -like '`[Win32App`] Installation is done, collecting result') {
|
|
$Win32AppInstallEndIndex = $i
|
|
|
|
$ProcessStartTime = $null
|
|
$ProcessEndTime = $LogEntryList[$i].LogEntryDateTime
|
|
$Thread = $LogEntryList[$i].Thread
|
|
Write-Verbose "Found Win32 App install end process message (Line $($LogEntryList[$i].Line) Thread $Thread): $($LogEntryList[$i].Message)"
|
|
|
|
$Win32AppExitCode='N/A'
|
|
$Win32AppInstallSuccess='N/A'
|
|
|
|
# Check if we can find exitCode and Success/failed information in next 20 lines
|
|
for($b = $Win32AppInstallEndIndex + 1; $b -lt $Win32AppInstallEndIndex + 20; $b++ ) {
|
|
$Matches=$null
|
|
|
|
# Run this if first because it is much faster than run -Match for every line
|
|
if($Win32AppExitCode -eq 'N/A') {
|
|
if(($LogEntryList[$b].Message -Match '^\[Win32App\] lpExitCode (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$Win32AppExitCode = $Matches[1]
|
|
Write-Verbose "Found Win32App install exitCode: $Win32AppExitCode"
|
|
|
|
# Continue For-loop to next log entry
|
|
Continue
|
|
}
|
|
}
|
|
|
|
# Run this if first because it is much faster than run -Match for every line
|
|
if($Win32AppInstallSuccess -eq 'N/A') {
|
|
if(($LogEntryList[$b].Message -Match '^\[Win32App\] lpExitCode is defined as (.*)$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$Win32AppInstallSuccess = $Matches[1]
|
|
Write-Verbose "Found Win32App install success: $Win32AppInstallSuccess"
|
|
|
|
# Break out from For-loop
|
|
Break
|
|
}
|
|
}
|
|
} # For-loop end
|
|
|
|
# Check if we can find previous LogEntry that matches same process thread start message
|
|
$TemporaryObjectIndex = $Win32AppInstallEndIndex - 1
|
|
Do {
|
|
if(($LogEntryList[$TemporaryObjectIndex].Message -like '`[Win32App`] process id =*') -and ($LogEntryList[$TemporaryObjectIndex].Thread -eq $Thread)) {
|
|
# Message matches Win32App install start message
|
|
$ProcessStartTime = $LogEntryList[$TemporaryObjectIndex].LogEntryDateTime
|
|
$ProcessRunTime = New-Timespan $ProcessStartTime $ProcessEndTime
|
|
$ProcessRunTimeTotalSeconds = $ProcessRunTime.TotalSeconds
|
|
$ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Win32App", $ProcessRunTimeTotalSeconds
|
|
|
|
Write-Verbose "Found Win32 App install Start process message (Line $($LogEntryList[$TemporaryObjectIndex].Line)): $($LogEntryList[$TemporaryObjectIndex].Message)"
|
|
|
|
# Set RunTime to Win32App Install Start log entry
|
|
$LogEntryList[$TemporaryObjectIndex].ProcessRunTime = "{0,-16} {1,8:n1}" -f "Start Win32App", $ProcessRunTimeTotalSeconds
|
|
|
|
# Set RunTime to Powershell script Install End log entry
|
|
$LogEntryList[$i].ProcessRunTime = "{0,-16} {1,8:n1}" -f "End Win32App", $ProcessRunTimeTotalSeconds
|
|
|
|
Write-Verbose "Runtime: $ProcessRunTimeTotalSeconds"
|
|
|
|
Break
|
|
} else {
|
|
# Message does not match Win32App install start message
|
|
}
|
|
$TemporaryObjectIndex--
|
|
} While ($TemporaryObjectIndex -ge 0)
|
|
|
|
# Try to find Win32App name if we have start and end time
|
|
$Win32AppId = 'N/A'
|
|
$Win32AppName = 'N/A'
|
|
if($ProcessStartTime -and $ProcessEndTime) {
|
|
$Win32AppInstallStartIndex = $TemporaryObjectIndex
|
|
|
|
$Win32AppNameRegex = '\[StatusService\] Downloading app \(id = (.*), name (.*)\).*$'
|
|
|
|
Do {
|
|
if($LogEntryList[$TemporaryObjectIndex].Message -Match $Win32AppNameRegex) {
|
|
if($LogEntryList[$TemporaryObjectIndex].Thread -eq $Thread) {
|
|
|
|
# Message matches Win32App Name message
|
|
$Win32AppId = $Matches[1]
|
|
$Win32AppName = $Matches[2]
|
|
$ProcessRunTime = "$ProcessRunTime $Win32AppName"
|
|
|
|
Write-Verbose "Win32App Name $Win32AppName"
|
|
|
|
#Write-Host "DEBUG: Found Win32 App install Start process message (Line $($LogEntryList[$TemporaryObjectIndex].Line)): $($LogEntryList[$TemporaryObjectIndex].Message)`n"
|
|
|
|
# Set Win32App Name to Win32App Install Start entry
|
|
$LogEntryList[$Win32AppInstallStartIndex].ProcessRunTime = "$($LogEntryList[$Win32AppInstallStartIndex].ProcessRunTime) $Win32AppName"
|
|
|
|
# Set Win32App Name to Win32App Install End entry
|
|
$LogEntryList[$i].ProcessRunTime = "$($LogEntryList[$i].ProcessRunTime) $Win32AppName (ExitCode $Win32AppExitCode $Win32AppInstallSuccess)"
|
|
|
|
# Add AppId and name to Hashtable
|
|
if($IdHashtable.ContainsKey($Win32AppId)) {
|
|
$IdHashtable[$Win32AppId].id = $Win32AppId
|
|
$IdHashtable[$Win32AppId].name = $Win32AppName
|
|
$IdHashtable[$Win32AppId].displayName = $Win32AppName
|
|
|
|
} else {
|
|
$AppIdCustomObjectProperties = @{
|
|
id = $Win32AppId
|
|
name = $Win32AppName
|
|
displayName = $Win32AppName
|
|
}
|
|
$AppIdCustomObject = New-Object -TypeName PSObject -Prop $AppIdCustomObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($Win32AppId , $AppIdCustomObject)
|
|
}
|
|
|
|
Break
|
|
} else {
|
|
# Found Win32App Name but Thread does not match
|
|
# Aborting Name find
|
|
Break
|
|
}
|
|
} else {
|
|
# Message does not match Win32App Name
|
|
}
|
|
$TemporaryObjectIndex--
|
|
} While ($TemporaryObjectIndex -ge 0)
|
|
|
|
# Change text color depending on Win32App install Success status
|
|
if($Win32AppInstallSuccess -eq 'Success') {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$Win32AppInstallEndIndex].DateTime -status "Success / Win32App" -detail "[Win32App] App install: $Win32AppName (ExitCode $Win32AppExitCode $Win32AppInstallSuccess)" -logEntry "Line $($LogEntryList[$Win32AppInstallEndIndex].Line)" -Seconds $ProcessRunTimeTotalSeconds -Color 'Green'
|
|
|
|
} else {
|
|
|
|
RecordStatusToTimeline -date $LogEntryList[$Win32AppInstallEndIndex].DateTime -status "Failed / Win32App" -detail "[Win32App] App install: $Win32AppName (ExitCode $Win32AppExitCode $Win32AppInstallSuccess)" -logEntry "Line $($LogEntryList[$Win32AppInstallEndIndex].Line)" -Seconds $ProcessRunTimeTotalSeconds -Color 'Red'
|
|
}
|
|
}
|
|
|
|
|
|
if(($TemporaryObjectIndex -eq 0) -and (-not $ProcessStartTime)) {
|
|
# We did not find Win32App install start time
|
|
Write-Host "Did NOT find matching Win32App install start time for Win32App install in line $LineNumber (Thread=$Thead)`n" -ForegroundColor Yellow
|
|
}
|
|
|
|
# Cleanup variables
|
|
$ProcessStartTime = $null
|
|
$ProcessEndTime = $null
|
|
$Thread = $null
|
|
$ProcessRunTimeTotalSeconds = $null
|
|
$ProcessRunTimeText = $null
|
|
}
|
|
# endregion Find Win32App installations
|
|
|
|
# region Find WinGet Application install
|
|
if($LogEntryList[$i].Message -like '`[Win32App`]`[V3Processor`] Processing subgraph with app ids:*') {
|
|
if($LogEntryList[$i].Message -Match '^\[Win32App\]\[V3Processor\] Processing subgraph with app ids: (.*)$') {
|
|
$WinGetAppId = $Matches[1]
|
|
$WinGetProcessingStartIndex = $i
|
|
|
|
$Thread = $LogEntryList[$i].Thread
|
|
$WinGetInstallationStartedDateTime = $LogEntryList[$i].DateTime
|
|
|
|
$WinGetDownloadFirstEntryFound=$false
|
|
|
|
#Write-Host "DEBUG: Found WinGet installation start. `$i=$i"
|
|
|
|
# Find next WinGet installation log entries
|
|
# Now we go down (to end)
|
|
for($b = $i + 1; $b -lt $LogEntryList.Count; $b++ ) {
|
|
|
|
# Use this to debug WinGet log entries
|
|
#Write-Host "DEBUG: $b - $($LogEntryList[$b].Message)"
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[Win32App\]\[WinGetApp\]\[WinGetAppExecutionExecutor\] Starting execution of app with id: (.*) and context: (.*)\.$') -and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
$TempAppId = $Matches[1]
|
|
$WinGetInstallContext = $Matches[2]
|
|
|
|
#Write-Host "DEBUG: Found WinGet id ($TempAppId) and context ($WinGetInstallContext)"
|
|
|
|
if($TempAppId -ne $WinGetAppId) {
|
|
# We got different AppIds
|
|
# Something is big time wrong here
|
|
|
|
#Write-Host "DEBUG: WinGet Application Id mismatch in log entries. Aborting WinGet AppId: $WinGetAppId"
|
|
|
|
# Break For-loop
|
|
Break
|
|
}
|
|
|
|
# Continue For-loop to next round
|
|
Continue
|
|
}
|
|
|
|
|
|
if(-not $WinGetDownloadFirstEntryFound) {
|
|
if(($LogEntryList[$b].Message -Match '^\[StatusService\] Downloading app .id = (.*), name (.*). via WinGet, bytes (.*)\/(.*) for user (.*)$')) {
|
|
$WinGetDownloadFirstEntryFound=$True
|
|
|
|
$WinGetDownloadStartDateTime = $LogEntryList[$b].DateTime
|
|
|
|
$AppId = $Matches[1]
|
|
$WinGetAppName = $Matches[2]
|
|
$WinGetAppBytes = $Matches[4]
|
|
$WinGetUserId = $Matches[5]
|
|
|
|
# Add WinGetAppId and name to Hashtable
|
|
if($IdHashtable.ContainsKey($AppId)) {
|
|
$IdHashtable[$AppId].id = $AppId
|
|
$IdHashtable[$AppId].name = $WinGetAppName
|
|
$IdHashtable[$AppId].displayName = $WinGetAppName
|
|
} else {
|
|
$AppIdCustomObjectProperties = @{
|
|
id = $AppId
|
|
name = $WinGetAppName
|
|
displayName = $WinGetAppName
|
|
}
|
|
$AppIdCustomObject = New-Object -TypeName PSObject -Prop $AppIdCustomObjectProperties
|
|
|
|
# Create new UserId hashtable entry
|
|
$IdHashtable.Add($AppId , $AppIdCustomObject)
|
|
}
|
|
|
|
|
|
# Get real UserName if found from Hashtable
|
|
if($IdHashtable.ContainsKey($WinGetUserId)) {
|
|
$Name = $IdHashtable[$WinGetUserId].name
|
|
if($Name) {
|
|
$WinGetUserName = $IdHashtable[$WinGetUserId].name
|
|
} else {
|
|
$WinGetUserName = $WinGetUserId
|
|
}
|
|
}
|
|
|
|
$LogEntryList[$b].ProcessRunTime = " Start Download ($WinGetAppName) for user $WinGetUserName"
|
|
|
|
#Write-Host "DEBUG: Found WinGet first download entry"
|
|
|
|
# Continue For-loop to next round
|
|
Continue
|
|
}
|
|
}
|
|
|
|
$DownloadCompleteString1 = 'State transition processed - Previous State:Download In Progress received event: Download Finished which took state machine into Download Complete state.'
|
|
$DownloadCompleteString2 = 'Processing state transition - Current State:Download Complete With Event: Continue Install.'
|
|
if(($LogEntryList[$b].Message -eq $DownloadCompleteString1) -or ($LogEntryList[$b].Message -eq $DownloadCompleteString2)) {
|
|
|
|
$WinGetDownloadEnddDateTime = $LogEntryList[$b].DateTime
|
|
|
|
$LogEntryList[$b].ProcessRunTime = " Download complete ($WinGetAppName)"
|
|
|
|
#Write-Host "DEBUG: Found WinGet download complete"
|
|
|
|
# Continue For-loop to next round
|
|
Continue
|
|
}
|
|
|
|
|
|
$WinGetInstallStartString = 'State transition processed - Previous State:Download Complete received event: Continue Install which took state machine into Install In Progress Download Complete state.'
|
|
if(($LogEntryList[$b].Message -eq $WinGetInstallStartString)) {
|
|
# -and ($LogEntryList[$b].Thread -eq $Thread)
|
|
$WinGetInstallStartdDateTime = $LogEntryList[$b].DateTime
|
|
$LogEntryList[$b].ProcessRunTime = " Start Install process ($WinGetAppName)"
|
|
#Write-Host "WinGet Found install start"
|
|
|
|
}
|
|
|
|
|
|
if(($LogEntryList[$b].Message -Match '^\[WinGet\] Install Operation Result has arrived (.*)$')) {
|
|
$WinGetInstallResult = $Matches[1]
|
|
|
|
# Calculate install time
|
|
$ProcessStartTime = $LogEntryList[$i].LogEntryDateTime
|
|
$ProcessEndTime = $LogEntryList[$b].LogEntryDateTime
|
|
$ProcessRunTime = New-Timespan $ProcessStartTime $ProcessEndTime
|
|
$ProcessRunTimeTotalSeconds = $ProcessRunTime.TotalSeconds
|
|
|
|
$WinGetInstallEndDateTime = $LogEntryList[$b].DateTime
|
|
$LogEntryList[$i].ProcessRunTime = "Start WinGet processing App ($WinGetAppName)"
|
|
|
|
if($WinGetInstallResult -eq 'OK') {
|
|
RecordStatusToTimeline -date $LogEntryList[$b].DateTime -status "Success / WinGet" -detail "[WinGet] App install: $WinGetAppName for user $WinGetUserName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Green' -logEntry "Line $($LogEntryList[$b].Line)"
|
|
|
|
$LogEntryList[$b].ProcessRunTime = "Stop WinGet Install Success ($WinGetAppName) for user $WinGetUserName ($ProcessRunTimeTotalSeconds)"
|
|
|
|
} else {
|
|
RecordStatusToTimeline -date $LogEntryList[$b].DateTime -status "Failed / WinGet" -detail "[WinGet] App install: $WinGetAppName for user $WinGetUserName" -Seconds $ProcessRunTimeTotalSeconds -Color 'Red' -logEntry "Line $($LogEntryList[$b].Line)"
|
|
|
|
$LogEntryList[$b].ProcessRunTime = "Stop WinGet Install Failed ($WinGetAppName) for user $WinGetUserName ($ProcessRunTimeTotalSeconds)"
|
|
}
|
|
|
|
#Write-Host "WinGet Found install OK"
|
|
|
|
# Break For-loop
|
|
Break
|
|
|
|
}
|
|
|
|
if(((($LogEntryList[$b].Message -eq '[Win32App][V3Processor] Done processing subgraph.') -or `
|
|
($LogEntryList[$b].Message -like '`[Win32App`]`[V3Processor`] Done processing * subgraphs.') -or `
|
|
($LogEntryList[$b].Message -eq '[Win32App][V3Processor] All apps in the subgraph are not applicable due to assignment filters. Processing will be skipped.')))`
|
|
-and ($LogEntryList[$b].Thread -eq $Thread)) {
|
|
|
|
#Write-Host "DEBUG: Found WinGet Done Processing subgraph log entry"
|
|
Write-Verbose "Subgraph $WinGetAppId did not include WinGet Application install ($($LogEntryList[$i].FileName) Line $($LogEntryList[$i].Line))"
|
|
|
|
# Break For-loop
|
|
Break
|
|
}
|
|
|
|
|
|
# Check that we didn't go over end log of this installation process
|
|
# This is just fail safe check. We should not get here
|
|
if($LogEntryList[$b].Message -like '`[Win32App`]`[V3Processor`] Processing subgraph with app ids:*') {
|
|
# New WinGet installation started.
|
|
# We didn't catch all log entries to process WinGet installation
|
|
Write-Verbose "Could not detect WinGet installation for WinGet AppId: $WinGetAppId (Line $($LogEntryList[$i].Line))"
|
|
|
|
# Break For-loop
|
|
Break
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
} # Region ends here
|
|
# region Find WinGet Application install
|
|
|
|
$i++
|
|
} While ($i -lt $LogEntryList.Count)
|
|
# endregion analyze log entries
|
|
|
|
|
|
# This is aligned with Michael Niehaus's Get-AutopilotDiagnostics script just in case
|
|
Write-Host ""
|
|
Write-Host "OBSERVED TIMELINE" -ForegroundColor Magenta
|
|
$observedTimeline |
|
|
Format-Table @{
|
|
label= 'Date'
|
|
Expression = { $_.Date.Split('.')[0] }
|
|
},
|
|
@{
|
|
Label = "Status"
|
|
Expression =
|
|
{
|
|
Switch ($_.Color)
|
|
{
|
|
'Red' { $color = "91"; break }
|
|
'Yellow' { $color = '93'; break }
|
|
'Green' { $color = "92"; break }
|
|
default { $color = "0" }
|
|
}
|
|
$e = [char]27
|
|
"$e[${color}m$($_.Status)$e[0m"
|
|
}
|
|
},
|
|
@{
|
|
Label = "Detail"
|
|
Expression =
|
|
{
|
|
Switch ($_.Color)
|
|
{
|
|
'Red' { $color = "91"; break }
|
|
#'Yellow' { $color = '93'; break }
|
|
'Green' { $color = "92"; break }
|
|
default { $color = "0" }
|
|
}
|
|
$e = [char]27
|
|
"$e[${color}m$($_.Detail)$e[0m"
|
|
}
|
|
},
|
|
@{
|
|
label= 'Seconds'
|
|
Expression = { "{0,6:N0}" -f $_.Seconds }
|
|
},logEntry
|
|
|
|
Write-Host ""
|
|
|
|
|
|
# Print $IdHastable
|
|
#Write-Host "DEBUG: `$IdHastable:`n$($IdHashtable | ConvertTo-Json)"
|
|
|
|
|
|
# Command line Parameter -ShowErrorsSummary
|
|
if($ShowErrorsSummary) {
|
|
if($PowershellScriptErrorLogs) {
|
|
Write-Host "`n`n`n`n"
|
|
Write-Host "####################################"
|
|
Write-Host "Powershell scripts error logs found:"
|
|
Write-Host "$($PowershellScriptErrorLogs -Join "`n")"
|
|
Write-Host "`n"
|
|
}
|
|
}
|
|
|
|
# Command line Parameter -ConvertAllKnownGuidsToClearText
|
|
if($ConvertAllKnownGuidsToClearText) {
|
|
Foreach($HashtableKey in $IdHashtable.Keys) {
|
|
#Write-Host "Processing Hashtable object $($IdHashtable[$HashtableKey].id)"
|
|
|
|
Foreach($LogEntry in $LogEntryList) {
|
|
$Logentry.Message = ($Logentry.Message).Replace($HashtableKey, "'$($IdHashtable[$HashtableKey].name)'")
|
|
}
|
|
|
|
Foreach($LogEntry in $LogEntryList | Where-Object ProcessRuntime -ne $null) {
|
|
$Logentry.ProcessRuntime = ($Logentry.ProcessRuntime).Replace($HashtableKey, "'$($IdHashtable[$HashtableKey].name)'")
|
|
}
|
|
}
|
|
}
|
|
|
|
<# # This on work in progress so disabled at this time
|
|
# Export data to json file
|
|
if($ExportJson) {
|
|
Write-Host "Export all processed data to json file: IntuneManagementExtensionDiagnostics_export.json"
|
|
[array]$ExportJson = $LogEntryList | Select-Object -Property Index, FileName, Line, DateTime, Multiline, ProcessRunTime, Message, Component, Context, Type, Thread, File
|
|
#$ExportJson = $LogEntryList | Select-Object -Property Index, FileName, Line, DateTime, Multiline, ProcessRunTime, Message, Component, Context, Type, Thread, File #| Sort-Object -Property Index
|
|
$ExportJson | ConvertTo-Json | Out-File -Filepath ./IntuneManagementExtensionDiagnostics_export.json -Force
|
|
Write-Host "Done"
|
|
}
|
|
#>
|
|
|
|
if($ShowLogViewerUI) {
|
|
Write-Host "Show logs in Out-GridView"
|
|
|
|
# Show log in Out-GridView
|
|
if($SelectedLogFiles -is [Array]) {
|
|
# Sort log entries and add index number
|
|
# This is needed with multiple files so log entries
|
|
# can be sorted based on this index column
|
|
|
|
# With multiple log files we sort log entries by date
|
|
#$SelectedLines = $LogEntryList | Where-Object { ($_.LogEntryDateTime -gt $LogStartDateTimeObject) -and ($_.LogEntryDateTime -lt $LogEndDateTimeObject) } | Select-Object -Property Index, FileName, Line, DateTime, Multiline, ProcessRunTime, Message, Component, Context, Type, Thread, File | Sort-Object -Property Index | Out-GridView -Title "Intune IME Log Viewer $($SelectedLogFiles.Name -join " ")" -OutputMode Multiple
|
|
|
|
$SelectedLines = $LogEntryList | Select-Object -Property Index, FileName, Line, DateTime, Multiline, ProcessRunTime, Message, Component, Context, Type, Thread, File | Sort-Object -Property Index | Out-GridView -Title "Intune IME Log Viewer $($SelectedLogFiles.Name -join " ")" -OutputMode Multiple
|
|
} else {
|
|
# With single log file sorting is ok by default (line numbers)
|
|
#$SelectedLines = $LogEntryList | Where-Object { ($_.LogEntryDateTime -gt $LogStartDateTimeObject) -and ($_.LogEntryDateTime -lt $LogEndDateTimeObject) } | Select-Object -Property Index, FileName, Line, DateTime, Multiline, ProcessRunTime, Message, Component, Context, Type, Thread, File | Out-GridView -Title "Intune IME Log Viewer $LogFilePath" -OutputMode Multiple
|
|
|
|
$SelectedLines = $LogEntryList | Select-Object -Property Index, FileName, Line, DateTime, Multiline, ProcessRunTime, Message, Component, Context, Type, Thread, File | Out-GridView -Title "Intune IME Log Viewer $LogFilePath" -OutputMode Multiple
|
|
}
|
|
} else {
|
|
|
|
Write-Host "`nTip: Use Parameter -ShowLogViewerUI to get LogViewerUI for graphical log viewing/debugging`n" -ForegroundColor Cyan
|
|
|
|
# Not showing Out-GridView so we need to make sure Do-While loop will exit
|
|
$SelectedLines = $null
|
|
Break
|
|
}
|
|
|
|
} While ($SelectedLines.Message -eq 'RELOAD LOG FILE(S). Select this line and OK button from right botton corner')
|
|
|
|
#Write-Host "Script end"
|