form the log file as a table - powershell

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

Related

Collecting event logs

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

Formatting in PowerShell

I am getting the content of a JSON file into the variable $variable and filtering the $variable on a condition as in the code below and holding the filtered data in $logs. The output of echo $logs is as below.
0.842093077,
0.792955,
0.8910225,
0.8724875,
0.852885333,
0.774708,
0.987243333,
0.87078,
0.9565,
0.839393333
My question is how do i convert each of these decimal values to a percentage like 84.20 %, 79.20% and so on, so that when the mail goes out with the Percentage Utilisation field as a percentage value and not a decimal value?
DESIRED SAMPLE OUTPUT
Workspace Name : A NAME
Workspace Allowance : 100
Workspace Usage : 79.2955
Workspace Size Free : 20.7
**Percentage Utilization : 79.29**
Predicted Usage : 86.05585477
PRESENT OUTPUT
Workspace Name : A NAME
Workspace Allowance : 100
Workspace Usage : 79.2955
Workspace Size Free : 20.7
*Percentage Utilization : 0.792955*
Predicted Usage : 86.05585477
# Convert the CSV file into its JSON equivalent
import-csv "Workspacesize.csv" | ConvertTo-Json | Add-Content -Path "output.json" -Force -ErrorAction Stop
# Import email settings from config file
[xml]$ConfigFile = Get-Content "Settings.xml"
#Write-Output $ConfigFile
$emailTo = #(abc#gmail.com, xyz#gmail.com)
# Create email content
$smtpsettings = #{
From = $ConfigFile.Settings.EmailSettings.MailFrom
Subject = "TEST EMAIL"
SmtpServer = $ConfigFile.Settings.EmailSettings.SMTPServer
}
[String]$messagebody = ""
$variable=Get-Content -raw "output.json" | ConvertFrom-Json
$logs=$variable | Where-Object { [double]$_.'Percentage Utilization' -gt 0.75}
echo $logs
}
foreach ($log in $logs )
{
$messagebody = $messagebody + $log + "`r`n"
}
$messagebody = $logs | Out-String
$messagebody = '<pre>' + $messagebody + '</pre>'
#-------------------------------------------------
# Script
#-------------------------------------------------
try
{
Send-MailMessage #smtpsettings -To $emailTo -Body $messagebody -BodyAsHtml -Encoding utf8 -verbose -ErrorAction Stop
}
catch
{
Write-Warning $_.Exception.Message
}
# MOVE THE JSON & CSV FILE TO AN ARCHIVED LOCATION
Move-Item -Path $Jsonfile -Destination C:\Users\Siddhartha.S.Das2\Desktop -Force
Move-Item -Path $Csvfile -Destination C:\Users\Siddhartha.S.Das2\Desktop -Force
#-------------------------------------------------
# The End
#-------------------------------------------------
As commented, you can simplify the code by using the data from the CSV file and only if you really also need that in JSON format, convert that data and save it at the end of the script.
$CsvFile = 'C:\Somewhere\Workspacesize.csv'
$data = Import-Csv -Path $CsvFile
$logs = $data | Where-Object { [double]$_.'Percentage Utilization' -gt 0.75 }
# update the 'Percentage Utilization' property on each item
foreach ($item in $logs) {
$item.'Percentage Utilization' = [Math]::Round(([double]$item.'Percentage Utilization' * 100), 2)
}
# format the mesage body as monospaced formatted list output
$messagebody = '<pre>{0}</pre>' -f ($logs | Format-List | Out-String)
# Import email settings from config file
[xml]$ConfigFile = Get-Content "Settings.xml"
# BTW. it is better to use:
# $ConfigFile = ([System.XML.XMLDocument]::new()).Load('X:\Path\To\Settings.xml')
# because that way you'll get the encoding right.
# You need to load it using the absolute Full path and filename
# Create email splat hashtable
$smtpsettings = #{
To = 'abc#gmail.com', 'xyz#gmail.com'
From = $ConfigFile.Settings.EmailSettings.MailFrom
Subject = "TEST EMAIL"
SmtpServer = $ConfigFile.Settings.EmailSettings.SMTPServer
}
# try to send the email
try {
Send-MailMessage #smtpsettings -Body $messagebody -BodyAsHtml -Encoding utf8 -Verbose -ErrorAction Stop
}
catch {
Write-Warning $_.Exception.Message
}
# move the csv file to an archived location
Move-Item -Path $Csvfile -Destination 'C:\Users\Siddhartha.S.Das2\Desktop' -Force
# if you really also need a json file from the data retrieved from the Csv, convert and save it here:
$data | ConvertTo-Json | Set-Content -Path 'C:\Users\Siddhartha.S.Das2\Desktop\output.json'
Quick example with format specifier p for percentage, see https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings. The inputs are [double] type.
0.842093077,
0.792955,
0.8910225,
0.8724875,
0.852885333,
0.774708,
0.987243333,
0.87078,
0.9565,
0.839393333 | % tostring p
84.21%
79.30%
89.10%
87.25%
85.29%
77.47%
98.72%
87.08%
95.65%
83.94%

