Powershell Get-WinEvent Truncates Message - powershell

I have the following Powershell code that counts the number of errors occurring per Provider, based on Error ID and Message. What I am after is getting the count of each unique message per computer, log, provider, id and level.
However the issue that I'm having is that when I export this information to .csv the event message is being truncated. Or rather, I'm not able to quite grasp what I need to do to get the entire event message.
Here is the code that grabs the event logs and exports it to .csv.
$computername = 'mycomputer'
$logs = "application", "system"
$info = get-winevent -ComputerName $computername -FilterHashTable #{LogName=$logs; Level=1,2,3} |
sort MachineName,LogName,ProviderName,{$_.LevelDisplayName},ID |
Group MachineName, LogName, ProviderName, ID, {$_.LevelDisplayName}, Count, Message |
select #{N="Computer";E={$_.name.split(',')[0]}},#{N="Log";E={$_.name.split(',')[1]}}, #{N="Provider";E={$_.name.split(',')[2]}}, #{N="Error ID";E={$_.name.split(',')[3]}},`
#{N="Type";E={$_.name.split(',')[4]}}, count, #{N="Message";E={$_.name.split(',')[5 |
Export-Csv -notypeinformation -Path c:\Test\events.csv
If I comment out
#| Export-Csv -notypeinformation -Path c:\Test\events.csv
And output
$info
I see the complete message. I also can see more of the message if I continue to expand the array indexes. For example I get more of the message if I do this:
, #{N="Message1";E={$_.name.split(',')[6]}}, #{N="Message2";E={$_.name.split(',')[7]}}
What do I need to code correct to get the entire message out in the
#{N="Message";E={$_.name.split(',')[5]}}
selection?

I think you Can take a much easier approach, with the built-in ConvertFrom-Csv, Also the script would be much more Readable
$info = get-winevent -ComputerName $computername -FilterHashTable #{LogName=$logs; Level=1,2,3} |
sort MachineName,LogName,ProviderName,{$_.LevelDisplayName},ID |
Group MachineName, LogName, ProviderName, ID, {$_.LevelDisplayName}, Count, Message
$Array = #()
$Header = "Computer","Log","Provider","ErrorID","Type","Message","Count"
Foreach ($item in $Info)
{
$Count = $Item.Count
$CSV = $item.Name | ConvertFrom-Csv -Delimiter "," -Header $Header
$CSV.Count = $Count
$Array += $CSV
}
$Array | Export-CSV C:\Test\Events.csv -NoTypeInformation

I'm guessing you're not truncating. You've just got messages with commas in them, and Split() doesn't have any way to know what's a delimiter and what's part of the message.
Well, I suppose you could specify:
#{N="Message";E={($_.name.split(',')[5..($_.name.split(',').Count - 1)]) -join ','}}
But, honestly, this feels like some mighty gigantic hoops to jump through.
What's wrong with $_.Group[0].<item> instead of $_.name.split(',')[<num>]? Except count, you're grouping by all the fields you're pulling, aren't you? So the value on the first event should, by definition, be the same on every other event in the same group, right?
Also, why {$_.LevelDisplayName} instead of just LevelDisplayName?
And I think you'll find that Message won't work very well in a CSV unless you strip line feeds and carriage returns.
Try this:
$ComputerName = 'ComputerName';
$Logs = 'Application', 'System';
$Info = Get-WinEvent -ComputerName $ComputerName -FilterHashTable #{LogName=$Logs; Level=1,2,3} `
| Sort-Object MachineName, LogName, ProviderName, LevelDisplayName, ID `
| Group-Object MachineName, LogName, ProviderName, ID, LevelDisplayName, Count, Message `
| Select-Object #{N="Computer";E={$_.Group[0].MachineName}}, `
#{N="Log"; E={$_.Group[0].LogName}}, `
#{N="Provider";E={$_.Group[0].ProviderName}}, `
#{N="Error ID";E={$_.Group[0].ID}}, `
#{N="Type"; E={$_.Group[0].LevelDisplayName}}, `
count, `
#{N="Message"; E={$_.Group[0].Message.Replace("`r",' ').Replace("`n",' ')}} `
| Export-Csv -NoTypeInformation -Path c:\Test\events.csv;

