Collecting event logs - powershell

I want to collect all the event logs since a defined timestamp. Here there is my chunck code:
$StartTime = (Get-Date).AddMinutes(-5)
$rawdata = Get-WinEvent -ListLog *
$eventlogs = #{}
foreach ($record in $rawdata) {
if ($record.LastWriteTime -gt $StartTime) {
$eventlogs[$record.GetHashCode()] = #{
'LogType' = $record.LogType
'Name' = $record.LogName
'Provider' = $record.OwningProviderName
'Path' = $record.LogFilePath
'Mode' = $record.LogMode
'Time' = $record.LastWriteTime
}
}
}
In addition to the above info, how can I retrieve a full extended description of each event log? I would like to avoid parsing each single .evtx file

Best way to do that is to use FilterXml parameter of Get-WinEvent.
You can actually create your filter by creating a filter using Event Viewer:
After that you copy > paste that in PowerShell. Also you will need to escape single quotation marks that surround time '2021-11-23T10:00:00.000Z' should become ''2021-11-23T10:00:00.000Z'' . NB, those are 2 * ', not double quote ".
Of course if you want to pass dates via variable, you can do that by using Get-Date
$filterXml = '
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">*[System[TimeCreated[#SystemTime>=''2021-11-23T10:00:00.000Z'' and #SystemTime<=''2021-11-23T14:03:00.999Z'']]]</Select>
</Query>
</QueryList>'
Get-WinEvent –FilterXml $filterXml

Not sure about reading extended descriptions, but you can read the event log with the following. Note logname allows wild cards, thus the *.
$time = (get-date).AddMinutes(-5)
Get-WinEvent –FilterHashtable #{logname='*'; starttime=$time}

I found a solution with a chunk of code like this one I have written:
# get events log
Write-Host -NoNewline "Retrieving events log... "
$logtime = (Get-Date).AddMinutes(-15)
$eventlogs.Clear()
$string = ''
$ErrorActionPreference= 'SilentlyContinue'
foreach ($logtype in ('System', 'Security','Application')) {
$rawdata = Get-WinEvent -FilterHashTable #{LogName=$logtype; StartTime=$logtime}
foreach ($record in $rawdata) {
$logkey = '[' + $record.LogName + ']_'
$logkey += Get-Date $record.TimeCreated -format "yyyy-MM-dd_HH-mm-ss-fff"
$record.Message -match "^(.*)\r+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$record.Message -match "^(.*)\n+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$string = $record.Message
}
}
$eventlogs[$logkey] = #{
'Name' = $record.LogName
'Time' = Get-Date $record.TimeCreated -format "yyyy-MM-dd_HH-mm-ss"
'Id' = $record.Id
'Message' = $string
'Type' = $record.LevelDisplayName
}
}
}
$ErrorActionPreference= 'Inquire'
$logfile = $logpath + '\' + $timestamp + '_EventLogs.csv'
'ID;LOGTYPE;NAME;TIME;MESSAGE' | Out-File $logfile -Encoding UTF8 -Append
foreach ($item in ($eventlogs.Keys | Sort-Object)) {
$new_record = #(
$eventlogs[$item].Id,
$eventlogs[$item].Type,
$eventlogs[$item].Name,
$eventlogs[$item].Time,
$eventlogs[$item].Message
)
$new_string = [system.String]::Join(";", $new_record)
$new_string | Out-File $logfile -Encoding UTF8 -Append
}
Write-Host -ForegroundColor Green 'DONE'
For convenience I will catch only the first line of the message. In order to get this I have adopted the following block:
$record.Message -match "^(.*)\r+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$record.Message -match "^(.*)\n+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$string = $record.Message
}
}
Such solution is due to the fact that some messages use \r\n, others only \n and still others I don't know. This last block does not satisfy me, even if it works.

Simply getting all recent logs, working within the windows api limits. For some reason it still says ProviderName in the headers.
$time = (get-date).AddMinutes(-5)
get-winevent -listlog * | % { Get-WinEvent #{logname=$_.logname;
starttime=$time} -ea 0} | ft -GroupBy logname
ProviderName: Microsoft-Windows-WMI-Activity/Operational
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
11/27/2021 10:52:54 AM 5857 Information Win32_WIN32_TERMINALSERVICE_Prov provider started with result c...
11/27/2021 10:52:54 AM 5857 Information CIMWin32 provider started with result code 0x0. HostProcess = w...
11/27/2021 10:51:59 AM 5857 Information StateMessageProvider provider started with result code 0x0. Hos...
% -parallel works even better in ps 7.
get-winevent -logname *
Get-WinEvent: Log count (458) is exceeded Windows Event Log API limit (256). Adjust filter to return less log names.
$time = (get-date).AddMinutes(-5)
get-winevent -listlog * | % -parallel { Get-WinEvent #{logname=$_.logname;
starttime=$using:time} -ea 0} | ft -GroupBy logname

Related

How to remove the entire row when any one field of CVS is null in powershell?

ProcessName UserName PSComputerName
AnyDesk NT-AUTORITÄT\SYSTEM localhost
csrss dc-01
ctfmon SAD\Administrator rdscb-01
SAD\Administrator srv-01
Remove the second and last row here
Based on your comments, if $data is read from a CSV file and contains custom objects, you can do the following:
$data | where { $_.PsObject.Properties.Value -notcontains $null -and $_.PsObject.Properties.Value -notcontains '' }
This will apply to every property and won't require supplying named properties.
There are more elegant ways, but, here is a kind of ugly answer, to illustrate this...
$Data = #"
"ProcessName","UserName","PSComputerName"
"AnyDesk","NT-AUTORITÄT\SYSTEM","localhost"
"csrss","","dc-01"
"ctfmon","SAD\Administrator","rdscb-01"
"","SAD\Administrator","srv-01"
"# | Out-File -FilePath 'D:\Temp\ProcData.csv'
$headers = (
(Get-Content -Path 'D:\Temp\ProcData.csv') -replace '"','' |
select -First 1
) -split ','
$data = Import-Csv -Path 'D:\Temp\ProcData.csv'
$colCnt = $headers.count
$lineNum = 0
:newline
foreach ($line in $data)
{
$lineNum++
for ($i = 0; $i -lt $colCnt; $i++)
{
# test to see if contents of a cell is empty
if (-not $line.$($headers[$i]))
{
Write-Warning -Message "$($lineNum): $($headers[$i]) is blank"
continue newline
}
}
"$($lineNum): OK"
# Perform other actions with good data
}
<#
# Results
1: OK
WARNING: 2: UserName is blank
3: OK
WARNING: 4: ProcessName is blank
#>

form the log file as a table

I have the following code
function ping-test($hosts) {
$conn = [System.Collections.ArrayList]#($hosts)
[int]$hostsamount = $conn.Count
foreach($co in $conn)
{
$check = Test-Connection $co -Count 3 -ErrorAction SilentlyContinue
$zugriffzeit = $check | select ResponseTime | Measure-Object ResponseTime -Average
$avg = [system.math]::Round($zugriffzeit.Average)
$zeit = Get-Date -Format HH:mm:ss
if($check -eq $null)
{
$pcre = Write-Output $co
$pire = Write-Output 'False'
$zure = $null
}
else
{
$pcre = Write-Output $co
$pire = Write-Output 'True'
$zure = Write-Output "$avg ms"
$zure = $zure.Replace(' ','')
}
[void]$re.Add([PSCustomObject] #{PCName=$pcre; PingResult=$pire; Zugriffszeit=$zure; Zeit=$zeit} )
**$log = "Host:{0} Ping: {1} Zugriffszeit: {2} Zeit: {3}" -f $pcre, $pire, $zure, $zeit
$log >> $logpath**
[int]$recount = $re.Count
[int]$eff = $recount - $hostsamount
try {
$re.RemoveRange(0, $eff)
}
catch{
Write-Host $Error
}
}
return $re
}
I use the following code(is in that function)
$log = "Host:{0} Ping: {1} Zugriffszeit: {2} Zeit: {3}" -f $pcre, $pire, $zure, $zeit
$log >> $logpath
the Question is: I want to form a table with the Colums "Host", "Ping", "Zugriffszeit", and "Zeit".
How can I form this table and save as a .txt or .log file somewhere??
Thx for the help
Use the same data as you're outputting!
To export to csv (if you want to re-use the data programmatically later):
$re |Export-Csv $logpath -NoTypeInformation
If you want to ever format it in a nice table again, it's as easy as:
Import-Csv $logpath |Format-Table
If you simply want nicely tabulated output in your log file:
$re |Format-Table |Out-String |Out-File $logfile
#MathiasR.Jessen showed import and export to csv.
But if you are bound to use .txt or .log files (As the aspect of your question says) then use PSCustomObject and Out-File
[PSCustomObject]#{
Host = $pcre
Ping = $pire
Zugriffszeit = $zure
Zeit = $zeit
} | Out-File $logpath
Later import it like:
Get-Content $logpath

Writing an output on a .txt file on Powershell