Powershell append csv file after each while loop

Can you please tell me how to add a csv file so that the data for each variable is written to the required column? for each new loop to a new line?
New-Item -Path . -Name "Test.csv" -ItemType "file" -force | Out-Null
("" | Select-Object Date, RAM_1, RAM_2, RAM_3, RAM_Used_total_%, RAM_Free%, CPU_Load% | ConvertTo-Csv -NoType -Delimiter ";")[0] | Out-File '.\Test.csv'
# Start the while loops
while($true) {
# In which data is collected into variables that need to be entered into each of the columns, respectively
$date
$RAM_1
$RAM_2
$RAM_3
$RAM_Usage
$Free_RAM
$CPU_Load
Start-Sleep -s 60
}
thank you in advance
Don't attempt to manually write the CSV file - let Export-CSV take care of that for you instead:
while($true) {
# populate your variables ...
$date = $(<# ... #>)
$RAM_1 = $(<# ... #>)
$RAM_2 = $(<# ... #>)
$RAM_3 = $(<# ... #>)
$RAM_Usage = $(<# ... #>)
$Free_RAM = $(<# ... #>)
$CPU_Load = $(<# ... #>)
# then create a new object with the correct column names
$record = [pscustomobject]#{
'Date' = $date
'RAM_1' = $RAM_1
'RAM_2' = $RAM_2
'RAM_3' = $RAM_3
'RAM_Used_total_%' = $RAM_Usage
'RAM_Free%' = $Free_RAM
'CPU_Load%' = $CPU_Load
}
# append record to CSV
$record |Export-Csv .\path\to\output.csv -Append
Start-Sleep -s 60
}

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
#>

Best Practice - If match return result

Use Case
The below script is to be placed in a scheduled task to notify me if my public IP Address changes
The IF match condition is not the correct behaviour
What would be the best way to match if the IP Address changed and output that to host?
Code
$ip = Invoke-RestMethod http://ipinfo.io/json | select -ExpandProperty ip
$date = (get-date).date
$value = "{0} - {1}" -f ($date),($ip)
Add-Content -Value $value -Path "C:\users\Sumeet\Documents\WindowsPowerShell\ip.txt"
$file = Get-Content -Path "C:\users\Sumeet\Documents\WindowsPowerShell\ip.txt"
if ($file | Select-String -Pattern $ip) {
clear-host
write-host "Match found at $_ your public IP interface has changed"
}
File Output
Date - IP
9/06/2018 12:00:00 AM - 121.211.177.20
9/06/2018 12:00:00 AM - 121.211.177.20
Output
I think something similar would be more helpful, as #LotPings suggested. You should update the file only if there is a change and follow up with notification as needed.
Also note, that change of IP can occur at anytime, but your scheduled task will let you know only whenever it is scheduled to run.
$ip = Invoke-RestMethod http://ipinfo.io/json | select -ExpandProperty ip
$date = (get-date).date
$value = "{0} - {1}" -f ($date),($ip)
$file = Get-Content -Path "E:\Code\powershell\myPS\2018\Jun\checkPublicIP\ip.txt"
if ($file | Select-String -Pattern $ip) {
Write-Host "IP is not changed"
}
else {
#Adding current IP to the file
Add-Content -Value $value -Path "E:\Code\powershell\myPS\2018\Jun\checkPublicIP\ip.txt"
#Add more code as needed for notification / email / alert.
}
Handling of data is IMO done easier when using csv files as columns/properties are assigned on import.
Because of my locale which doesn't support the date separator / or tt for AM/PM I had to use a CultureInfo object.
## Q:\Test\2018\06\09\SO_50771712.ps1
$File = "$Env:USERPROFILE\Documents\WindowsPowerShell\ip.csv"
$CIUS = New-Object System.Globalization.CultureInfo("en-US")
$Actual = [PSCustomObject]#{
Date = (get-date).ToString("d/M/yyyy hh:mm:ss tt",$CIUS)
IP = (Invoke-RestMethod http://ipinfo.io/json).ip
}
if (!(Test-Path $File)){'"Date","IP"'|Set-Content $File}
$Last=Import-Csv $File|Sort-Object {[DateTime]$_.Date}|Select-Object -Last 1
If ($Last.ip -ne $Actual.ip){
Write-Host ("Last ip : {0} from: {1}" -f $LAst.IP,$Last.date)
Write-Host ("New ip : {0} from: {1}" -f $Actual.IP,$Actual.date)
Export-Csv $File -InputObject $Actual -Append -NoTypeInformation
}
Sample output:
> Q:\Test\2018\06\09\SO_50771712.ps1
Last ip : 92.123.13.83 from: 9/6/2018 09:19:21 PM
New ip : 92.123.13.84 from: 9/6/2018 09:34:59 PM
> gc $file
"Date","IP"
"9/6/2018 08:51:00 PM","92.123.13.82"
"9/6/2018 09:19:21 PM","92.123.13.83"
"9/6/2018 09:34:59 PM","92.123.13.84"