quser language independent parsing in powershell - powershell

I would like to query all users currently logged in to my computer with powershell. My current solution looks like this:
$userObject = quser /server:localhost 2>&1 | ForEach-Object -Process { ($_ -replace '\s{2,}', ';').Trim() } | ConvertFrom-Csv -Delimiter ';'
This gives me the currently logged in users, but the language for the header column depends on the system language. So if I run the script on a system with english as system language I get the following result
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
random console 1 Active none 4/7/2021 6:52 AM
If I run the exactly same command on a german system I get the following:
BENUTZERNAME SITZUNGSNAME ID STATUS LEERLAUF ANMELDEZEIT
random console 1 Aktiv 1+02:29 07.04.2021 19:10
The same on a french system, you get it...
I also tried to replace the default headers with my custom ones by using this code
$header = 'UserName', 'SessionName', 'ID', 'State', 'IdleTime', 'LogonTime'
$userObject = quser /server:localhost 2>&1 | Select-Object -Skip 1 | ForEach-Object -Process { ($_ -replace '\s{2,}', ';').Trim() } | ConvertFrom-Csv -Header $header -Delimiter ';'
but this only changes the headers but not the language dependent "state" property, so if I would like to filter the users by the state I would still have to convert the property to the correct language.
Any help is much appreciated

Try:
chcp 65001
Before your command. The output is always in English in my case.

Related

How to invoke-sqlcmd and export WITH headers and dashes/hyphens but tab-delimited

Say I run this:
$HOME_DATABASE = 'mydb'
$params = #{
'Database' = $HOME_DATABASE
'ServerInstance' = 'mydb'
'Username' = 'myuser'
'Password' = 'mypass'
'Query' = 'select * from sometable'
}
$queryresults = Invoke-Sqlcmd #params
I want the output to look like:
Name Date Number of Records 95th Percentile 99.5th Percentile 100th Percentile
---- ---- ----------------- --------------- ----------------- ----------------
asdf 2022-10-02 00:00:00.000 1234 5678 9012 12345
where what's separating the fields is a tab.
I tried this, but it doesn't include the hyphen/dash line.
$queryresults |ConvertTo-Csv -NoTypeInformation -delimiter "$query_result_separator" | `
ForEach-Object {$_ -Replace('"','')} | `
Out-file c:\temp\$($query_attachment_filename) -fo -en ascii
Somebody else said to export-csv, then go back and replace the quotes in the file. Is there a better way?
Both Export-Csv and ConvertTo-Csv (the former's in-memory counterpart) by design do not create a separator line between their header row and the data rows, given that the sole purpose of such a line would be to make the the data human-friendly, whereas CSV / TSV data is meant for programmatic processing.
To get such a line, you'll have to insert it manually.
Also, both cmdlets "..."-enclose all field values - invariably in Windows PowerShell, by default in PowerShell (Core) 7+.
In PowerShell (Core) 7+ you can use -UseQuotes Never as an opt-out of this behavior.
In Windows PowerShell you need to remove the " chars. after the fact, as you do in your own solution attempt.
A solution that works in both PowerShell editions:
$i = 0
$queryresults |
ConvertTo-Csv -NoTypeInformation -Delimiter "`t" |
ForEach-Object {
$_ -replace '"' # output with " chars. removed
# If it was the first, i.e. the *header* row that was just output,
# construct and output the desired separator row.
if ($i++ -eq 0) {
($_ -replace '"' -split "`t").ForEach({ '-' * $_.Length }) -join "`t"
}
}
Pipe to Set-Content as needed (which is preferable to Out-File, given that it is strings that are to be saved - see this answer).

How can I use input file to feed powershell command?

I have a csv file with the following info (for readibility I removed commas) :
box1 1.1.1.1 user password
box2 2.2.2.2 user password
box3 3.3.3.3 user password
I need to execute command with parameters from the csv file, like below :
plink -l user -pw password 1.1.1.1 lsvdisk -delim , -nohdr
and feed a file, with the box name on the start of each line.
In other words, I have ~300 storage boxes (V7k/SVC) and I need an inventory of all the UUID of LUNs. Preferably powershell.
Import-Csv and ForEach-Object are your friend here:
Import-Csv -Header Host,Ip,User,Pw foo.csv | ForEach-Object {
plink -l $_.User -pw $_.Pw $_.Ip lsvdisk -delim ',' -nohdr
}
Note that we have to enclose the , in quotes to ensure it's passed as a single parameter.¹
Within the script block of ForEach-Object $_ is the current object, which has the properties from the CSV file. We had to give the import custom headers since your file apparently doesn't have any. If that's not how your file looks, then adjust accordingly.
This will give you lots of output, of which perhaps the host name is no longer available. Depending on how the output looks, it may make sense to parse it and wrap it up into nice objects, but I don't really know how the output looks. A simple way of simply capturing output and associating it with the host name would be the following:
Import-Csv -Header Host,Ip,User,Pw foo.csv | ForEach-Object {
$output = plink -l $_.User -pw $_.Pw $_.Ip lsvdisk -delim ',' -nohdr
New-Object PSObject -Property #{
Host = $_.Host
Output = $output
}
}
To prefix each line of the output with the host name you can loop over the output again:
Import-Csv -Header Host,Ip,User,Pw foo.csv | ForEach-Object {
$host = $_.Host
plink -l $_.User -pw $_.Pw $_.Ip lsvdisk -delim ',' -nohdr | ForEach-Object {
"${host}: $_"
}
}
¹ If the command line includes lots of things that PowerShell wants to interpret itself, it's often easier to use %-- to stop parsing for the rest of the line after that, which then passes the rest verbatim to external commands.