I found a little script to get all the local groups and members and it's working perfectly but I need to write the output on PowerShell.
Trap {"Error: $_"; Break;}
function EnumLocalGroup($LocalGroup) {
$Group = [ADSI]"WinNT://$strComputer/$LocalGroup,group"
"`r`n" + "Group: $LocalGroup"
$Members = #($Group.psbase.Invoke("Members"))
foreach ($Member In $Members) {
$Name = $Member.GetType().InvokeMember("Name", 'GetProperty', $Null, $Member, $Null)
$Name
}
}
$strComputer = gc env:computername
"Computer: $strComputer"
$computer = [adsi]"WinNT://$strComputer"
$objCount = ($computer.PSBase.Children | Measure-Object).Count
$i = 0
foreach ($adsiObj in $computer.PSBase.Children) {
switch -regex ($adsiObj.PSBase.SchemaClassName) {
"group" {
$group = $adsiObj.Name
EnumLocalGroup $group
}
}
$i++
}
I already tried this:
function EnumLocalGroup($LocalGroup) | Out-File -FilePath "E:\PS\Malik\group.txt"
But the code won't start if I do that. I also tried to use this whole Out-File line at the end of the code after the } but doesn't work either and this is the only solution I find on Internet.
If you want to incorporate logging into a function you need to put it into the function body, e.g.
function EnumLocalGroup($LocalGroup) {
....
$foo = 'something'
$foo # output returned by function
$foo | Add-Content 'log.txt' # output to log file
...
}
or
function EnumLocalGroup($LocalGroup) {
...
$foo = 'something'
$foo | Tee-Object 'log.txt' -Append # output goes to log file and StdOut
...
}
Otherwise you have to do the logging when you call the function:
EnumLocalGroup $group | Add-Content 'C:\log.txt'

powershell script that pulls WMIC data from remote workstations

I am looking for a Powershell script that queries remote workstations for installed Windows updates looking for specific HotFixes (6 of them) and then reports back which ones (if any) are installed.
Query would get the remote host's name from a text file.
I found this script on Microsoft's site and tried to alter it, but I have no where near the skills needed to it.
Function Get-MSHotfix
{
$outputs = Invoke-Expression "wmic qfe list"
$outputs = $outputs[1..($outputs.length)]
foreach ($output in $Outputs) {
if ($output) {
$output = $output -replace 'y U','y-U'
$output = $output -replace 'NT A','NT-A'
$output = $output -replace '\s+',' '
$parts = $output -split ' '
if ($parts[5] -like "*/*/*") {
$Dateis = [datetime]::ParseExact($parts[5], '%M/%d/yyyy',[Globalization.cultureinfo]::GetCultureInfo("en-US").DateTimeFormat)
} elseif (($parts[5] -eq $null) -or ($parts[5] -eq ''))
{
$Dateis = [datetime]1700
}
else {
$Dateis = get-date([DateTime][Convert]::ToInt64("$parts[5]", 16))-Format '%M/%d/yyyy'
}
New-Object -Type PSObject -Property #{
KBArticle = [string]$parts[0]
Computername = [string]$parts[1]
Description = [string]$parts[2]
FixComments = [string]$parts[6]
HotFixID = [string]$parts[3]
InstalledOn = Get-Date($Dateis)-format "dddd d MMMM yyyy"
InstalledBy = [string]$parts[4]
InstallDate = [string]$parts[7]
Name = [string]$parts[8]
ServicePackInEffect = [string]$parts[9]
Status = [string]$parts[10]
}
}
}
}
Here's something I wrote up for a very similar situation:
ForEach($Server in $ServerList){
$QFE=Get-WmiObject Win32_QuickFixEngineering -ComputerName $Server
$IDRX = 3188732,3188743,3192392 -join '|'
$QFE|?{$_.HotFixId -match $IDRX}
}
Mind you, I was looking for QFEs 3188732, 3188743, and 3192392, but you could easily modify that line for the ones you are looking for.

Undesired Newlines in Output from AD Search

I am having an issue with the out put; I wanted a second look to see another way of getting my output to not have return or new lines in it.
Could someone take a look for me please?
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://OU=Workstations");
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher;
$objSearcher.SearchRoot = $objDomain;
$objSearcher.PageSize = 100000;
$objSearcher.SearchScope = "Subtree";
$dateMonth = Get-Date -Format "MM";
$dateDay = Get-Date -Format "dd";
$dateYear = Get-Date -Format "yyyy";
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll();
foreach ($objResult in $colResults)
{
$objItem = $objResult.Properties;
$computer = $objItem.name | Select-String -Pattern 'NSC';
Write-Host $computer;
#Add-Content "C:\PowerShell\Reports\Computer Report - $dateMonth-$dateDay-$dateYear.csv" "$computer";
}
Example output:
NSCNPR02
NSCNPR05
NSCNPR01
NSCNPR03
Expected Output:
NSCNPR03
NSCNPR05
NSCNPR01
NSCNPR03
Try this:
$date = Get-Date -Format "MM-dd-yyyy"
$filename = "C:\PowerShell\Reports\Computer Report - $date.csv"
...
$objSearcher.FindAll() | ? {
$_.Properties.Name -like 'NSC*'
} | Add-Content $filename
The problem with your code is that
Add-Content "C:\Pow...csv" "$computer"
always adds a new line, even if $computer is $null. This behavior is due to the double quotes around $computer. Without them the problem wouldn't exist (but my suggestion would still be a cleaner solution ;).
Add-Content adds new lines.
As a workaround you may out text into the string and then write it into the file when the cycle is done.
Can you use the LDAP filtering instead of pulling all the records and then searching?
Instead of
"LDAP://OU=Workstations"
Use
"LDAP://OU=Workstations?(&(objectCategory=computer)(name=NSC*))"
For more information: http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx