#PSScriptInfo
.VERSION 3.3
.GUID e0307766-d5a7-4704-a578-5ff1fb315a26
.AUTHOR Petri.Paavola@yodamiitti.fi
.COMPANYNAME Yodamiitti Oy
.COPYRIGHT Petri.Paavola@yodamiitti.fi
.TAGS Intune Windows Autopilot troubleshooting log analyzer
.LICENSEURI
.PROJECTURI https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
Version 1.0: Original published version
Version 1.1: Win32App and WinGetApp Required/Available and Install/Uninstall intent is detected right
Win32App Supersedence should be recognized (first uninstall and then install)
Win32App failed (un)install process is detected
Win32App Download Statistics table added
Added export to text files
Version 2.0: Huge new feature is to create html report
Html report is primary reporting and console observed timeline is secondary
All future development will be done to html report
Console timeline will be available for example for OOBE troubleshooting scenarios
Added App detection events to timeline
Html report entries support HoverOn ToolTips which include more information
Version 2.3: Updated script to use Microsoft.Graph.Authentication module to download data from Graph API
Version 2.4: Fix to process new log files AppWorkload.log
Version 3.0: Support for Intune Device Preparation.
Shows Remediation and PowerShell platform script contents and script output (if available) in (hover on) ToolTip with -Online option
Copy any ToolTip data including PowerShell scripts
-DoNotDownloadClearTextRemediationScriptsToReport - New option not to download Remediation script in clear text
Version 3.1: Fixed Remediation script output printing which broke when log line changed at some time
Version 3.2: Fixed partial PowerShell script content showing in ToolTips. Now full script is shown in ToolTips.
Version 3.3: Added Application table to HTML report which shows all applications which were in logs with their detection results and intents. This should help to quickly find application related issues. Also added filtering for this table so you can easily find for example all not detected applications or all required install applications.
#>
<#
.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 log events.
Report is saved to HTML file. Events are also shown in Powershell console window.
Timeline report includes information about Intune Win32App, WinGetApp, Powershell scripts, 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 -> ProcessRunTime -> is not empty.
What really differentiates this LogViewer from other tools is it's capability to convert GUIDs to known names
try parameter -ConvertAllKnownGuidsToClearText and you can see for example real application names instead of GUIDs on log events.
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 -ShowStdOutInReport and -ShowErrorsInReport
This shows instantly what is possible problem in Powershell scripts.
Possible Microsoft 365 App and MSI Line-of-Business Apps (maybe change to Win32App ;) installations are not seen by this report because they are not installed with Intune Management Agent.
Author:
Petri.Paavola@yodamiitti.fi
Senior Modern Management Principal
Microsoft MVP - Windows and Intune
2026-04-23
https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
.PARAMETER Online
Download Powershell, Remediation and custom Compliance policy scripts to get displayName to Timeline report
Install Microsoft Graph module with command: Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser
.PARAMETER LogFile
Specify log file fullpath
.PARAMETER LogFilesFolder
Specify folder where to check log files. Will show UI where you can select what logs to process
.PARAMETER LogStartDateTime
Specify date and time to start log entries. For example -
.PARAMETER LogEndDateTime
Specify date and time to stop log entries
.PARAMETER ShowLogViewerUI
Shows graphical LogViewerUI where all log events are easily browsed, searched and filtered in graphical UI
This parameter will always show file selection UI and event selection UI.
.PARAMETER LogViewerUI
Shows graphical LogViewerUI where all log events are easily browsed, searched and filtered in graphical UI
This parameter will always show file selection UI and event selection UI.
.PARAMETER AllLogEvents
Process all found log events.
Selecting this parameter will disable UI which asks date/time/hour selection for logs (use for silent commands or scripts)
This is default option (aka silent and no selection UI shown)
.PARAMETER LogEventsSelectionUI
Selecting this parameter will enable UI which asks date/time/hour selection for logs
.PARAMETER AllLogFiles
Process all found supported log file(s) automatically. This includes *AgentExecutor*.log, *IntuneManagementExtension*.log and *AppWorkload*.log
Selecting this parameter will disable UI which asks which log files to process (use for silent commands or scripts)
This is default option (aka silent and no selection UI shown)
.PARAMETER LogFilesSelectionUI
Selecting this parameter will enable UI which asks which log files to process
.PARAMETER Today
Show log entries from today (from midnight)
.PARAMETER ShowAllTimelineEvents
Shows more entries in report. This option will show starting messages for events which are not shown by default
.PARAMETER ShowStdOutInReport
Show script StdOut in events. This shows for example what Remediation script will return back to Intune
.PARAMETER ShowErrorsInReport
This will show found error messages from Powershell scripts. Note that Powershell script may succeed and still have errors shown here.
.PARAMETER ShowErrorsSummary
Show separate all errors summary after Timeline.
.PARAMETER ConvertAllKnownGuidsToClearText
This parameter replaces all known GUIDs to cleartext in LogViewerUI. Known GUIDs are Win32Apps and WinGetApps by default.
With -Online option also Powershell scripts, Proactive Remediation scripts and custom Compliance script will get name shown in UI.
Often this parameter helps a lot debugging log entries in LogViewerUI
.PARAMETER LongRunningPowershellNotifyThreshold
Threshold (seconds) after Timeline report will show warning message for long running Powershell scripts. Default value is 180 seconds.
.PARAMETER ExportTextFileName
Export Timeline information and possible Powershell script error to text file.
This expects either text filename or fullpath to textfile.
.PARAMETER FindAllLongRunningPowershellScripts
Poweruser option to try to find all long running Powershell scripts over threshold which default is 180 seconds
This option could find long running scripts which we don't even have event in our report.
.PARAMETER DoNotOpenReportAutomatically
Do not open html report file automatically in browser
.PARAMETER DoNotDownloadClearTextRemediationScriptsToReport
Do not download Remediation scripts in clear text to the report
This could be used for security measures.
Note that PowerShell script is always shown in report ToolTip in clear text because clear text PowerShell script is in log files
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -AllLogEvents -AllLogFiles
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -AllLogEvents -ShowAllTimelineEvents
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ShowLogViewerUI
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ShowLogViewerUI -ConvertAllKnownGuidsToClearText
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -Today
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -LogStartDateTime "10.3.2023 5.00:00" -LogEndDateTime "11.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"
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ShowStdOutInReport
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ShowErrorsInReport
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ShowErrorsSummary
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ExportTextFileName ExportTextFile.txt
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -Online -ExportTextFileName C:\temp\ExportTextFile.txt
.EXAMPLE
.\Get-IntuneManagementExtensionDiagnostics.ps1 -AllLogEntries -AllLogFiles -ExportTextFileName C:\temp\ExportTextFile.txt
.EXAMPLE
Get-ChildItem "C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log" | .\Get-IntuneManagementExtensionDiagnostics.ps1 -AllLogEntries -Online
.INPUTS
Script accepts File object as input. This would be same than specifying Parameter -LogFile
.OUTPUTS
None
.NOTES
You can download current version of this script from PowershellGallery with command
Save-Script Get-IntuneManagementExtensionDiagnostics -Path ./
.LINK
https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,
HelpMessage = 'Enter Intune IME log file fullpath',
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[Alias("FullName")]
[String]$LogFile = $null,
[Parameter(Mandatory=$false,
HelpMessage = 'Enter Intune IME log files folder path',
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)]
[String]$LogFilesFolder = $null,
[Parameter(Mandatory=$false)]
[Switch]$Online,
[Parameter(Mandatory=$false,
HelpMessage = 'Enter Start DateTime for log entries (for example "10.3.2023 5:00:00")',
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)]
$LogStartDateTime = $null,
[Parameter(Mandatory=$false,
HelpMessage = 'Enter End DateTime for log entries (for example "11.3.2023 23.00:00")',
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)]
$LogEndDateTime = $null,
[Parameter(Mandatory=$false)]
[Switch]$ShowLogViewerUI,
[Parameter(Mandatory=$false)]
[Switch]$LogViewerUI,
[Parameter(Mandatory=$false)]
[Switch]$AllLogEntries=$true,
[Parameter(Mandatory=$false)]
[Switch]$LogEntriesSelectionUI,
[Parameter(Mandatory=$false)]
[Switch]$AllLogFiles=$true,
[Parameter(Mandatory=$false)]
[Switch]$LogFilesSelectionUI,
[Parameter(Mandatory=$false)]
[Switch]$Today,
[Parameter(Mandatory=$false)]
[Switch]$ShowAllTimelineEvents,
[Parameter(Mandatory=$false)]
[Switch]$ShowStdOutInReport,
[Parameter(Mandatory=$false)]
[Switch]$ShowErrorsInReport,
[Parameter(Mandatory=$false)]
[Switch]$ShowErrorsSummary,
[Parameter(Mandatory=$false)]
[Switch]$ConvertAllKnownGuidsToClearText,
[Parameter(Mandatory=$false,
HelpMessage = 'Threshold seconds to highlight long running Powershell scripts',
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[int]$LongRunningPowershellNotifyThreshold = 180,
[Parameter(Mandatory=$false,
HelpMessage = 'Enter text (.txt) filename to export info to',
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)]
[String]$ExportTextFileName=$null,
[Parameter(Mandatory=$false)]
[String]$ExportHTMLReportPath=$null,
[Parameter(Mandatory=$false)]
[Switch]$FindAllLongRunningPowershellScripts,
[Parameter(Mandatory=$false)]
[Switch]$DoNotOpenReportAutomatically,
[Parameter(Mandatory=$false)]
[Switch]$DoNotDownloadClearTextRemediationScriptsToReport
)
$ScriptVersion = "3.3"
$TimeOutBetweenGraphAPIRequests = 300
Write-Host "Get-IntuneManagementExtensionDiagnostics.ps1 $ScriptVersion" -ForegroundColor Cyan
Write-Host "Author: Petri.Paavola@yodamiitti.fi / Microsoft MVP - Windows and Intune"
Write-Host ""
$ExportHTML=$True
# Make script to not show start selection UIs by default
# This should be fixed in the code
# but this was quicker hack to make script silent by default
$AllLogEntries=$true
$AllLogFiles = $true
if($LogEntriesSelectionUI) {
$AllLogEntries=$false
}
if($LogFilesSelectionUI) {
$AllLogFiles = $false
}
# Show selection UIs with LogViewerUI because we would like to
# limit as less as events possible to save memory and speed up Out-GridView
# With LogViewerUI show Events selection UI always
# With LogViewerUI show file selection UI always
if($ShowLogViewerUI -or $LogViewerUI) {
$LogEntriesSelectionUI = $true
$LogFilesSelectionUI = $true
$AllLogEntries=$false
$AllLogFiles = $false
Write-Host "Parameter -ShowLogViewerUI selected. Script will show log file and event selection UIs."
}
# Set variables automatically 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'
# Do not open HTML report to browser
$DoNotOpenReportAutomatically = $true
if((-not $LogFilesFolder) -or (-not $LOGFile)) {
Write-Host "Configuring parameters automatically"
Write-Host "Selected: All log files from default Intune IME logs folder"
Write-Host "Selected: Do not open HTML report automatically"
Write-Host
$LogFilesFolder = 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs'
if(-not (Test-Path $LogFilesFolder)) {
Write-Host "Log folder does not exist yet: $LogFilesFolder"
Write-Host "Try again in a moment..." -ForegroundColor Yellow
Write-Host ""
Exit 0
}
}
# Save Computer Name
$ComputerNameForReport = $env:ComputerName
# Process all found supported log files
# This will not show file selection UI
$AllLogFiles=$True
# Process all log entries
# This will not show time selection UI
$AllLogEntries=$True
# Show all entries in Timeline
# This especially useful if some script or application hangs for a long time
# so then you can see start entry for that script or application and you know what is current running Intune deployment
$ShowAllTimelineEvents=$True
# Do not download Remediation scripts in clear text to Report
# Reason is that many may not event know that script did also html report
# In OOBE usually the console view is enough and html file may be left to the device disk
$DoNotDownloadClearTextRemediationScriptsToReport=$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
}
}
# Hashtable with ids and names
$IdHashtable = @{}
# Save timeline objects to this List
$observedTimeline = [System.Collections.Generic.List[PSObject]]@()
# Save application download statistics to this list
$ApplicationDownloadStatistics = [System.Collections.Generic.List[PSObject]]@()
# Save filtered oud applications to to this list
$ApplicationAssignmentFilterApplied = [System.Collections.Generic.List[PSObject]]@()
# Save targeted app policy snapshots for the Targeted Applications table
$TargetedAppPolicySnapshots = [System.Collections.Generic.List[PSObject]]@()
# TimeLine entry index
# This might be used in HTML table for sorting entries
$Script:observedTimeLineIndexToHTMLTable=0
################ Functions ################
# 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] $type,
[Parameter(Mandatory=$false)] [String] $intent,
[Parameter(Mandatory=$false)] [String] $detail,
[Parameter(Mandatory=$false)] $seconds,
[Parameter(Mandatory=$false)] [String] $logEntry,
[Parameter(Mandatory=$false)] [String] $color,
[Parameter(Mandatory=$false)] [String] $DetailToolTip
)
$Script:observedTimeLineIndexToHTMLTable++
# Round seconds to full seconds
if($seconds -and ($seconds -as [double]) -ne $null) {
$seconds = [math]::Round($seconds)
}
$observedTimeline.add([PSCustomObject]@{
'Index' = $Script:observedTimeLineIndexToHTMLTable
'Date' = $date
'Status' = $status
'Type' = $type
'Intent' = $intent
'Detail' = $detail
'Seconds' = $seconds
'LogEntry' = $logEntry
'Color' = $color
'DetailToolTip' = $DetailToolTip
})
}
Function Get-AppIntent {
Param(
$AppId
)
$intent = 'Unknown Intent'
if($AppId) {
if($IdHashtable.ContainsKey($AppId)) {
$AppPolicy=$IdHashtable[$AppId]
if($AppPolicy.Intent) {
Switch ($AppPolicy.Intent)
{
0 { $intent = 'Not Targeted' }
1 { $intent = 'Available Install' }
3 { $intent = 'Required Install' }
4 { $intent = 'Required Uninstall' }
default { $intent = 'Unknown Intent' }
}
}
}
}
return $intent
}
Function Get-AppIntentNameForNumber {
Param(
$IntentNumber
)
Switch ($IntentNumber)
{
0 { $intent = 'Not Targeted' }
1 { $intent = 'Available Install' }
3 { $intent = 'Required Install' }
4 { $intent = 'Required Uninstall' }
default { $intent = 'Unknown Intent' }
}
return $intent
}
Function Get-AppDetectionResultForNumber {
Param(
$DetectionNumber
)
Switch ($DetectionNumber)
{
0 { $DetectionState = 'Unknown' }
1 { $DetectionState = 'Detected' }
2 { $DetectionState = 'Not Detected' }
3 { $DetectionState = 'Unknown' }
4 { $DetectionState = 'Unknown' }
5 { $DetectionState = 'Unknown' }
default { $DetectionState = 'Unknown' }
}
return $DetectionState
}
Function Get-AppName {
Param(
$AppId
)
$AppName = $null
if($AppId) {
if($IdHashtable.ContainsKey($AppId)) {
$AppPolicy=$IdHashtable[$AppId]
if($AppPolicy.Name) {
$AppName = $AppPolicy.Name
}
}
}
return $AppName
}
Function Get-AppType {
Param(
$AppId
)
$AppType = 'App'
if($AppId) {
if($IdHashtable.ContainsKey($AppId)) {
$AppPolicy=$IdHashtable[$AppId]
if($AppPolicy.InstallerData) {
# This should be New Store App
$AppType = 'WinGetApp'
} else {
# This should be Win32App
$AppType = 'Win32App'
}
}
}
return $AppType
}
Function Convert-AppDetectionValuesToHumanReadable {
Param(
$DetectionRulesObject
)
# Object has DetectionType and DetectionText objects
# Object is array of objects
<#
[
{
"DetectionType": 2,
"DetectionText": {
"Path": "C:\\Program Files (x86)\\Foo",
"FileOrFolderName": "bar.exe",
"Check32BitOn64System": true,
"DetectionType": 1,
"Operator": 0,
"DetectionValue": null
}
}
]
#>
foreach($DetectionRule in $DetectionRulesObject) {
# Change DetectionText properties values to text
# DetectionType: Registry
if($DetectionRule.DetectionType -eq 0) {
# Registry Detection Type values
# https://learn.microsoft.com/en-us/graph/api/resources/intune-apps-win32lobappregistrydetectiontype?view=graph-rest-beta
Switch ($DetectionRule.DetectionText.DetectionType) {
0 { $DetectionRule.DetectionText.DetectionType = 'Not configure' }
1 { $DetectionRule.DetectionText.DetectionType = 'Value exists' }
2 { $DetectionRule.DetectionText.DetectionType = 'Value does not exist' }
3 { $DetectionRule.DetectionText.DetectionType = 'String comparison' }
4 { $DetectionRule.DetectionText.DetectionType = 'Integer comparison' }
5 { $DetectionRule.DetectionText.DetectionType = 'Version comparison' }
}
# Registry Detection Operation values
# https://learn.microsoft.com/en-us/graph/api/resources/intune-apps-win32lobappruleoperator?view=graph-rest-beta
Switch ($DetectionRule.DetectionText.Operator) {
0 { $DetectionRule.DetectionText.Operator = 'Not configured' }
1 { $DetectionRule.DetectionText.Operator = 'Equals' }
2 { $DetectionRule.DetectionText.Operator = 'Not equal to' }
4 { $DetectionRule.DetectionText.Operator = 'Greater than' }
5 { $DetectionRule.DetectionText.Operator = 'Greater than or equal to' }
8 { $DetectionRule.DetectionText.Operator = 'Less than' }
9 { $DetectionRule.DetectionText.Operator = 'Less than or equal to' }
}
}
# DetectionType: File
if($DetectionRule.DetectionType -eq 2) {
# File Detection Type values
# https://learn.microsoft.com/en-us/graph/api/resources/intune-apps-win32lobappfilesystemdetectiontype?view=graph-rest-beta
Switch ($DetectionRule.DetectionText.DetectionType) {
0 { $DetectionRule.DetectionText.DetectionType = 'Not configure' }
1 { $DetectionRule.DetectionText.DetectionType = 'File or folder exists' }
2 { $DetectionRule.DetectionText.DetectionType = 'Date modified' }
3 { $DetectionRule.DetectionText.DetectionType = 'Date created' }
4 { $DetectionRule.DetectionText.DetectionType = 'String (version)' }
5 { $DetectionRule.DetectionText.DetectionType = 'Size in MB' }
6 { $DetectionRule.DetectionText.DetectionType = 'File or folder does not exist' }
}
# File Detection Operator values
# https://learn.microsoft.com/en-us/graph/api/resources/intune-apps-win32lobappdetectionoperator?view=graph-rest-beta
Switch ($DetectionRule.DetectionText.Operator) {
0 { $DetectionRule.DetectionText.Operator = 'Not configured' }
1 { $DetectionRule.DetectionText.Operator = 'Equals' }
2 { $DetectionRule.DetectionText.Operator = 'Not equal to' }
4 { $DetectionRule.DetectionText.Operator = 'Greater than' }
5 { $DetectionRule.DetectionText.Operator = 'Greater than or equal to' }
8 { $DetectionRule.DetectionText.Operator = 'Less than' }
9 { $DetectionRule.DetectionText.Operator = 'Less than or equal to' }
}
}
# DetectionType: Custom script
if($DetectionRule.DetectionType -eq 3) {
# Convert base64 script to clear text
#$DetectionRule.DetectionText.ScriptBody
# Decode Base64 content
$b = [System.Convert]::FromBase64String("$($DetectionRule.DetectionText.ScriptBody)")
$DetectionRule.DetectionText.ScriptBody = [System.Text.Encoding]::UTF8.GetString($b)
}
<#
# Change DetectionType value to text
Switch ($DetectionRule.DetectionType) {
0 { $DetectionRule.DetectionType = 'Registry' }
1 { $DetectionRule.DetectionType = 'MSI' }
2 { $DetectionRule.DetectionType = 'File' }
3 { $DetectionRule.DetectionType = 'Custom script' }
default { $DetectionRule.DetectionType = $DetectionRule.DetectionType }
}
#>
# Add new property with DetectionType value as text
Switch ($DetectionRule.DetectionType) {
0 { $DetectionRule | Add-Member -MemberType noteProperty -Name DetectionTypeAsText -Value 'Registry' }
1 { $DetectionRule | Add-Member -MemberType noteProperty -Name DetectionTypeAsText -Value 'MSI' }
2 { $DetectionRule | Add-Member -MemberType noteProperty -Name DetectionTypeAsText -Value 'File' }
3 { $DetectionRule | Add-Member -MemberType noteProperty -Name DetectionTypeAsText -Value 'Custom script' }
default { $DetectionRule | Add-Member -MemberType noteProperty -Name DetectionTypeAsText -Value $DetectionRule.DetectionType }
}
}
return $DetectionRulesObject
}
function Invoke-MgGraphRequestGetAllPages {
param (
[Parameter(Mandatory = $true)]
[String]$uri
)
$MgGraphRequest = $null
$AllMSGraphRequest = $null
Start-Sleep -Milliseconds $TimeOutBetweenGraphAPIRequests
try {
# Save results to this variable
$allGraphAPIData = @()
do {
$MgGraphRequest = $null
$MgGraphRequest = Invoke-MgGraphRequest -Uri $uri -Method 'Get' -OutputType PSObject -ContentType "application/json"
if($MgGraphRequest) {
# Test if object has attribute named Value (whether value is null or not)
#if((Get-Member -inputobject $MgGraphRequest -name 'Value' -Membertype Properties) -and (Get-Member -inputobject $MgGraphRequest -name '@odata.context' -Membertype Properties)) {
if(Get-Member -inputobject $MgGraphRequest -name 'Value' -Membertype Properties) {
# Value property exists
$allGraphAPIData += $MgGraphRequest.Value
# Check if we have value starting https:// in attribute @odate.nextLink
# and check that $Top= parameter was NOT used. With $Top= parameter we can limit search results
# but that almost always results .nextLink being present if there is more data than specified with top
# If we specified $Top= ourselves then we don't want to fetch nextLink values
#
# So get GraphAllPages if there is valid nextlink and $Top= was NOT used in url originally
if (($MgGraphRequest.'@odata.nextLink' -like 'https://*') -and (-not ($uri.Contains('$top=')))) {
# Save nextLink url to variable and rerun do-loop
$uri = $MgGraphRequest.'@odata.nextLink'
Start-Sleep -Milliseconds $TimeOutBetweenGraphAPIRequests
# Continue to next round in Do-loop
Continue
} else {
# We dont have nextLink value OR
# $top= exists so we return what we got from first round
#return $allGraphAPIData
$uri = $null
}
} else {
# Sometimes we get results without Value-attribute (eg. getting user details)
# We will return all we got as is
# because there should not be nextLink page in this case ???
return $MgGraphRequest
}
} else {
# Invoke-MGGraphRequest failed so we return false
return $null
}
} while ($uri) # Always run once and continue if there is nextLink value
# We should not end here but just in case
return $allGraphAPIData
} catch {
Write-Error "There was error with MGGraphRequest with url $url!"
return $null
}
}
function Get-IntunePowershellScriptContentInCleartext {
Param(
[Parameter(Mandatory=$true)]
[String]$PowershellScriptPolicyId
)
# Check if we already have value
if($IdHashtable[$PowershellScriptPolicyId].scriptContentClearText) {
# Powershell script in clear text already exists in HashTable object
# So we can return it
return $IdHashtable[$PowershellScriptPolicyId].scriptContentClearText
} else {
# Property scriptContentClearText does NOT exist in HashTable object
# Download Powershell script in cleartext if -Online parameter has been specified
# PowerShell script is in clear text in IME log files so this is always shown
# And later version might get PowerShell script from IME log instead from Graph
if($Online) {
#Write-Verbose "Downloading Powershell script: $PowershellScriptPolicyId"
$uri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts/$PowershellScriptPolicyId"
$IntunePowershellScriptInformation = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($IntunePowershellScriptInformation) {
#Write-Verbose "Done" -ForegroundColor Green
if($IntunePowershellScriptInformation.scriptContent) {
Try {
# Convert Intune Powershell script base64 content to clear text
$b = [System.Convert]::FromBase64String("$($IntunePowershellScriptInformation.scriptContent)")
$IntuneScriptContentInClearText = [System.Text.Encoding]::UTF8.GetString($b)
} catch {
# Some fatal error converting base64 to cleartext
Write-Error "Error converting Intune Powershell script base64 to cleartext" -ForegroundColor Red
return 'N/A'
}
# Add new property to HashTable object if it doesn't already exist
if(-not $IdHashtable[$PowershellScriptPolicyId].scriptContentClearText) {
$IdHashtable[$PowershellScriptPolicyId] | Add-Member -MemberType noteProperty -Name scriptContentClearText -Value $IntuneScriptContentInClearText
} else {
$IdHashtable[$PowershellScriptPolicyId].scriptContentClearText = $IntuneScriptContentInClearText
}
return $IntuneScriptContentInClearText
} else {
# Did not get scriptContent information
# We should never get here if we got anything successfully from Graph API
Write-Verbose "Failed to download Powershell scriptContent property" -ForegroundColor Yellow
return 'N/A'
}
} else {
# Could not get Intune Powershell information from Intune
# Doing nothing
Write-Host "Failed to download Powershell script from Intune" -ForegroundColor Yellow
return 'N/A'
}
} else {
# -Online not selected and we didn't have value so return $null
return 'N/A'
}
}
# We should not get here
return 'N/A'
}
function Get-IntuneRemediationDetectionScriptContentInCleartext {
Param(
[Parameter(Mandatory=$true)]
[String]$ScriptPolicyId
)
# Check if we already have value
if($IdHashtable[$ScriptPolicyId].detectionScriptContentClearText) {
# Powershell script in clear text already exists in HashTable object
# So we can return it
return $IdHashtable[$ScriptPolicyId].detectionScriptContentClearText
} else {
# Property detectionScriptContent does NOT exist in HashTable object
# Download Remediation Detection script in cleartext if -Online parameter has been specified
# and -DoNotDownloadClearTextRemediationScriptsToReport is NOT specified
if($Online -and (-not $DoNotDownloadClearTextRemediationScriptsToReport)) {
#Write-Verbose "Downloading Remediation Detection script: $ScriptPolicyId"
$uri = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts/$ScriptPolicyId"
#Write-Verbose "URI: $uri"
$IntuneRemediationScriptInformation = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($IntuneRemediationScriptInformation) {
#Write-Verbose "Done" -ForegroundColor Green
if($IntuneRemediationScriptInformation.detectionScriptContent) {
Try {
# Convert Intune Remediation Detection script base64 content to clear text
$b = [System.Convert]::FromBase64String("$($IntuneRemediationScriptInformation.detectionScriptContent)")
$IntuneScriptContentInClearText = [System.Text.Encoding]::UTF8.GetString($b)
} catch {
# Some fatal error converting base64 to cleartext
Write-Error "Error converting Intune Remediation Detection script base64 to cleartext" -ForegroundColor Red
return 'N/A'
}
# Add new property to HashTable object if it doesn't already exist
if(-not $IdHashtable[$ScriptPolicyId].detectionScriptContentClearText) {
$IdHashtable[$ScriptPolicyId] | Add-Member -MemberType noteProperty -Name detectionScriptContentClearText -Value $IntuneScriptContentInClearText
} else {
$IdHashtable[$PowershellScriptPolicyId].detectionScriptContentClearText = $IntuneScriptContentInClearText
}
return $IntuneScriptContentInClearText
} else {
# Did not get detectionScriptContent information
# We should never get here if we got anything successfully from Graph API
Write-Verbose "Failed to download Remediation Detect detectionScriptContent property" -ForegroundColor Yellow
return 'N/A'
}
} else {
# Could not get Intune Remediation Detect information from Intune
# Doing nothing
Write-Host "Failed to download Remediation Detect script from Intune" -ForegroundColor Yellow
return 'N/A'
}
} else {
# -Online not selected and we didn't have value so return $null
return 'N/A'
}
}
# We should not get here
return 'N/A'
}
function Get-IntuneRemediationRemediateScriptContentInCleartext {
Param(
[Parameter(Mandatory=$true)]
[String]$ScriptPolicyId
)
# Check if we already have value
if($IdHashtable[$ScriptPolicyId].RemediateScriptContentClearText) {
# Powershell script in clear text already exists in HashTable object
# So we can return it
return $IdHashtable[$ScriptPolicyId].RemediateScriptContentClearText
} else {
# Property remediationScriptContent does NOT exist in HashTable object
# Download Remediation Remediate script in cleartext if -Online parameter has been specified
# and -DoNotDownloadClearTextRemediationScriptsToReport is NOT specified
if($Online -and (-not $DoNotDownloadClearTextRemediationScriptsToReport)) {
#Write-Verbose "Downloading Remediation Remediate script: $ScriptPolicyId"
$uri = "https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts/$ScriptPolicyId"
$IntuneRemediationScriptInformation = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($IntuneRemediationScriptInformation) {
#Write-Verbose "Done" -ForegroundColor Green
if($IntuneRemediationScriptInformation.remediationScriptContent) {
Try {
# Convert Intune Remediation Detection script base64 content to clear text
$b = [System.Convert]::FromBase64String("$($IntuneRemediationScriptInformation.remediationScriptContent)")
$IntuneScriptContentInClearText = [System.Text.Encoding]::UTF8.GetString($b)
} catch {
# Some fatal error converting base64 to cleartext
Write-Error "Error converting Intune Remediation Detection script base64 to cleartext" -ForegroundColor Red
return 'N/A'
}
# Add new property to HashTable object if it doesn't already exist
if(-not $IdHashtable[$ScriptPolicyId].RemediateScriptContentClearText) {
$IdHashtable[$ScriptPolicyId] | Add-Member -MemberType noteProperty -Name RemediateScriptContentClearText -Value $IntuneScriptContentInClearText
} else {
$IdHashtable[$PowershellScriptPolicyId].RemediateScriptContentClearText = $IntuneScriptContentInClearText
}
return $IntuneScriptContentInClearText
} else {
# Did not get remediationScriptContent information
# We should never get here if we got anything successfully from Graph API
Write-Verbose "Failed to download Powershell remediationScriptContent property" -ForegroundColor Yellow
return 'N/A'
}
} else {
# Could not get Intune Remediation script information from Intune
# Doing nothing
Write-Host "Failed to download Remediation Remediate script from Intune" -ForegroundColor Yellow
return 'N/A'
}
} else {
# -Online not selected and we didn't have value so return $null
return 'N/A'
}
}
# We should not get here
return 'N/A'
}
function Get-IntuneCustomComplianceScriptContentInCleartext {
Param(
[Parameter(Mandatory=$true)]
[String]$ScriptPolicyId
)
# Check if we already have value
if($IdHashtable[$ScriptPolicyId].detectionScriptContentClearText) {
# Powershell script in clear text already exists in HashTable object
# So we can return it
return $IdHashtable[$ScriptPolicyId].detectionScriptContentClearText
} else {
# Property detectionScriptContent does NOT exist in HashTable object
# Download Custom Compliance script in cleartext if -Online parameter has been specified
# and -DoNotDownloadClearTextRemediationScriptsToReport is NOT specified
if($Online -and (-not $DoNotDownloadClearTextRemediationScriptsToReport)) {
#Write-Verbose "Downloading Custom Compliance script: $ScriptPolicyId"
$uri = "https://graph.microsoft.com/beta/deviceManagement/deviceComplianceScripts/$ScriptPolicyId"
$IntuneRemediationScriptInformation = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($IntuneRemediationScriptInformation) {
#Write-Verbose "Done" -ForegroundColor Green
if($IntuneRemediationScriptInformation.detectionScriptContent) {
Try {
# Convert Intune Custom Compliance script base64 content to clear text
$b = [System.Convert]::FromBase64String("$($IntuneRemediationScriptInformation.detectionScriptContent)")
$IntuneScriptContentInClearText = [System.Text.Encoding]::UTF8.GetString($b)
} catch {
# Some fatal error converting base64 to cleartext
Write-Error "Error converting Intune Custom Compliance script base64 to cleartext" -ForegroundColor Red
return 'N/A'
}
# Add new property to HashTable object if it doesn't already exist
if(-not $IdHashtable[$ScriptPolicyId].detectionScriptContentClearText) {
$IdHashtable[$ScriptPolicyId] | Add-Member -MemberType noteProperty -Name detectionScriptContentClearText -Value $IntuneScriptContentInClearText
} else {
$IdHashtable[$PowershellScriptPolicyId].detectionScriptContentClearText = $IntuneScriptContentInClearText
}
return $IntuneScriptContentInClearText
} else {
# Did not get detectionScriptContent information
# We should never get here if we got anything successfully from Graph API
Write-Verbose "Failed to download Custom Compliance detectionScriptContent property" -ForegroundColor Yellow
return 'N/A'
}
} else {
# Could not get Intune Custom Compliance information from Intune
# Doing nothing
Write-Host "Failed to download Custom Compliance script from Intune" -ForegroundColor Yellow
return 'N/A'
}
} else {
# -Online not selected and we didn't have value so return $null
return 'N/A'
}
}
# We should not get here
return 'N/A'
}
### HTML Report helper functions ###
function Fix-HTMLSyntax {
Param(
$html
)
$html = $html.Replace('<', '<')
$html = $html.Replace('>', '>')
$html = $html.Replace('"', '"')
return $html
}
function Fix-HTMLColumns {
Param(
$html
)
# Rename column headers
$html = $html -replace '
@odata.type
','
App type
'
$html = $html -replace '
displayname
','
App name
'
$html = $html -replace '
assignmentIntent
','
Assignment Intent
'
$html = $html -replace '
assignmentTargetGroupDisplayName
','
Target Group
'
$html = $html -replace '
assignmentFilterDisplayName
','
Filter name
'
$html = $html -replace '
FilterIncludeExclude
','
Filter Intent
'
$html = $html -replace '
publisher
','
Publisher
'
$html = $html -replace '
productVersion
','
Version
'
$html = $html -replace '
filename
','
Filename
'
$html = $html -replace '
createdDateTime
','
Created
'
$html = $html -replace '
lastModifiedDateTime
','
Modified
'
return $html
}
function return-ObjectPropertiesAsAlignedString {
Param(
$object
)
# Calculate the maximum length of property names for alignment
$maxWidth = ($object.PSObject.Properties.Name | Measure-Object -Maximum -Property Length).Maximum
# Build the formatted string
$result = $object.PSObject.Properties | ForEach-Object {
"{0,-$maxWidth} : {1}" -f $_.Name, $_.Value
}
# Convert array to single string
$output = $result -join "`n"
return $output
}
# endregion Functions
################ 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) {
if(-not (Test-Path $LogFilesFolder)) {
Write-Host "LogFilesFolder: $LogFilesFolder does not exist" -ForegroundColor Yellow
Write-Host "Script will exit" -ForegroundColor Yellow
exit 0
}
# Sort files: new files first and IntuneManagementExtension before AgentExecutor
# ORIGINAL
#$LogFiles = Get-ChildItem -Path $LogFilesFolder -Filter *.log | Where-Object { ($_.Name -like '*IntuneManagementExtension*.log') -or ($_.Name -like '*AgentExecutor*.log') -or ($_.Name -like '*AppWorkload*.log') } | Sort-Object -Property Name -Descending | Sort-Object -Property LastWriteTime -Descending
# Added -Depth 2 so you can point to Intune Diagnostics package root folder
$LogFiles = Get-ChildItem -Path $LogFilesFolder -Filter *.log -Depth 2 | Where-Object { ($_.Name -like '*IntuneManagementExtension*.log') -or ($_.Name -like '*AgentExecutor*.log') -or ($_.Name -like '*AppWorkload*.log') } | Sort-Object -Property Name -Descending | Sort-Object -Property LastWriteTime -Descending
} else {
# Running report using local computer's Intune log files
# Save Computer Name
$ComputerNameForReport = $env:ComputerName
# 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') -or ($_.Name -like '*AppWorkload*.log') } | Sort-Object -Property Name -Descending | Sort-Object -Property LastWriteTime -Descending
$LogFiles = Get-ChildItem -Path 'C:\ProgramData\Microsoft\intunemanagementextension\Logs' -Filter *.log | Where-Object { ($_.Name -like '*IntuneManagementExtension*.log') -or ($_.Name -like '*AgentExecutor*.log') -or ($_.Name -like '*AppWorkload*.log') -or ($_.Name -like '*HealthScripts*.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) {
if($AllLogFiles) {
$SelectedLogFiles = $LogFiles
} else {
$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' = 'Last 7 days';
'LogStartDateTimeObject' = (Get-Date).AddDays(-7);
'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';
})
$LogStartEndTimeOutGridviewEntries.add([PSCustomObject]@{
'Log Start and End time' = 'First 24 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 Single
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) {
$GraphAuthenticationModule = $null
$MgContext = $null
# Test if we are in Enrollment Status Page (ESP) phase
# Detect defaultuser0 loggedon
if($env:UserName -eq 'defaultUser0') {
# Make sure we can connect
$GraphAuthenticationModule = Import-Module Microsoft.Graph.Authentication -PassThru -ErrorAction Ignore
if (-not $GraphAuthenticationModule) {
Write-Host "Installing module Microsoft.Graph.Authentication"
Install-Module Microsoft.Graph.Authentication -Force
$Success = $?
if($Success) {
Write-Host "Success`n" -ForegroundColor Green
Write-Host "Import module Microsoft.Graph.Authentication"
$GraphAuthenticationModule = Import-Module Microsoft.Graph.Authentication -PassThru -ErrorAction Ignore
if($GraphAuthenticationModule) {
# Module imported successfully
Write-Host "Success`n" -ForegroundColor Green
} else {
# Failed to import module
Write-Host "Failed to import module! Skip downloading script names...`n" -ForegroundColor Red
}
} else {
Write-Host "Failed to install module! Skip downloading script names...`n" -ForegroundColor Red
}
}
if($GraphAuthenticationModule) {
Write-Host "Connect to Microsoft Graph API"
$scopes = "DeviceManagementConfiguration.Read.All"
$MgGraph = Connect-MgGraph -scopes $scopes
$Success = $?
if ($Success -and $MgGraph) {
Write-Host "Success`n" -ForegroundColor Green
# Get MgGraph session details
$MgContext = Get-MgContext
if($MgContext) {
$TenantId = $MgContext.TenantId
$AdminUserUPN = $MgContext.Account
Write-Host "Connected to Intune tenant:`n$TenantId`n$AdminUserUPN`n"
} else {
Write-Host "Error getting MgContext information! Skip downloading script names..." -ForegroundColor Red
}
} else {
Write-Host "Could not connect to Graph API! Skip downloading script names..." -ForegroundColor Red
}
} else {
Write-Host "Could not connect to Graph API! Skip downloading script names..." -ForegroundColor Red
}
} else {
Write-Host "Connecting to Intune using module Microsoft.Graph.Authentication"
Write-Host "Import module Microsoft.Graph.Authentication"
Import-Module Microsoft.Graph.Authentication
$Success = $?
if($Success) {
# Module imported successfully
Write-Host "Success`n" -ForegroundColor Green
} else {
Write-Host "Failed" -ForegroundColor Red
Write-Host "Make sure you have installed module Microsoft.Graph.Authentication"
Write-Host "You can install module without admin rights to your user account with command:`n`nInstall-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser" -ForegroundColor Yellow
Write-Host "`nor you can install machine-wide module with with admin rights using command:`nInstall-Module -Name Microsoft.Graph.Authentication"
Write-Host ""
Exit 1
}
Write-Host "Connect to Microsoft Graph API"
$scopes = "DeviceManagementConfiguration.Read.All"
$MgGraph = Connect-MgGraph -scopes $scopes
$Success = $?
if ($Success -and $MgGraph) {
Write-Host "Success`n" -ForegroundColor Green
# Get MgGraph session details
$MgContext = Get-MgContext
if($MgContext) {
$TenantId = $MgContext.TenantId
$AdminUserUPN = $MgContext.Account
Write-Host "Connected to Intune tenant:`n$TenantId`n$AdminUserUPN`n"
} else {
Write-Host "Error getting MgContext information!`nScript will exit!" -ForegroundColor Red
Exit 1
}
} else {
Write-Host "Could not connect to Graph API!" -ForegroundColor Red
Exit 1
}
}
# Download Intune scripts information if we have connection to Microsoft Graph API
if($MgContext) {
Write-Host "Download Intune Powershell scripts"
# Get PowerShell Scripts
$uri = 'https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts'
$AllIntunePowershellScripts = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($AllIntunePowershellScripts) {
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 }
# Save locally for debugging
#$AllIntunePowershellScripts | ConvertTo-Json -Depth 4 | Out-File -FilePath "$PSScriptRoot\AllIntunePowershellScripts.json" -Force
} else {
Write-Error "Did not find Intune Powershell scripts"
}
Start-Sleep -MilliSeconds 500
Write-Host "Download Intune Remediations Scripts"
# Get Proactive Remediations Scripts
$uri = 'https://graph.microsoft.com/beta/deviceManagement/deviceHealthScripts'
$AllIntuneProactiveRemediationsScripts = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($AllIntuneProactiveRemediationsScripts) {
Write-Host "Done" -ForegroundColor Green
# Add Name Property to object
$AllIntuneProactiveRemediationsScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name name -Value $_.displayName }
# Add policyType 6
# Which is Remediation script type
$AllIntuneProactiveRemediationsScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name policyType -Value 6 }
# Add all PowershellScripts to Hashtable
$AllIntuneProactiveRemediationsScripts | Foreach-Object { $id = $_.id; $value=$_; $IdHashtable["$id"] = $value }
# Save locally for debugging
#$AllIntuneProactiveRemediationsScripts | ConvertTo-Json -Depth 4 | Out-File -FilePath "$PSScriptRoot\AllIntuneProactiveRemediationsScripts.json" -Force
} else {
Write-Error "Did not find Intune Remediation scripts"
}
Start-Sleep -MilliSeconds 500
Write-Host "Download Intune Windows Device Compliance custom Scripts"
# Get Windows Device Compliance custom Scripts
$uri = 'https://graph.microsoft.com/beta/deviceManagement/deviceComplianceScripts'
$AllIntuneCustomComplianceScripts = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($AllIntuneCustomComplianceScripts) {
Write-Host "Done" -ForegroundColor Green
# Add Name Property to object
$AllIntuneCustomComplianceScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name name -Value $_.displayName }
# Add policyType 8
# Which is Custom Compliance script type
$AllIntuneCustomComplianceScripts | Foreach-Object { $_ | Add-Member -MemberType noteProperty -Name policyType -Value 8 }
# Add all PowershellScripts to Hashtable
$AllIntuneCustomComplianceScripts | Foreach-Object { $id = $_.id; $value=$_; $IdHashtable["$id"] = $value }
# Save locally for debugging
#$AllIntuneCustomComplianceScripts | ConvertTo-Json -Depth 4 | Out-File -FilePath "$PSScriptRoot\AllIntuneCustomComplianceScripts.json" -Force
} else {
Write-Error "Did not find Intune Windows custom Compliance scripts"
}
Start-Sleep -MilliSeconds 500
Write-Host "Download Intune Filters"
$uri = 'https://graph.microsoft.com/beta/deviceManagement/assignmentFilters?$select=*'
$AllIntuneFilters = Invoke-MgGraphRequestGetAllPages -Uri $uri
if($AllIntuneFilters) {
Write-Host "Done" -ForegroundColor Green
# Add all Filters to Hashtable
$AllIntuneFilters | Foreach-Object { $id = $_.id; $value=$_; $IdHashtable["$id"] = $value }
} else {
Write-Error "Did not find Intune filters"
}
} 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: