IntuneTT.psm1
########################################### # Get Uninstall Strings from Registry # ########################################### function Get-UninstallStrings { <# .SYNOPSIS Will try to collect all uninstall strings from registry. .NOTES This will not output any applications that do not have a displayname. #> Begin { $applications = @() $registryKeys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\" ) } Process { foreach ($key in $registryKeys) { $subKeys = Get-ChildItem $key $count = 0 foreach ($subkey in $subKeys) { $count++ Write-Progress -PercentComplete ($count / $subKeys.count * 100) ` -Status "Processing subkeys of $key" ` -Activity "Processing subkey $count of $($subKeys.Count)" $appInformation = Get-ItemProperty ( $subkey -replace "HKEY_LOCAL_MACHINE", "HKLM:" ) if($appInformation.displayname){ $applications += [PSCustomObject]@{ Name = $appInformation.displayname Version = $appInformation.DisplayVersion InstallDate = if ($appInformation.InstallDate) { [datetime]::ParseExact($appInformation.InstallDate, "yyyyMMdd", $null) } else { $appInformation.InstallDate } UninstallString = $appInformation.UninstallString MSIExecCommand = if ($appInformation.UninstallString -match "MsiExec.exe") { "MSIExec.exe " + ($appInformation.UninstallString -replace '/I|/X', '/x ' -replace "MsiExec.exe", "") + " /NORESTART" } else { "N\A" } Path = $appInformation.PSPath -replace "Microsoft.PowerShell.Core\\Registry::", "" } } } Write-Progress -Activity "Processing subkeys" -Completed } } End { return $applications } } #endRegion ####################################### # Get Running Process information # ####################################### function Get-RunningProcessInfo { <# .SYNOPSIS Will search the running processes and return the ones matching the searched for string. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$searchString, [Parameter()] [switch]$outDefault ) $gatheredProcesses = Get-CimInstance -ClassName Win32_Process | Where-Object { $_.name -match $searchString -or $_.CommandLine -match $searchString } if ($outDefault) { $gatheredProcesses | Format-List -Property ProcessId, Name, CommandLine } } #endRegion ###################################### # Install-RequiredMGGraphModules # ###################################### function Install-RequiredModule { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]]$InputValues ) Begin { $requiredModules = @( "Microsoft.Graph.Authentication", "Microsoft.Graph.DeviceManagement", "Microsoft.Graph.Beta.Devices.CorporateManagement", "Microsoft.Graph.Beta.DeviceManagement", "Microsoft.Graph.Compliance", "Microsoft.Graph.Users", "Microsoft.Graph.Groups" ) } Process { foreach ($module in $requiredModules) { if ( -not (Get-Module -Name $module -ErrorAction SilentlyContinue) ) { try { Write-Host "Installing Module $module" Install-Module -Name $module -Confirm:$false } catch { $_ } } } } End { return } } #endRegion ############################# # Collect MDM Diag Logs # ############################# function Get-AutopilotDiagnosticInfo { <# .SYNOPSIS Will collect the MDM Diag Logs and then parse them using the Get-AutopilotDiagnosticsCommunity script. #> Begin { $runTime = Get-Date -uFormat "%H-%M-%S" $outPutFilename = "mdmdiags-$runTime.txt" $diagFileName = "mdmdiags-$runTime.zip" $mdmDiagTool = "C:\windows\System32\MdmDiagnosticsTool.exe" $processArgs = "-area Autopilot -zip C:\Temp\IntuneTroubleshootingTool\AutopilotDiag\$diagFileName" } Process { # Collect Diagnostics # Write-Output "Collecting Autopilot Diagnostics" Start-Process $mdmDiagTool -ArgumentList $processArgs -NoNewWindow -Wait Start-Sleep -Seconds 15 if (Test-Path "C:\Temp\IntuneTroubleshootingTool\AutopilotDiag\$diagFileName") { Write-Output "Diagnostic Logs collected successfully" } else { Write-Output "Error collecting Diagnostic Logs, please try again." Return } #endRegion # Download Community Script # if (!(Get-InstalledScript Get-AutopilotDiagnosticsCommunity)) { try { Write-Output "Installing Get-AutopilotDiagnosticsCommunity script..." Install-Script -Name Get-AutopilotDiagnosticsCommunity -Force # Manually update path to save restarting session. $env:PATH += ";C:\Program Files\PowerShell\Scripts" } catch { Write-Output "Error Downloading Script." Return } } #endRegion # Run the Script # Write-Output "Exporting all data here, $outPutFilename" Get-AutopilotDiagnosticsCommunity.ps1 -ZIPFile "C:\Temp\IntuneTroubleshootingTool\AutopilotDiag\$diagFileName" -Online *>&1 | ` Tee-Object -FilePath "C:\Temp\IntuneTroubleshootingTool\AutopilotDiag\$outPutFilename" #endRegion } End { Write-Output "Completed Get-MDMDiagnostics flow" return } } #endRegion ###################### # Parse IME Logs # ###################### function ParseIMELogs { <# .SYNOPSIS Will attempt to parse the IME logs that you pass into it. .NOTES This does need teaking, MS have added new log files and some new formats. #> [CmdletBinding()] param ( [Parameter()] [string] $fileName ) Begin { $pattern = '<!\[LOG\[(?<Message>.*?)\]LOG\]!><time="(?<Time>[\d:.]+)" date="(?<Date>\d{1,2}-\d{1,2}-\d{4})"(?<Misc>.*?)>' $rawLogs = (Get-Content -Path $fileName -Raw) -join "`r`n" $matchedStrings = [regex]::Matches($rawLogs, $pattern) $nonMatchedStrings = [regex]::Matches($rawLogs, '^(?!.*$pattern).*') $matchedStringsArray = [System.Collections.Generic.List[PSCustomObject]]::new() $count = 0 } Process { foreach ($string in $matchedStrings) { $count++ Write-Progress -PercentComplete ($count / $matchedStrings.count * 100) ` -Status "Processing Matches" ` -Activity "Processing match $count of $($matchedStrings.Count)" $matchedStringsArray.Add([PSCustomObject]@{ Date = $string.Groups["Date"].Value Time = $string.Groups["Time"].Value Message = $string.Groups["Message"].Value Misc = $string.Groups["Misc"].Value }) } Write-Progress -Activity "Processing Matches" -Completed } End { if ($nonMatchedStrings.count -gt 0) { $nonMatchedStrings | Export-csv -path "$env:temp\IMENonMatchedStrings.csv" -NoTypeInformation Write-Output "Non-matching strings exported to $env:temp\IMENonMatchedStrings.csv" } return $matchedStringsArray.ToArray() } } #endRegion ################################### # Search for Strings in Files # ################################### function Find-StringInFile { <# .SYNOPSIS Will find the files that contain the string you parse it. #> param( [string]$searchString, [string]$folderpath = 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs' ) (Get-ChildItem -Path $folderpath | Where-Object { -not $_.PSIsContainer }).FullName | ForEach-Object { $file = $_ if ((Get-Content -Path $file | Select-String -SimpleMatch $searchString).Count -gt 0) { return $file } } } #endRegion ############################# # Get Win32 App Results # ############################# function Get-Win32AppReport { <# .SYNOPSIS Will attempt to install Get-Win32AppResults Script from PSGallery and run it. #> if (Get-InstalledScript -Name Get-Win32AppResult) { Get-Win32AppResult.ps1 } else { try { Install-Script -Name Get-Win32AppResult } catch { $_ } } } #endRegion ################################# # Check for pending Reboots # ################################# function Get-PendingReboot { <# .SYNOPSIS Will attempt to check for any pending reboots. #> $keys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update", "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" ) $rebootRequired = $false foreach ($key in $keys) { if (Test-Path $key) { $properties = Get-ItemProperty -Path $key -ErrorAction SilentlyContinue if ($properties -match "RebootPending|RebootRequired|PendingFileRenameOperations") { Write-Output "Reboot required due to: $key" $rebootRequired = $true } } } if (-not $rebootRequired) { Write-Output "No reboot required." } } #endregion ################################# # Get App Assignment Groups # ################################# function Get-WinAppAssignments { <# .SYNOPSIS Will attempt to install the Get-IntuneAppAssignments and run it. #> if (Get-InstalledScript -Name Get-IntuneAppAssignments -ErrorAction SilentlyContinue) { Get-IntuneAppAssignments } else { try { Install-Script -Name Get-IntuneAppAssignments } catch { $_ } } } #endRegion ################################### # Check Attestation Readiness # ################################### function Get-AttestationReadiness { <# .SYNOPSIS Will attempt to run the Autopilottestattestation script created by RudyOoms. https://www.powershellgallery.com/packages/Autopilottestattestation/1.0.0.34 #> Begin { $outPutFilename = "attestationtest-$(Get-Date -uFormat "%H-%M-%S").txt" # Check and import the module # if (!(Get-Module -ListAvailable -Name Autopilottestattestation -ErrorAction SilentlyContinue)) { Write-Host "Requred Module is not installed, installing now..." -ForegroundColor Yellow try { Install-Module -Name Autopilottestattestation -Scope CurrentUser } catch { $_ return } } } Process { Write-Host "Executing attestattion testing command..." -ForegroundColor Yellow # No try catch due to deprecated wmic commands, seems to break the try-catch process even with exception handling. Test-AutopilotAttestation *>&1 | Tee-Object -FilePath "C:\Temp\Wintune\Reports\$outPutFilename" } End { Write-Host "Exported console output to C:\Temp\Wintune\Reports\$outPutFilename" -ForegroundColor Yellow } } #endRegion ############################### # Collect MDM Diagnostics # ############################### function Get-AllMDMDiagnosticInfo { <# .SYNOPSIS Will collect all of the MDM Diag Logs. #> Begin { $outputPath = "C:\Temp\IntuneTroubleshootingTool\MDMDiags\Diagnostics-$(Get-Date -uFormat "%H-%M-%S").zip" $mdmDiagTool = "C:\windows\System32\MdmDiagnosticsTool.exe" $processArgs = "-area `"DeviceEnrollment;DeviceProvisioning;ManagementService;PushNotification;WnsProvider;Autopilot`" -zip $outputPath" } Process { # Collect Diagnostics # Write-Output "Collecting Autopilot Diagnostics" Start-Process $mdmDiagTool -ArgumentList $processArgs -NoNewWindow -Wait Start-Sleep -Seconds 5 if (Test-Path $outputPath) { Write-Output "Diagnostic Logs collected successfully" } else { Write-Output "Error collecting Diagnostic Logs, please try again." Return } #endRegion } End { Write-Output "Completed Get-MDMDiagnostics flow" return } } #endRegion ########################################## # Delete Win32App Keys from Registry # ########################################## function Remove-Win32AppKeys { <# .SYNOPSIS Will attempt to remove an app from the Win32App registry key paths, which forces the IME to update its status. This is akin to gpupdate for apps, essentially allowing you to force required and available apps to update their status. #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [string]$appId ) Begin { # Define the base registry key path and initialize variables $win32AppKeys = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps $removeTheString = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IntuneManagementExtension\\" $keysForDeletion = @() $propertiesForDeletion = @() } Process { # Iterate through each of the found registry keys foreach ($keyItem in $win32AppKeys) { $subkeys = Get-ChildItem -Path ($keyItem.PSPath -replace "Microsoft.PowerShell.Core\\", "") -Recurse foreach ($subkeyItem in $subkeys) { $currentKey = Split-Path $subkeyItem.Name -Leaf $getProperties = Get-ItemProperty ($subkeyItem.PSPath -replace "Microsoft.PowerShell.Core\\", "") $excludePSProperties = $getProperties.PSObject.Properties | Where-Object Name -notlike "PS*" # Check if the current key matches the appId if ($currentKey -like "$appId*") { Write-Output "Found Key: $( $subkeyItem.Name -replace $removeTheString )" $keysForDeletion += $subkeyItem.Name } # Check if any property names contain the appId elseif ($excludePSProperties.Name -contains $appId) { Write-Output "Found property under: $( $subkeyItem.Name -replace $removeTheString )" $propertiesForDeletion += [PSCustomObject]@{ Path = $subkeyItem.Name Name = $appId } } } } } End { # Confirm the deletion action with the user if ($PSCmdlet.ShouldProcess("Attempting to delete all references of the App ID, $appId, from the registry", $appId, "delete")) { $deletePrompt = Read-Host -Prompt "Happy to delete? (Y/N)" if ($deletePrompt -eq 'n') { Write-Output "Cancelling Delete operation" return } foreach ($keyItem in $keysForDeletion) { Remove-Item -Path ($keyItem -replace "HKEY_LOCAL_MACHINE", "HKLM:") -Confirm:$false -Recurse } foreach ($propItem in $propertiesForDeletion) { Remove-ItemProperty -Path ($propItem.Path -replace "HKEY_LOCAL_MACHINE", "HKLM:") -Name $propItem.Name } Write-Output "All references found have been deleted." } } } #endRegion |