There are a few PowerShell EventLogs and some files containing your PowerShell history and the commands, script blocks, etc., that you have used. This can be very helpful if your computer or servers are hacked at your office. Or, if you want to check things ๐ In this blog post, I will show you how to retrieve all those events locally and remotely and save those in an Excel sheet.
What are the PowerShell logs being collected?
In Windows, there are a few default PowerShell EventLogs available:
- Windows PowerShell
- PowerShellCore/Operational
- Microsoft-Windows-PowerShell/Admin
- Microsoft-Windows-PowerShell/Operational
- Microsoft-Windows-PowerShell-DesiredStateConfiguration-FileDownloadManager/Operational
- Microsoft-Windows-WinRM/Operational
These EventLogs contain information about scripts and script blocks used on the system or DSC information. (The script can modify the list to include other EventLogs if needed.)
There are also logs containing all the commands typed in your PowerShell sessions, which were recorded by the PSReadline module. The location of these files is C:\Users\{UserName}\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine.
How does the script work?
You can start the script and specify these parameters:
- -Computername You can specify one (W2K22DC, for example) or multiple separated by a comma (“W2K22DC, W2K22Member”, for example). If not specified, it will be your local system.
- -Filename You can specify the location of the Excel sheet in which the results will be saved. C:\Temp\Events.xlsx, for example. The FileName parameter is Mandatory.
- -PowerShellEventLogOnly When specified, the script will only export events from the EventLogs and not from the PSReadLine history.
- -PSReadLineHistoryOnly When specified, the script will only export the command lines found in the PSReadLine history files, not the EventLogs data.
The data is being saved to an Excel file in two tabs, one for the EventLog data and one for the PSReadline history. When you run it against multiple computers, you can filter those on the ComputerName column.
The script also checks if the specified Excel file is already present and will append data to it if that’s the case. Both tabs have a time and date suffix, including the time you ran it so that it won’t insert the same date twice.
If the script can’t access something, it will show that on screen as a warning message. Green lines are good, and grey ones are information (That it couldn’t find events in a certain log, for example)
Running the script
Below is the screen status output when I ran the script on my Windows Server 2022 Domain Controller and specified (using the -ComputerName parameter) to scan that and the Windows 2022 member server. The output is saved to c:\temp\events.csv (Using the -Filename parameter).

Screen output of the script
Example Excel file
Based on the two scanned servers above, the Excel file looks like this:

The PowerShell Eventlogs
Some of the messages are very long. To view it in the cell, you can double-click it or use the Wrap Text button to let it expand for you:

An expanded cell to view its contents
You can use the Filter on the ComputerName, for example, to display only a specific machine or to filter for a specific keyword:

Filtering computers or text
Below is a screenshot of the PSReadLine history; as you can see, it shows all your history. ๐ (This is dangerous if you have credentials, API keys in it, etc.)

