Forum Discussion
dmarquesgn
Nov 09, 2022Iron Contributor
Powershell return variable from within a Invoke-Command
I'm developing a powershell script (and kind of new to it) to go to a couple of servers and extract the RDP logons, so we can check if a certain policy is being followed. So I've search a bit and now...
- Nov 10, 2022
There's a bit of double-handling going on but the biggest issue is on line 17, where you're taking the data and converting it into a table on the server.
Using Format-Table prevents you from seeing the data as objects, which is what renders the output from Invoke-Command basically unusable.
Here's a simpler version that treats the data as data, not a table. Once the data is back on the calling client, then you can go crazy with things like Format-Table if you like, but the important thing is that because the data is still in a usable (almost-native) state, you can do anything you like with it.
Here's a basic version of the important part: fetching the data.
$RemoteData = Invoke-Command -UseSSL -ComputerName (Get-Content "D:\Data\Servers-RDP2.txt") -ScriptBlock { $Username = "lain.robertson" $FilterPath = "<QueryList><Query Id='0'><Select Path='Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'>*[System[(EventID=1149) and TimeCreated[timediff(@SystemTime) <= 604800000]]] and *[UserData[EventXML[@xmlns='Event_NS'][Param1='$Username']]]</Select></Query></QueryList>" Get-WinEvent -ErrorAction SilentlyContinue -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' -FilterXPath $FilterPath | ForEach-Object { [PSCustomObject] @{ TimeCreated = $_.TimeCreated.ToString("dd-MM-yyyy HH:mm:ss"); User = $_.Properties[0].Value; Domain = $_.Properties[1].Value; Client = $_.Properties[2].Value; Server = $env:COMPUTERNAME; } } }
Here, the remote data is held in a client-side variable named $RemoteData, which is what you can then use as you see fit, including displaying as a table.
$RemoteData | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId | Sort-Object -Property Server, Domain, User | Format-Table -AutoSize;
Note: While this is more efficient than the original, it's only an example and I would generally discourage storing large data sets in variables (i.e. $RemoteData in this example). But if your list of servers isn't particularly large then diving into efficiency isn't going to matter.
Another example of using $RemoteData to produce a very simple HTML document instead (which you could use as your e-mail body, for example):
$RemoteData | Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, RunspaceId | Sort-Object -Property Server, Domain, User | ConvertTo-Html -Title "RDP logons";
I was also rather curious that you're only searching for one specific username in the event logs, but I just ran with the assumption that was deliberate.
Cheers,
Lain
LainRobertson
Nov 10, 2022Silver Contributor
There's a bit of double-handling going on but the biggest issue is on line 17, where you're taking the data and converting it into a table on the server.
Using Format-Table prevents you from seeing the data as objects, which is what renders the output from Invoke-Command basically unusable.
Here's a simpler version that treats the data as data, not a table. Once the data is back on the calling client, then you can go crazy with things like Format-Table if you like, but the important thing is that because the data is still in a usable (almost-native) state, you can do anything you like with it.
Here's a basic version of the important part: fetching the data.
$RemoteData = Invoke-Command -UseSSL -ComputerName (Get-Content "D:\Data\Servers-RDP2.txt") -ScriptBlock {
$Username = "lain.robertson"
$FilterPath = "<QueryList><Query Id='0'><Select Path='Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'>*[System[(EventID=1149) and TimeCreated[timediff(@SystemTime) <= 604800000]]] and *[UserData[EventXML[@xmlns='Event_NS'][Param1='$Username']]]</Select></Query></QueryList>"
Get-WinEvent -ErrorAction SilentlyContinue -LogName 'Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational' -FilterXPath $FilterPath |
ForEach-Object {
[PSCustomObject] @{
TimeCreated = $_.TimeCreated.ToString("dd-MM-yyyy HH:mm:ss");
User = $_.Properties[0].Value;
Domain = $_.Properties[1].Value;
Client = $_.Properties[2].Value;
Server = $env:COMPUTERNAME;
}
}
}
Here, the remote data is held in a client-side variable named $RemoteData, which is what you can then use as you see fit, including displaying as a table.
$RemoteData | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId | Sort-Object -Property Server, Domain, User | Format-Table -AutoSize;
Note: While this is more efficient than the original, it's only an example and I would generally discourage storing large data sets in variables (i.e. $RemoteData in this example). But if your list of servers isn't particularly large then diving into efficiency isn't going to matter.
Another example of using $RemoteData to produce a very simple HTML document instead (which you could use as your e-mail body, for example):
$RemoteData | Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, RunspaceId | Sort-Object -Property Server, Domain, User | ConvertTo-Html -Title "RDP logons";
I was also rather curious that you're only searching for one specific username in the event logs, but I just ran with the assumption that was deliberate.
Cheers,
Lain
- dmarquesgnNov 15, 2022Iron ContributorHi,
Thanks for the great tips.
I was already able to get it right. Also the HTML conversion is quite nice to send over email.
Cheers