Powershell function returning an array instead of string

i'm importing a csv and i would like to add a column to it (with the result based off of the previous columns)
my data looks like this
host address,host prefix,site
10.1.1.0,24,400-01
i would like to add a column called "sub site"
so I wrote this module but the problem is, the actual ending object is an array instead of string
function site {
Param($s)
$s -match '(\d\d\d)'
return $Matches[0]
}
$csv = import-csv $file | select-object *,#{Name='Sub Site';expression= {site $_.site}}
if I run the command
PS C:\>$csv[0]
Host Address :10.1.1.0
host prefix :24
site :400-01
sub site : {True,400}
when it should look like
PS C:\>$csv[0]
Host Address :10.1.1.0
host prefix :24
site :400-01
sub site : 400
EDIT: I found the solution but the question is now WHY.
If I change my function to $s -match "\d\d\d" |out-null I get back the expected 400
Good you found the answer. I was typing this up as you found it. The reason is because the -match returns a value and it is added to the pipeline, which is all "returned" from the function.
For example, run this one line and see what is does:
"Hello" -match 'h'
It prints True.
Since I had this typed up, here is another way to phrase your question with the fix...
function site {
Param($s)
$null = $s -match '(\d\d\d)'
$ret = $Matches[0]
return $ret
}
$csv = #"
host address,host prefix,site
10.1.1.1,24,400-01
10.1.1.2,24,500-02
10.1.1.3,24,600-03
"#
$data = $csv | ConvertFrom-Csv
'1 =============='
$data | ft -AutoSize
$data2 = $data | select-object *,#{Name='Sub Site';expression= {site $_.site}}
'2 =============='
$data2 | ft -AutoSize

How to modify string inside CSV for current PowerShell Session

I need to import a csv, select a string and change some part of it's value to $env:username. I think I have to work with select-string to get the string, but i'm stuck with changing the value. Or is it possible to write $env:username directly into the csv? I wasn't able to do it. also, the CSV should stay the way it is, only the current powershell session needs the correct string
CSV looks like this, userhome should be replaced by $env:username:
Test Test2
----- -----
Hi C:\user\userhome
something C:\test\install
hello C:\windows
Thats what i tried but I'm not successfull.
$test.Test2 | sls userhome -replace ($env:username)
another approach:
sls ($test.Test2).value -replace ("userhome",$env:username)
EDIT: This is the whole command where this csv is needed - just for information:
$Drives = Import-CSV .\NetworkDrives.csv -Delimiter ';' | ? {(($_.Group).split(',') -contains $UserOU) -and (!(Test-Path $_.Letter))} | % { #Do Stuff }
I'm glad that PetSerAl's answer in the comments worked for you. Personally I think it may have been simpler to use Get-Content, run a -Replace on that, and then pipe to ConvertFrom-CSV instead of Import-CSV.
$Drives= (Get-Content .\NetworkDrives.csv) -Replace "userhome",$env:username | ConvertFrom-CSV -Delimiter ';' | ? {(($_.Group).split(',') -contains $UserOU) -and (!(Test-Path $_.Letter))} | % { #Do Stuff }

powershell Get-Winevent SWITCH MATCH issue

'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