PSReadLine History
The script
Below are the contents of the Get-PowerShellLogs script. Save it somewhere (c:\scripts\Get-PowerShellLogs.ps1, for example) and run it with the abovementioned parameters. For example:
C:\Scripts\Get-PowerShellLogs.ps1 -ComputerName w2k22member -Filename C:\Temp\events.xlsx -PowerShellEventlogOnly
Get-PowerShellLogs.ps1
#Requires -RunAsAdministrator [CmdletBinding(DefaultparameterSetname = 'All')] param( [parameter(Mandatory = $false)][string[]]$ComputerName = $env:COMPUTERNAME, [parameter(Mandatory = $true)][string]$Filename, [parameter(Mandatory = $false, parameterSetname = "EventLog")][switch]$PowerShellEventlogOnly, [parameter(Mandatory = $false, parameterSetname = "History")][switch]$PSReadLineHistoryOnly ) #Validate output $filename if (-not ($Filename.EndsWith('.xlsx'))) { Write-Warning ("Specified {0} filename does not end with .xlsx, exiting..." -f $Filename) return } #Check access to the path, and if the file already exists, append if it does or test the creation of a new one if (-not (Test-Path -Path $Filename)) { try { New-Item -Path $Filename -ItemType File -Force:$true -Confirm:$false -ErrorAction Stop | Out-Null Remove-Item -Path $Filename -Force:$true -Confirm:$false | Out-Null Write-Host ("Specified {0} filename is correct, and the path is accessible, continuing..." -f $Filename) -ForegroundColor Green } catch { Write-Warning ("Path to specified {0} filename is not accessible, correct or file is in use, exiting..." -f $Filename) return } } else { Write-Warning ("Specified file {0} already exists, appending data to it..." -f $Filename) } #Check if the ImportExcel module is installed. Install it if not if (-not (Get-Module -ListAvailable -Name ImportExcel)) { Write-Warning ("The ImportExcel module was not found on the system, installing now...") try { Install-Module -Name ImportExcel -SkipPublisherCheck -Force:$true -Confirm:$false -Scope CurrentUser -ErrorAction Stop Import-Module -Name ImportExcel -Scope Local -ErrorAction Stop Write-Host ("Successfully installed the ImportExcel module, continuing..") -ForegroundColor Green } catch { Write-Warning ("Could not install the ImportExcel module, exiting...") return } } else { try { Import-Module -Name ImportExcel -Scope Local -ErrorAction Stop Write-Host ("The ImportExcel module was found on the system, continuing...") -ForegroundColor Green } catch { Write-Warning ("Error importing the ImportExcel module, exiting...") return } } #List of PowerShell event logs to search in $Eventlogs = @( 'Windows PowerShell' 'PowerShellCore/Operational' 'Microsoft-Windows-PowerShell/Admin' 'Microsoft-Windows-PowerShell/Operational' 'Microsoft-Windows-PowerShell-DesiredStateConfiguration-FileDownloadManager/Operational' 'Microsoft-Windows-WinRM/Operational' ) #Set dateformat for the Excel tabs $date = Get-Date -Format ddMMyyhhmm #Loop through all computers specified in $ComputerName. If not specified, it will use your local computer foreach ($computer in $ComputerName | Sort-Object) { #Check if the computer is reachable if (Test-Path -Path "\\$($computer)\c$" -ErrorAction SilentlyContinue) { Write-Host ("`nComputer {0} is accessible, continuing..." -f $computer) -ForegroundColor Green #Eventlogs if (-not $PSReadLineHistoryOnly) { #Search all EventLogs specified in the $eventlogs variable $TotalEventLogs = foreach ($Eventlog in $Eventlogs) { $events = Get-WinEvent -LogName $Eventlog -ComputerName $computer -ErrorAction SilentlyContinue if ($events.count -gt 0) { Write-Host ("- Exporting {0} events from the {1} EventLog" -f $events.count, $Eventlog) -ForegroundColor Green foreach ($event in $events) { [PSCustomObject]@{ ComputerName = $computer EventlogName = $Eventlog TimeCreated = $event.TimeCreated EventID = $event.Id Message = $event.Message } } } else { Write-Host ("- No events found in the {0} Eventlog" -f $Eventlog) -ForegroundColor Gray } } #Create an Excel file and add an Eventlog tab containing the events for the computer if ($TotalEventLogs.count -gt 0) { try { $TotalEventLogs | Export-Excel -Path $Filename -WorksheetName "PowerShell_EventLog_$($date)" -AutoFilter -AutoSize -Append Write-Host ("Exported Eventlog data to {0}" -f $Filename) -ForegroundColor Green } catch { Write-Warning ("Error exporting Eventlog data to {0} (File in use?), exiting..." -f $Filename) return } } } #PSreadLineHistory if (-not $EventlogOnly) { #Search for all PSReadLine history files in all Windows User profiles on the system if (-not $PowerShellEventlogOnly) { Write-Host ("Checking for Users/Documents and Settings folder on {0}" -f $computer) -ForegroundColor Green try { if (Test-Path "\\$($computer)\c$\Users") { $UsersFolder = "\\$($computer)\c$\Users" } else { $UsersFolder = "\\$($computer)\c$\Documents and Settings" } } catch { Write-Warning ("Error finding Users/Documents and Settings folder on {0}. Exiting..." -f $computer) return } Write-Host ("Scanning for PSReadLine History files in {0}" -f $UsersFolder) -ForegroundColor Green $HistoryFiles = foreach ($UserProfileFolder in Get-ChildItem -Path $UsersFolder -Directory) { $list = Get-ChildItem -Path "$($UserProfileFolder.FullName)\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\*.txt" -ErrorAction SilentlyContinue if ($list.count -gt 0) { Write-Host ("- {0} PSReadLine history file(s) found in {1}" -f $list.count, $UserProfileFolder.FullName) -ForegroundColor Green foreach ($file in $list) { [PSCustomObject]@{ HistoryFileName = $file.FullName } } } else { Write-Host ("- No PSReadLine history file(s) found in {0}" -f $UserProfileFolder.FullName) -ForegroundColor Gray } } #Get the contents of the found PSReadLine history files on the system $TotalHistoryLogs = foreach ($file in $HistoryFiles) { $HistoryData = Get-Content -Path $file.HistoryFileName -ErrorAction SilentlyContinue if ($HistoryData.count -gt 0) { Write-Host ("- Exporting {0} PSReadLine History events from the {1} file" -f $HistoryData.count, $file.HistoryFileName) -ForegroundColor Green foreach ($line in $HistoryData) { if ($line.Length -gt 0) { [PSCustomObject]@{ ComputerName = $computer FileName = $File.HistoryFileName Command = $line } } } } else { Write-Warning ("No PSReadLine history found in the {0} file" -f $Log) } } #Create an Excel file and add the PSReadLineHistory tab containing PowerShell history if ($TotalHistoryLogs.count -gt 0) { try { $TotalHistoryLogs | Export-Excel -Path $Filename -WorksheetName "PSReadLine_History_$($date)" -AutoFilter -AutoSize -Append Write-Host ("Exported PSReadLine history to {0}" -f $Filename) -ForegroundColor Green } catch { Write-Warning ("Error exporting PSReadLine history data to {0} (File in use?), exiting..." -f $Filename) return } } } } } else { Write-Warning ("Specified computer {0} is not accessible, check permissions and network settings. Skipping..." -f $computer) continue } }
Download the script(s) from GitHub here
Hi Harm:
This is how I ran the script:
PS C:\WINDOWS\system32> C:\Users\bromberg.DESKTOP-V44B91G\Downloads\Get-PowerShellLogs.ps1 -Filename C:\Temp\events.xlsx -PowershellEventlog
and this is the error I got:
[D] Do not run [R] Run once [S] Suspend [?] Help (default is “D”): r
Specified C:\Temp\events.xlsx filename is correct, and the path is accessible, continuing…
The ImportExcel module was found on the system, continuing…
WARNING: Specified computer DESKTOP-V44B91G is not accessible, check permissions and network settings. Skipping…
What permissions do I need to give?
Dan
Hmmm, seems like a PowerShell Exeuction policy thing, before running you could try set-executionpolicy bypass first?
If I reply YES to the bypass option, that would change the policy only for this PS session, correct?
Thanks for your reply.
Correct, that’s only for that session and if you’re allowed to do that
I’m still having security issues but I do thank you for your replies; I’ll file this one away for a rainy day (plenty of them here in Florida!).
Dan
Same here in the Netherlands ๐ #rain
But I am curious why I’m being prompted for a file name when I already gave it in the command:
C:\WINDOWS\system32>C:\Users\bromberg.DESKTOP-V44B91G\Downloads\Get-PowerShellLogs.ps1 -Filename C:\Temp\events.xlsx -PowershellEventlog
Security warning
Run only scripts that you trust. While scripts from the internet can be useful, this script can potentially harm your
computer. If you trust this script, use the Unblock-File cmdlet to allow the script to run without this warning
message. Do you want to run C:\Users\bromberg.DESKTOP-V44B91G\Downloads\Get-PowerShellLogs.ps1?
[D] Do not run [R] Run once [S] Suspend [?] Help (default is “D”): r
cmdlet Get-PowerShellLogs.ps1 at command pipeline position 1
Supply values for the following parameters:
Filename:
I think it asks that because it got a prompt in the middle. You can use unblock-file get-powershelllogs.ps1 and rerun it again?
If I understood you correctly, you asked me to run the script as follows:
unblock-file get-powershelllogs.ps1
This results in no error messages but also no output. The cursor just moves to a new line next to a new command prompt.
The script seems to be complaining about my ComputerName. Where do I find that? When I run the systeminfo command I get a system name (in my case, DESKTOP-V44B91G). Is that the same as the ComputerName your script is expecting? If not, where can I find that?
When I run your script using all your parameters, except replacing your w2k22member with my DESKTOP-V44B91G as the computer name, I get this error message:
WARNING: Specified computer DESKTOP-V44B91G is not accessible, check permissions and network settings. Skipping…
Thanks,
Dan
If you don’t specify a computername, it will connect to your local computer. If you do specify a computername using the -ComputerName parameter, if must be a name that your can reach/ping and if the computer you must be able to authenticate it using the user that you’re running the script with. To avoid permission issues in that case, you can do a net use z: \computername\$ and enter the correct credentials so that you’re already authenticated to it. (Do a net use z: /d afterwards)
I am the only user of this PC so to avoid any security issues, I chose not to include the computername parameter since I want it to connect only to this PC. Therefore, I omitted the ComputerName parameter and this is how I entered the command:
.\Get-PowerShellLogs.ps1 -Filename C:\Temp\events.xlsx -PowerShellEventlog
and this is what PS returned:
Specified C:\Temp\events.xlsx filename is correct, and the path is accessible, continuing…
The ImportExcel module was found on the system, continuing…
WARNING: Specified computer DESKTOP-V44B91G is not accessible, check permissions and network settings. Skipping…
So even though I never specified DESKTOP-V$$B91G as the ComputerName, the script appears to use it as the default (just like you said).
Before I enter the ‘net user’ command as you suggested in your reply (net use z: \computername\$) , I have 3 basic questions:
1) I don’t have a ‘z’ drive, is that OK?
2) what did you mean by ‘enter the correct credentials’ – was that referring to the
‘z’ drive? If so, I have no other drives on this PC to use.
3) in the net command, you wrote ‘$’, should that have been ‘s’ instead?
Thanks!
Dan
You are administrator of the system otherwise it would have given an error about that.. I think that this is the problem:
“if (Test-Path -Path “\$($computer)\c$” -ErrorAction SilentlyContinue) ” It checks if there is a c$ default share before continuing… Does \localhost\c$ work if you use Start, Run and enter \localhost\c$ ? (You should see the contents of your c:\ drive) . Could you try and change “[parameter(Mandatory = $false)][string[]]$ComputerName = $env:COMPUTERNAME” to “[parameter(Mandatory = $false)][string[]]$ComputerName = ‘localhost” in the script and see if that works?
The net use command was, if you wanted to retrieve logs from another system, was to connect the pc and be authenticated to it before running the script. Credentials are the username/password of the system with enough permissions. And that should have been c$, forgot the c ๐
some few comments
In the presence test of the ImportExcel module, a command is missing. If module not present, installation and after the if…else it would be necessary to add Import-Module to load it
to avoid duplicates in the excel file, you could do something like this:
– Check if Excel file already exists. If yes, get the modification date.
– Then when you do your research in the eventLogs, you could use something like that :
$StartTime = (Get-Item -Path ‘C:\temp\0367-support-powershell.pdf’).LastWriteTime
$EndTime = Get-Date
Get-WinEvent -ComputerName $Computer -FilterHashtable @{
LogName = $EventLog
StartTime = $StartTime
EndTime = $EndTime
} -ErrorAction SilentlyContinue
for the PSReadLineHistory, maybe a way like:
– read the last line in the excel file (if existing of course)
– Look for this line in the PSreadlineHistory and take line+1 until the end to add to the excel export file.
It seems that this script is designed to run in interactive mode. Using the PS Module PSWriteColor, you could add indentation to the output in an easy way.
Regards
First of all, thanks for your feedback ๐ I started with a ComputerName_Tabname approach… And then you have the 30 character limit of an Excel tab and the 15 character computername… Perhaps I will change it to add a small date value to it to avoid having duplicates and still have the option to add to an existing Excel file. I didn’t use the PSWriteColor module before, I will look into that (For future posts perhaps).
I will start the date field addidtion now and see what else I have time for today ๐
Added a timestamp to the tabs and an Import-Module ImportExcel ๐ Updated the blogpost and GitHub to reflect that change