powershell Get-Winevent SWITCH MATCH issue - powershell

'm running this powershell command and saving the output in a csv.
powershell "Get-WinEvent -EA SilentlyContinue -FilterHashtable #{Logname='System';ID=42}| SELECT-Object #{Label = 'TimeCreated'; Expression = {Get-Date $_.TimeCreated -Format 'yyyy-MM-dd HH:mm:ss'}},#{Label = 'DayOfWeek'; Expression = {(Get-Date $_.TimeCreated).DayOfWeek}},ID,#{l='ID Description';e={Switch ($_) { {$_.ID -eq '42'}{'Type=Sleep matched using EventID';break} {$_.MESSAGE -Match 'Sleep Reason: Application API'}{Type='Sleep matched using Message';break} }}},MESSAGE|ConvertTo-Csv -NoTypeInformation | %{ $_ -replace """`r`n""",',' } | select -Skip 1 | Out-File -Append c:\logs\timeline\TEMP_TimeLine.csv"
I get the expected results as below:
"2014-05-10 00:00:04","Saturday","42","Type=Sleep matched using EventID","The system is entering sleep.,,Sleep Reason: Application API"
"2014-05-09 00:00:02","Friday","42","Type=Sleep matched using EventID","The system is entering sleep.,,Sleep Reason: Application API"
"2014-05-08 00:00:02","Thursday","42","Type=Sleep matched using EventID","The system is entering sleep.,,Sleep Reason: Application API"
But, if i switch the positions of the two case statements inside the switch, i'm not getting the expected output(The derived field 'ID Description' is blank). I am trying to get mix of string matches on the message field and EventID field to be working together.
This is what i'm trying:
powershell "Get-WinEvent -EA SilentlyContinue -FilterHashtable #{Logname='System';ID=42}| SELECT-Object #{Label = 'TimeCreated'; Expression = {Get-Date $_.TimeCreated -Format 'yyyy-MM-dd HH:mm:ss'}},#{Label = 'DayOfWeek'; Expression = {(Get-Date $_.TimeCreated).DayOfWeek}},ID,#{l='ID Description';e={Switch ($_) { {$_.MESSAGE -Match 'Sleep Reason: Application API'}{Type='Sleep matched using Message';break} {$_.ID -eq '42'}{'Type=Sleep matched using EventID';break} }}},MESSAGE|ConvertTo-Csv -NoTypeInformation | %{ $_ -replace """`r`n""",',' } | select -Skip 1 | Out-File -Append c:\logs\timeline\TEMP_TimeLine.csv"
The Message field clearly has the string 'Sleep Reason: Application API' as we can see from the first output. Wondering whats going on here... Any clues powershell experts?

Ok, two issues I see:
A) You're probably breaking your own script. I'll get to that in a sec.
B) You're missing a ' on the $_.Message line. Type='Sleep should be 'Type=Sleep
Ok, back to point A. I'll start with ;break. In 99% of cases don't do it, you'll make the scripting gods angry, and you wouldn't like them when they're angry. In most cases you want to use ;Continue instead. Break literally breaks out of things, and depending on where it's used it can break out of parent loops entirely stopping part way through a set of things. Continue on the other hand moves to the end of the current loop, skipping anything else. Same thing? Kinda, sorta, but Continue won't break a ForEach-Object loop like Break will.
So, with that said, let's try this in your switch:
Switch ($_) {
{$_.ID -eq '42'}{'Type=Sleep matched using EventID';continue}
{$_.MESSAGE -Match 'Sleep Reason: Application API'}{'Type=Sleep matched using Message';continue}
}
Ok, that's great, along with the whole ' issue in point B that would probably fix the code in general.
So, with that said, why are you running it like that? Dear lord, running an insanely long one liner is just crazy. Save it to a .PS1 file, and if you're calling it from a batch file then call the script file, but ug, that's just hard to work with in general, it's no wonder you missed the ' in the middle of that line. If you are calling it from a batch file, name it GetSleepLogs.ps1 (or whatever you want, just modify the file name in the command) and try this:
PowerShell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File GetSleepLogs.ps1
Edit: I didn't like the convertto-CSV | select -skip 1 | %{ %_ -replace ...} | Out-File thing, it just seemed clunky to me. Also, all the impromptu hashtables on the Select command were a little hard to follow. Check out this alternative that creates 1 object with several properties, and then just pipes that to Export-CSV with the -append and -NoTypeInformation switches set which should just tack it to the bottom of an existing CSV file.
Get-WinEvent -EA SilentlyContinue -FilterHashtable #{Logname='System';ID=42}| ForEach{[PSCustomObject][Ordered]#{
'TimeCreated' = Get-Date $_.TimeCreated -Format 'yyyy-MM-dd HH:mm:ss'
'DayOfWeek' = (Get-Date $_.TimeCreated).DayOfWeek
'ID' = $_.ID
'ID Description' = Switch($_){
{$_.ID -eq '42' -AND $_.Message -match 'Sleep Reason: Application API'}{'Type=Sleep matched using EventID and Message';continue}
{$_.ID -eq '42'}{'Type=Sleep matched using EventID';continue}
{$_.Message -match 'Sleep Reason: Application API'}{'Type=Sleep matched using Message';continue}}
'MESSAGE' = $_.Message.replace("`r`n`r`n","`r`n") -replace "((?<!`")`r`n|`n|`r)", ","
}}|Export-Csv C:\temp\TimeLine.csv -NoTypeInformation -append

Related

Can't figure out Powershell Where-Object. SSAS Cube Processed State via Powershell,

I've created a powershell script to list the Processed State of a number of SSAS Cubes.
I'm puzzled why the Where-Object only produces the correct results when I use -like. I can't seem to use -eq. Anyone got any ideas ?
I'm using the Where-Object to zero in on the production Database.
The code i'm using is this..
Import-Module SQLASCmdlets -DisableNameChecking
$SSASServer = New-Object Microsoft.AnalysisServices.Server
$instanceName = "SSASSVR01"
$SSASServer.connect($instanceName)
$ProdCubes = $SSASServer.Databases.Cubes
$ProdCubes | Select Parent , State , Name `
| Where-Object { $_.Parent -like "DWProd*" }
I'd expect that -eq would work, but it doesn't
I don't get any response if the last line is written as...
$ProdCubes | Select Parent , State , Name `
| Where-Object { $_.Parent -eq "DWProd" }
I suspect it's whitespace, but how do you filter out the whitespace in powershell.

Extracting part of a host name with select-object

I have a simple powershell function where I provide the log type and event and it scans all of our SQL servers. it works except the host name is returned as hostname.domain.local. I want it to return just the host name. I've tried machinename.split('.') and substring and it won't work. I've tried putting the select-object into a separate variable and was going to join it with the rest of the columns, but it takes too long to run.
Here is my sample scrap code i'm testing with before I change my function along with the commented out parts that didn't work. Looked around and found lots of resources about the commands, but they don't work when I try to use them in my script.
The error I keep getting is A positional parameter cannot be found that accepts argument '. '.
$servers = Get-Content -literalpath "C:\temp\sql_servers3.txt"
#$server
#$result =
ForEach($box in $servers) {Get-Eventlog -ComputerName $box -LogName
application -After 1-4-2018 -Entrytype Error | Where {$_.source -notin
'Perfnet','Perflib', 'ntfs', 'vss'}| select-object -property MachineName}
#$result_Host_name = select-object -inputobject $result -property
'MachineName'
#'TimeGenerated', 'MachineName'.Split('.')[1], 'EventID','message'}
#| Where {$_.source -notin 'Perfnet','Perflib', 'ntfs', 'vss'} 0
#return $result_Host_name
What you are looking for is a "Calculated Property" when using Select-Object.
| Select-Object #{n='HostName';e={($_.MachineName -split '\.')[0]}}

Using Powershell to compare two files and then output only the different string names

So I am a complete beginner at Powershell but need to write a script that will take a file, compare it against another file, and tell me what strings are different in the first compared to the second. I have had a go at this but I am struggling with the outputs as my script will currently only tell me on which line things are different, but it also seems to count lines that are empty too.
To give some context for what I am trying to achieve, I would like to have a static file of known good Windows processes ($Authorized) and I want my script to pull a list of current running processes, filter by the process name column so to just pull the process name strings, then match anything over 1 character, sort the file by unique values and then compare it against $Authorized, plus finally either outputting the different process strings found in $Processes (to the ISE Output Pane) or just to output the different process names to a file.
I have spent today attempting the following in Powershell ISE and also Googling around to try and find solutions. I heard 'fc' is a better choice instead of Compare-Object but I could not get that to work. I have thus far managed to get it to work but the final part where it compares the two files it seems to compare line by line, for which would always give me false positives as the line position of the process names in the file supplied would change, furthermore I only want to see the changed process names, and not the line numbers which it is reporting ("The process at line 34 is an outlier" is what currently gets outputted).
I hope this makes sense, and any help on this would be very much appreciated.
Get-Process | Format-Table -Wrap -Autosize -Property ProcessName | Outfile c:\users\me\Desktop\Processes.txt
$Processes = 'c:\Users\me\Desktop\Processes.txt'
$Output_file = 'c:\Users\me\Desktop\Extracted.txt'
$Sorted = 'c:\Users\me\Desktop\Sorted.txt'
$Authorized = 'c:\Users\me\Desktop\Authorized.txt'
$regex = '.{1,}'
select-string -Path $Processes -Pattern $regex |% { $_.Matches } |% { $_.Value } > $Output_file
Get-Content $Output_file | Sort-Object -Unique > $Sorted
$dif = Compare-Object -ReferenceObject $(Get-Content $Sorted) -DifferenceObject $(get-content $Authorized) -IncludeEqual
$lineNumber = 1
foreach ($difference in $dif)
{
if ($difference.SideIndicator -ne "==")
{
Write-Output "The Process at Line $linenumber is an Outlier"
}
$lineNumber ++
}
Remove-Item c:\Users\me\Desktop\Processes.txt
Remove-Item c:\Users\me\Desktop\Extracted.txt
Write-Output "The Results are Stored in $Sorted"
From the length and complexity of your script, I feel like I'm missing something, but your description seems clear
Running process names:
$ProcessNames = #(Get-Process | Select-Object -ExpandProperty Name)
.. which aren't blank: $ProcessNames = $ProcessNames | Where-Object {$_ -ne ''}
List of authorised names from a file:
$AuthorizedNames = Get-Content 'c:\Users\me\Desktop\Authorized.txt'
Compare:
$UnAuthorizedNames = $ProcessNames | Where-Object { $_ -notin $AuthorizedNames }
optional output to file:
$UnAuthorizedNames | Set-Content out.txt
or in the shell:
#(gps).Name -ne '' |? { $_ -notin (gc authorized.txt) } | sc out.txt
1 2 3 4 5 6 7 8
1. #() forces something to be an array, even if it only returns one thing
2. gps is a default alias of Get-Process
3. using .Property on an array takes that property value from every item in the array
4. using an operator on an array filters the array by whether the items pass the test
5. ? is an alias of Where-Object
6. -notin tests if one item is not in a collection
7. gc is an alias of Get-Content
8. sc is an alias of Set-Content
You should use Set-Content instead of Out-File and > because it handles character encoding nicely, and they don't. And because Get-Content/Set-Content sounds like a memorable matched pair, and Get-Content/Out-File doesn't.

PowerShell: Start-Job -scriptblock Multi line scriptblocks?

Hoping someone can help me to figure out if this is possible. I have a 20 ish lines that scan a log file in a while loop. I need this to happen in parallel with the rest of the script. The log scanning loop is passing the entries into SQLite and other scripts need to act on this information - hence wanting to run them in parallel.
If i use the Job-Start command then it seems the -SciptBlock function will only accept one piped line of commands. I have too many commands to want to pipe so i need to run multiple lines in the scriptblock.
I tried several ways of doing it but the following examples give the least errors. I also tried it in an Invoke-Command -ScriptBlock -as job like in the second example - both ways will not accept a multiline scriptblock.
what am i doing wrong, please?
Start-Job -Name LogScan -ScriptBlock
{
$EventOld = ConvertFrom-Json (Get-content ( Get-ChildItem | Sort-Object -Property LastWriteTime | Select-Object -last 1 ) | Select-Object -last 1)
$RunLoop = 1
while (RunLoop -ge 1)
{
Start-Sleep -Milliseconds 333
$RunLoop = $RunLoop +1
$EventNew = ConvertFrom-Json (Get-content ( Get-ChildItem | Sort-Object -Property LastWriteTime | Select-Object -last 1 ) | Select-Object -last 1)
if ($EventOld.timestamp -ne $EventNew.timestamp)
{
# lots of commands and here passing the array to SQLite
}
$EventOld = $EventNew
}
}
Error is as follows:
Start-Job : Missing an argument for parameter 'ScriptBlock'.
Specify a parameter of type 'System.Management.Automation.ScriptBlock'
and try again. [..]
Kudos to briantist for helping me to get there :)
When using Start-Job and Invoke-Command, be careful to put the opening { for the -ScriptBlock parameter on the same line as the command. Do not put it on the line below as you would with an if or while command.
This leads to a the further issue of not spotting that you have not matched opening { and closing } brackets properly, as you are used to the convention of matching their indentation level to pair them up.
Please note that the references to $Event.timestamp in the code. These come from the fact that one of the JSON fields is called timestamp - it is not a method or property of a standard string or array.

Issues with Powershell Import-CSV

Main Script
$Computers = Get-Content .\computers.txt
If ( test-path .\log.txt ) {
$Log_Successful = Import-CSV .\log.txt | Where-Object {$_.Result -eq "Succesful"}
} ELSE {
Add-Content "Computer Name,Is On,Attempts,Result,Time,Date"
}
$Log_Successful | format-table -autosize
Issues:
Log_Successful."Computer Name" works fine, but if i change 4 to read as the following
$Log_Successful = Import-CSV .\log.txt | Where-Object {$_.Result -eq "Failed"}
Log_Successful."Computer Name" no longer works... Any ideas why?
Dataset
Computer Name,Is On,Attempts,Result,Time,Date
52qkkgw-94210jv,False,1,Failed,9:48 AM,10/28/2012
HELLBOMBS-PC,False,1,Successful,9:48 AM,10/28/2012
52qkkgw-94210dv,False,1,Failed,9:48 AM,10/28/2012
In case of "Successful" a single object is returned. It contains the property "Computer Name". In case of "Failed" an array of two objects is returned. It (the array itself) does not contain the property "Computer Name". In PowerShell v3 in some cases it is possible to use notation $array.SomePropertyOfContainedObject but in PowerShell v2 it is an error always. That is what you probably see.
You should iterate through the array of result objects, e.g. foreach($log in $Log_Successful) {...} and access properties of the $log objects.
And the last tip. In order to ensure that the result of Import-Csv call is always an array (not null or a single object) use the #() operator.
The code after fixes would be:
$logs = #(Import-Csv ... | where ...)
# $logs is an array, e.g. you can use $logs.Count
# process logs
foreach($log in $logs) {
# use $log."Computer Name"
}
I'm not sure if this is the problem but you have a typo, in Where-Object you compare against "Succesful" and the value in the file is "Successful" (missing 's').
Anyway, what's not working?