Related

Can't get multiple results

I have the following PowerShell code that should run and fetch the last login for the list of UPNs:
$UPNList = get-content c:\temp\users.txt
foreach ($User in $UPNList)
{
Start-Sleep -Milliseconds 1000
$result = Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$User'" -Top 1 | Select-Object CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress, TokenIssuerType, #{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
$result | Export-Csv -Path 'c:\temp\results.txt' -NoTypeInformation -Append
}
However, the "results.txt" file is empty when there is more than one (1) user in the input file.
If there's a single user, results are correctly returned.
How can I ensure the results are provided for all users?
Also, if the user did not log in at all, for example completely new account, how do I ensure that the UPN is still populated in the "results" file, but the rest of the details are empty?
Thank you.
Try not to write out to the output file in every iteration, but have PowerShell collect the objects you output inside the loop and then create the csv file:
# get the list of UPN's and skip empty lines
$UPNList = Get-Content -Path 'c:\test\users.txt' | Where-Object { $_ -match '\S' }
# loop through the list and collect the data in variable $result
$result = foreach ($User in $UPNList) {
# output the wanted data
Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$User'" -Top 1 |
Select-Object CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress,
TokenIssuerType, #{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
}
# now write the collected data to CSV file in one go
$result | Export-Csv -Path 'c:\test\results.csv' -NoTypeInformation
You may also try to do the filtering afterwards like below (could be slower than above code though)
# get the list of UPN's and skip empty lines
$UPNList = Get-Content -Path 'c:\test\users.txt' | Where-Object { $_ -match '\S' }
# filter with Where-Object afterwards and pipe through to the Export-Csv cmdlet
Get-AzureADAuditSignInLogs -All $true | Where-Object { $UPNList -contains $_.UserPrincipalName } |
Select-Object CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress,
TokenIssuerType, #{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}} |
Export-Csv -Path 'c:\test\results.csv' -NoTypeInformation
I tried to reproduce the same in my environment and got below results:
Initially, I checked with one user in users.txt file like this:
I ran the same script as you and got the response like below:
$UPNList = get-content c:\test\users.txt
foreach ($User in $UPNList)
{
Start-Sleep -Milliseconds 1000
$result = Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$User'" -Top 1 | Select-Object CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress, TokenIssuerType, #{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
$result | Export-Csv -Path 'c:\test\results.txt' -NoTypeInformation -Append
}
Output:
In results.txt file, I got the details of that user successfully like below:
Now I tried including more UPNs in users.txt file like below:
When I ran the same script, the results.txt file is empty as below:
Please note that, the response in results.txt file differs based on how you are giving input in users.txt file.
I tried changing the format of giving input in users.txt file like below:
Now, when I ran the script again, I got the details of those users successfully like below:
So, make sure to give input for users.txt file in correct format.
If the user did not log in at all, it's not possible to get their details using Get-AzureADAuditSignInLogs command.
Normally, you can make use of Get-AzureADUser command to get any user details.

Powershell script doesn't return event log results based on where clause

I am new to powershell and found examples to display messages from the event log. The -match and -eq property don't seem to be working. Below is my script which does not return any results. When I use -notmatch or -ne it displays correctly. I’ve tried many different variations of the code, but nothing has worked.
$sysEvent = Get-EventLog -LogName Application -Newest 300
$sysError = $sysEvent | where {$_entryType -match "Information"}
$sysError | Sort-Object EventID | Format-Table EventID, EntryType, Source, TimeWritten, Message -AutoSize

Work with ADComputer output in foreach loop

I want to output all hostnames within a network first with a foreach loop, in order (for example) to be able to ping them.
However with the following code I do not get any output in the console. The CSV file will be saved, but what is written in the loop will not be executed.
Does anyone know what the reason for this is and how I can solve it?
Import-Module activedirectory
Get-ADComputer -Filter * -Property * | Select Name | Export-CSV -Path $env:TEMP\ZZZEXPORTE.csv -NoTypeInformation -Encoding UTF8 | ForEach {
$computerName = $_.Name
Write-Host $computerName
Write-Host "----"
}
This occurs because Export-CSV does not output an object. Sometimes cmdlets like this have a -PassThru parameter which you can use to have an object passed along, but thats not the case with Export-CSV, they simply expect it to always be the last cmdlet in the pipeline.
You should instead do this:
$Computers = Get-ADComputer -Filter * -Property * | Select Name
$Computers | Export-CSV -Path $env:TEMP\ZZZEXPORTE.csv -NoTypeInformation -Encoding UTF8
$Computers | ForEach {
$computerName = $_.Name
Write-Host $computerName
Write-Host "----"
}
You could also do this:
Get-ADComputer -Filter * -Property * | Select Name | ForEach {
$computerName = $_.Name
Write-Host $computerName
Write-Host "----"
$_
} | Export-CSV -Path $env:TEMP\ZZZEXPORTE.csv -NoTypeInformation -Encoding UTF8
Noting that we have to add $_ to our ForEach-Object loop so that it outputs the current item to the pipeline, but that our Write-Host statements don't effect the pipeline because they are writing to the console only. To be honest though, this is a bit harder to follow for anyone else reading your code.

Select-Object selecting multiple objects but splitting the results and trimming them export to CSV

I have a script that reads from a list of computers, does a test connection. If it gets a reply from the Test-Connection, it does a if/else and builds a variable from a Get-ChildItem that reads from the suspect system C:\Users to see the last person to log in and select the name and last write time of the last user that logged in, and does a Write-Host of the results. If the Test-Conneciton didn't get a reply, it builds a variable of the system name with a "offline" statement and does a Write-Host to show it. Then, to finish it all off, it exports the results to a CSV.
Several things I am having issues with:
The variable for the Get-ChildItem does a Select-Object Name, LastWriteTime and builds the results into a variable. I would like to display those results (and of course export them into the CSV) separately instead of combined.
I need to trim the results or modify the script so it doesn't include extra characters in the results.
Here is an example of the results from the Get-ChildItem when the Test-Connection gets a reply from the machine (of course system name, IP address and user name have been changed to protect the innocent):
SYSTEMNAME #{IPV4Address=192.168.0.1} #{Name=John.Doe; LastWriteTime=03/08/2017 08:11:48}
The name and last write time are combined in the results and I need to split them out in the display and the CSV export as well as trim the extra characters. Something like this, where systemname, IP address, name and lastwritetime would all appear in their own cells in the CSV:
SYSTEMNAME 192.168.0.1 Name=John.Doe LastWriteTime=03/08/2017 08:11:48
Code below:
$computerList = Get-Content "D:\filelocation\LastLogIn.txt"
foreach ($Computername in $computerList) {
$ipreachable = Test-Connection $computerName -EA SilentlyContinue -Count 1 |
select IPV4address
$output =#()
if ($ipreachable) {
$LastUserLoggedIn = Get-ChildItem "\\$computername\c$\Users" -EA SilentlyContinue |
Sort-Object LastWriteTime -Descending |
Select-Object Name, LastWriteTime -First 1
$Details = "$LastUserLoggedIn"
Write-Host $computername $ipreachable $Details
} else {
$details = "$computerName Computer does not exisit or is offline"
Write-Host $Details
}
[PSCustomObject]#{
SystemName = $Computername
IPV4Address = $ipreachable
UserLogInDetails = $details
} | Export-Csv "D:\filelocation\lastuserreults.csv" -NoType -Append
}
Expand the IP address:
... | Select-Object -Expand IPV4Address | Select-Object -Expand IPAddressToString
and don't turn the directory information into a string:
$Details = "$LastUserLoggedIn"
Build your data object like this:
$obj = [PSCustomObject]#{
SystemName = $Computername
IPV4Address = $ipreachable
UserName = $LastUserLoggedIn.Name
Timestamp = $LastUserLoggedIn.LastWriteTime
}
So that you can display the information on the console as well as export it to a CSV:
$obj | Format-Table -AutoSize
$obj | Export-Csv 'C:\path\to\your.csv' -NoType -Append

Output data with no column headings using PowerShell

I want to be able to output data from PowerShell without any column headings. I know I can hide the column heading using Format-Table -HideTableHeaders, but that leaves a blank line at the top.
Here is my example:
get-qadgroupmember 'Domain Admins' | Select Name | ft -hide | out-file Admins.txt
How do I eliminate the column heading and the blank line?
I could add another line and do this:
Get-Content Admins.txt | Where {$_ -ne ""} | out-file Admins1.txt
But can I do this on one line?
In your case, when you just select a single property, the easiest way is probably to bypass any formatting altogether:
get-qadgroupmember 'Domain Admins' | foreach { $_.Name }
This will get you a simple string[] without column headings or empty lines. The Format-* cmdlets are mainly for human consumption and thus their output is not designed to be easily machine-readable or -parseable.
For multiple properties I'd probably go with the -f format operator. Something along the lines of
alias | %{ "{0,-10}{1,-10}{2,-60}" -f $_.COmmandType,$_.Name,$_.Definition }
which isn't pretty but gives you easy and complete control over the output formatting. And no empty lines :-)
A better answer is to leave your script as it was. When doing the Select name, follow it by -ExpandProperty Name like so:
Get-ADGroupMember 'Domain Admins' | Select Name -ExpandProperty Name | out-file Admins.txt
If you use "format-table" you can use -hidetableheaders
add the parameter -expandproperty after the select-object, it will return only data without header.
The -expandproperty does not work with more than 1 object. You can use this one :
Select-Object Name | ForEach-Object {$_.Name}
If there is more than one value then :
Select-Object Name, Country | ForEach-Object {$_.Name + " " + $Country}
Joey mentioned that Format-* is for human consumption. If you're writing to a file for machine consumption, maybe you want to use Export-*? Some good ones are
Export-Csv - Comma separated value. Great for when you know what the columns are going to be
Export-Clixml - You can export whole objects and collections. This is great for serialization.
If you want to read back in, you can use Import-Csv and Import-Clixml. I find that I like this better than inventing my own data formats (also it's pretty easy to whip up an Import-Ini if that's your preference).
First we grab the command output, then wrap it and select one of its properties. There is only one and its "Name" which is what we want. So we select the groups property with ".name" then output it.
to text file
(Get-ADGroupMember 'Domain Admins' |Select name).name | out-file Admins1.txt
to csv
(Get-ADGroupMember 'Domain Admins' |Select name).name | export-csv -notypeinformation "Admins1.csv"
$server = ('*')+(Read-Host -prompt "What Server Context?")+'*'
$Report = (Get-adcomputer -SearchBase "OU=serverou,DC=domain,DC=com" -filter {name -like $server} -SearchScope Subtree|select Name |Sort -Unique Name)
$report.Name | Out-File .\output\out.txt -Encoding ascii -Force
$Report
start notepad .\output\out.txt
Put your server SearchBase in above.
If you are not sure what your server OU is try this function below...
#Function Get-OSCComputerOU($Computername)
{
$Filter = "(&(objectCategory=Computer)(Name=$ComputerName))"
$DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher
$DirectorySearcher.Filter = $Filter
$SearcherPath = $DirectorySearcher.FindOne()
$DistinguishedName = $SearcherPath.GetDirectoryEntry().DistinguishedName
$OUName = ($DistinguishedName.Split(","))[1]
$OUMainName = $OUName.SubString($OUName.IndexOf("=")+1)
# $Obj = New-Object -TypeName PSObject -Property #{"ComputerName" = $ComputerName
# "BelongsToOU" = $OUMainName
# "Full" = $DistinguishedName}
$Obj = New-Object -TypeName PSObject -Property #{"Full" = $DistinguishedName}
$Obj
}
Makes sure to run the Get-OSCComputerOU Servername with a select -expandproperty Full filter.
Then just plug in the response to the Searchbase...
All thanks to http://www.jaapbrasser.com