I am trying to assign a local variable from within a scriptblock with no luck. The goal is log a status of each machine for the action taken with the data prior and the data after the change. I am not sure how to assign a local variable from within a script block. Any help is much appreciated.
$csvContents = #() # Create the empty array that will eventually be the CSV file
$Computers = Get-ADComputer -Filter '(OperatingSystem -like "Windows Server*") -and (Name -like "AD*")' | Sort-Object Name
foreach ($Computer in $Computers) {
$row = New-Object PSObject # Create an object to append to the array
$row | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "PingStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "DNSChangeStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "BeforeChange" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "AfterChange" -Value NotSet
#Write-Host "$($Computer.Name): " -ForegroundColor Yellow
$row.ComputerName = $Computer.Name
$rtn = Test-Connection -CN $Computer.dnshostname -Count 1 -BufferSize 16 -Quiet
if ($rtn -match 'True') {
Write-Host -ForegroundColor Green $Computer.DnsHostname
$row.PingStatus = 'Pingable'
Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
$NewDnsServerSearchOrder = "10.93.108.225","10.93.108.134"
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -eq "10.93.108.226"}
if ($Adapters -ne $null) {
# Show DNS servers before update
Write-Host "Before: " -ForegroundColor Green
$row.DNSChangeStatus = 'Change Needed'
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.BeforeChange = $_.DNSServerSearchOrder
}
# Update DNS servers
$Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null
# Show DNS servers after update
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null}
Write-Host "After: " -ForegroundColor Green
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.AfterChange = $_.DNSServerSearchOrder
}
} else {
Write-Host "No DNS change needed " -ForegroundColor Yellow
$row.DNSChangeStatus = 'No DNS Change Needed'
}
}
} else {
Write-Host -ForegroundColor Red $Computer.DnsGostname
Write-Host -ForegroundColor Red "Host not pingable"
$row.PingStatus = 'Not Pingable'
}
$csvContents += $row # append the new data to the array
$row = $null
}
$csvContents | Export-Csv -Path C:\DNSChanges.csv
I can't tell exactly what you want to do so I'll guess it's "pass a variable to scriptblock being invoked on a remote machine". If this is the case, you can either add parameters to the scriptblock or use the $using: qualifier as in:
$using:row.DNSChangeStatus = 'Change Needed'
Note that you can't "return" anything that way so you'd have to actually return the modified object from Invoke-Command.
$row = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
and add
$row
as the last line of the scriptblock.
Related
I am struggling for a couple of days to manipulate the output of a script, that generates the full IPtable of a vm, for the purpose of restoring set settings once the host is upgraded. Bellow is the .ps1 file:
$sudatapath = "c:\UpgradeData"
$OSV = [environment]::OSVersion.Version
$psVersion = $PSVersionTable.PSVersion
If ($psVersion) {
$psversion=2
}
Else{
$psversion=1
}
write-host "vmretainer ran" >$sudatapath\vmretainerran.txt
#Check if task needs scheduling
if( -not (test-path "$sudatapath\iprestoredfinal.flg") -and ($psversion -eq 2))
{
write-host $psversion
schtasks /create /tn "vmipretainer" /sc onstart /rl highest /ru system /tr "powershell.exe -Executionpolicy bypass -NonInteractive -NoLogo -NoProfile -File c:\UpgradeData\vmipretainer.ps1" /F
}
if( -not (test-path "$sudatapath\iprestoredfinal.flg") -and ($psversion -eq 1))
{
write-host $psversion
schtasks /create /tn "vmipretainer" /sc onstart /rl highest /ru system /tr "powershell.exe c:\UpgradeData\vmipretainer.ps1 -noprofile –Noninteractive" /F
}
if( -not (test-path "$sudatapath\ipexported.flg"))
{
if( -not (test-path "$sudatapath")) {mkdir $sudatapath}
write-host "ipexport ran" >$sudatapath\ipexportran.txt
if ($psversion -eq 2)
{
$ResultIPTable = #()
$NICs = Get-WMIObject Win32_NetworkAdapterConfiguration -computername . | where{$_.IPEnabled -eq $true}
Foreach($NIC in $NICs) {
$IPTable = #{}
$IPTable.Add("sIP",$NIC.IPAddress[0])
$IPTable.Add("sMACAddress",$NIC.MACAddress)
$IPTable.Add("sgateway",[string]$NIC.DefaultIPGateway)
$IPTable.Add("smask",$NIC.IPSubnet[0])
$IPTable.Add("sdns",[string]$NIC.DNSServerSearchOrder)
$IPTable.Add("sdescription",$NIC.Description)
$IPTable.Add("sDHCPEnabled",$NIC.DHCPEnabled)
$IPTable.Add("sDNSDomain",$NIC.DNSDomain)
$ResultIPTable += New-Object PsObject -Property $IPTable
}
$ResultIPTable | Export-Csv -NoTypeInformation "$sudatapath\ipsettings.csv"
#$ResultIPTable | format-table -property *
$gd = Get-Date
echo "exportedsettings" $gd >"$sudatapath\ipexported.flg"
exit
}
if ($psversion -eq 1)
{
$result2008table = #()
$NICs = Get-WMIObject Win32_NetworkAdapterConfiguration -computername . | where{$_.IPEnabled -eq $true}
Foreach($NIC in $NICs) {
$2008table = #{}
$2008table = New-Object -TypeName PSObject
$2008table | Add-Member -MemberType NoteProperty -Name sIP -Value $NIC.IPAddress[0]
$2008table | Add-Member -MemberType NoteProperty -Name sMACAddress -Value $NIC.MACAddress
$2008table | Add-Member -MemberType NoteProperty -Name sgateway -Value ([string]$NIC.DefaultIPGateway)
$2008table | Add-Member -MemberType NoteProperty -Name smask -Value $NIC.IPSubnet[0]
$2008table | Add-Member -MemberType NoteProperty -Name sdns -Value ([string]$NIC.DNSServerSearchOrder)
$2008table | Add-Member -MemberType NoteProperty -Name sdescription -Value $NIC.Description
$2008table | Add-Member -MemberType NoteProperty -Name sDHCPEnabled -Value $NIC.DHCPEnabled
$2008table | Add-Member -MemberType NoteProperty -Name sDNSDomain -Value $NIC.DNSDomain
$Result2008Table += $2008table
$Result2008Table | format-table -property *
$Result2008Table | Export-Csv -NoTypeInformation "$sudatapath\ipsettings.csv"
$gd = Get-Date
echo "exportedsettings" $gd >"$sudatapath\ipexported.flg"
}
}
}
if (test-path "$sudatapath\ipexported.flg")
{
write-host "Restoring NIC IP"
write-host "Restoring NIC IP" >$sudatapath\iprestoreran.txt
$NICIMPORTS = Import-Csv "$sudatapath\ipsettings.csv"
$i = 0
$NICs = Get-WMIObject Win32_NetworkAdapterConfiguration -computername . | where{$_.IPEnabled -eq $true}
foreach ($NICIMPORT in $NICIMPORTS){
$sIP = $NICIMPORT.sIP
$sMACAddress = $NICIMPORT.sMACAddress
$sgateway = $NICIMPORT.sgateway.trim('{}')
$smask = $NICIMPORT.smask
$sdns = $NICIMPORT.sdns.trim('{}')
# $sdescription = $NICIMPORT.sdescription
$sDHCPEnabled = $NICIMPORT.sDHCPEnabled
# $sDNSDomain = $NICIMPORT.sDNSDomain
if( $NICs[$i].IPAddress[0] -ne $sIP)
{
write-host "IPs do not match $NICs.IPAddress[0] $sIP"
write-host "IPs do not match $NICs.IPAddress[0] $sIP" >$sudatapath\ipchangeran.txt
$sndns=($sdns.Split(" ")[0],$sdns.Split(" ")[1])
write-host "Restoring IP Address from CSV"
$NICs[$i].EnableStatic($sIP, $smask)
$NICs[$i].SetGateways($sgateway)
$NICs[$i].SetDNSServerSearchOrder($sndns)
$gd = Get-Date
}
$i++
}
echo "IPrestored" $gd > "$sudatapath\iprestored.flg"
if (Get-item "$sudatapath\ipexported.flg" | Where LastWriteTime -lt (Get-Date).AddDays(-60)){schtasks.exe /delete /f /tn vmipretainer}
}
I tried numerous attempts, all have failed.
Here is one of them:
$sudatapath = "c:\UpgradeData"
$CSV = Import-CSV Import-Csv "$sudatapath\ipsettings.csv"
$newCsv = foreach ($row in $CSV) {
$row | Select-Object sMAcadress, #{Expression = {$row.sMACAddress.remove($13, $14).insert($13, '99')}}
$newCsv | Export-Csv -NoTypeInformation '$sudatapath\ipsettings2.csv'
}
I am looking for somethin inside the original script that does the thing that I want, at the moment the csv file looks like this:
"sdns","sMACAddress","sDNSDomain","sdescription","sDHCPEnabled","smask","sIP","sgateway"
"","00:15:5D:73:48:00",,"Microsoft Hyper-V Network Adapter","False","255.255.255.192","10.52.115.31","10.52.115.62"
"","00:15:5D:73:48:01",,"Microsoft Hyper-V Network Adapter #2","False","255.255.255.192","192.168.10.31",""
I want to change the value in column sMACAddress like this:
aa:bb:cc:dd:ee:ff to aa:bb:cc:dd:99:ff wihtout knowing the stored value of the column (macaddresses are dynamic, this is done for a match after the upgrade, that is why I need a specific value for the second to last pair, for the host I managed to figured it out, it is this script that is driving me crazy)
This made me crazy for now and I am trying to ask others if there is any solution to this.
Thank you for your input and have a great day.
If all you're looking for is the replacement of the 5th octet in the MAC Address for every row, one option is to use RegEx from this inside of a calculated property:
#"
"sdns","sMACAddress","sDNSDomain","sdescription","sDHCPEnabled","smask","sIP","sgateway"
"","00:15:5D:73:48:00",,"Microsoft Hyper-V Network Adapter","False","255.255.255.192","10.52.115.31","10.52.115.62"
"","00:15:5D:73:48:01",,"Microsoft Hyper-V Network Adapter #2","False","255.255.255.192","192.168.10.31",""
"# | ConvertFrom-Csv |
Select-Object -ExcludeProperty 'sMACAddress' -Property *,
#{
Name = 'sMACAddress';
Expression = {
$_.sMACAddress -replace "..(?=:..$)",'99'
}
}
This should give you results of:
sMACAddress
-----------
00:15:5D:73:99:00
00:15:5D:73:99:01
Here's a RegEx Demo that explains the pattern matching.
I am having some problems with updating data in a list from the results of a hash table. I am 99% sure it is due to the lack of understanding of what i am doing.
I am generating a $list of servers from a CSV. the CSV contains Servername, domain, description, plus some additional blank columns for use later.
what i am trying to do in a nutshell: i need to pull the down processes from a list of remote servers. to do this i am throwing each server from the list and function into its own runspace, the Hashtable is updating as expected. But i can not update the original $list i have.
here is my code:
Function OpenFile ($FilePath) {
$OFDiag = new-object system.windows.forms.openfiledialog
$OFDiag.filter = 'CSV (*.csv) | *.csv'
$OFDiag.ShowDialog() | out-null
$OFDiag.filename
}
# Create Primary Variables
$FilePath = OpenFile
$list = (get-content $FilePath) -replace '\(',' -' #ALL Servers and Groups need to remove parenthesis
$list = $list -replace '\)' #finish up removing the parenthesis
$list = $list -replace ' Or WorkGroup'
$list = convertFrom-CSV $list | select 'Name', 'Computer_Description', 'Domain' #Need to convert the list into a CSV formatted table.
$list = $list | sort Name
$list | Add-Member -NotePropertyName 'LastReboot' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'LastDeployment' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'RebootStatus' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'DownProcess' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'EnabledStatus' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'RDP' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'SchedTask' -NotePropertyValue $null
$servers = $list | %{$_.Name} | sort #ALL SERVERS - ONLY Servernames
$ServProSel = {
#
# Checks for Running Services and Processes.
# This Makes a determination as to what service/process groups should be checked.
# The Information as to what processes to look for are sent to the ProSer_Check function
# information from there is sent to the ServerStatus Tab
#
#Write-Host 'starting ServerProSel'
Param ($computer,$cred,$grpName,$hash)
#$cred = $(get-Variable "$Domain" -valueOnly)
$ck =#{} #$(Get-Variable -name "SCP_$serName" -ValueOnly)
Function ProSer_Check {
# This is the actual function that is run on the remote system to check
# for processes and services.
param ( [array] $Prcs,
[string] $Computer )
$script:chkres =#()
foreach ($p in $Prcs){
$script:res = Get-Process -name $p -ErrorAction SilentlyContinue
if (!$res) {
$chk = "$p -DOWN`r`n"
$chkres += $chk
}
}
if ($chkres.count -eq 0){
$chkres = "All Processes Up"}
Return $chkres
}
switch -Regex ($grpName){
'Demonstration' {
$Prcs = #('Process.Service'); break}
'Historian' {
$Prcs =#('Process.Service'); break}
'Models' {
$Prcs =#('UpdaterServer'); break}
'Inflictor' {
$Prcs =#('Automation.EngineService','Automation.Manager.Service','Automation.SmfLauncher','postgres','Redundancy.Server','WatchDog.Service'); break}
'Simulator' {
$Prcs =#('proc','moni','server','serve','clerk','web'); break}
'WebServer' {
$Prcs =#('w3wp','VShell'); break}
default {
$Prcs =#('svchost'); break}
}
$R = invoke-command -credential $cred -computername $Computer -scriptblock ${function:ProSer_Check} -ArgumentList $Prcs,$Computer
$hash[$Computer]=([string]$R)
}
$Script:runspaces = New-Object System.Collections.ArrayList
$Global:hash = [hashtable]::Synchronized(#{})
$global:sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$global:runspacepool = [runspacefactory]::CreateRunspacePool(1, 10, $sessionstate, $Host)
$global:runspacepool.Open()
Function SendToRunSpace {
$function = $args[0]
#$function
$powershell = [powershell]::Create().AddScript($function).AddArgument($computer.name).AddArgument($cred).AddArgument($grpName).AddArgument($hash)
$powershell.RunspacePool = $global:runspacepool
#$hash = #{Name=$computer.name;DownProcess = "Waiting.."}
$temp = "" | Select-Object PowerShell,Runspace,Computer
$Temp.Computer = $Computer
$temp.PowerShell = $powershell
$temp.Runspace = $powershell.BeginInvoke()
Write-Verbose ("Adding {0} collection" -f $temp.Computer)
$runspaces.Add($temp) | Out-Null
}
ForEach ($Computer in $list) {
$domain = $computer.Domain
$grpName = $computer.'Computer_Description'
$cred = $(get-Variable "$Domain" -valueOnly)
#Create the powershell instance and supply the scriptblock with the other parameters
if(!$(Get-Variable "TEST_$domain" -ValueOnly)){
CredCheck $computer.name $cred
}
#SendToRunSpace $scriptBlock $computer $domain $global:hash
SendToRunSpace $ServProSel $computer $cred $grpName $global:hash
}
I am running this in PowerShell ISE so i can edit on the fly and test things. When i run this code i generate the $list and $hash items. Ultimately i would like to grab the value out of the $hash for the server and update the corresponding server information in the $list object.
or is there a better way to do this? is the Hashtable the only way to Synchronize data from the runspaces to the current process?
I am working on a PowerShell script for a customer to pull information from Servers before they are decommissioned. I have had to jump through a couple of hoops, as they have 2008 and even some 2003 servers, which don't always support the same Get-CimInstance queries, but for the most part it is working as expected. One section I have as yet been unable to fix however is pulling a list of all local groups and their members, like so:
$localGroups = Invoke-Command -ScriptBlock {
[ADSI]$S = "WinNT://$($env:computername)"
$S.children.Where({$_.class -eq 'group'}) |
Select #{Name="Name";Expression={$_.name.value}},
#{Name="Members";Expression={
[ADSI]$group = "$($_.Parent)/$($_.Name),group"
$members = $Group.psbase.Invoke("Members")
($members | ForEach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}) -join ";"
}
}
} -ComputerName $sPC | Select PSComputername,Name,Members
This works just fine on Server 2012 and above, but fails on 2008 and below. The specific error states:
Method invocation failed because [System.DirectoryServices.DirectoryEntries] doesn't contain a method named 'Where'
So the OS does not like the $S.Children.Where() call. Just curious if anyone knows of another way to format it for use with 2008 and earlier. I could probably go the old WMI call route, but that takes forever to finish.
I ended up finding a (fairly) quick way of doing it, works on all OS flavors:
function get-groups {
param ($strcomputer)
$groups = gwmi win32_group -ComputerName $strcomputer
return $groups
}
function Get-LocalGroupMembers {
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[Alias("Name")]
[string]$ComputerName,
[string]$GroupName = "Administrators"
)
begin {}
process {
$ComputerName = $ComputerName.Replace("`$", '')
$arr = #()
$wmi = Get-WmiObject -ComputerName $ComputerName -Query "SELECT * FROM Win32_GroupUser WHERE GroupComponent=`"Win32_Group.Domain='$ComputerName',Name='$GroupName'`""
if ($wmi -ne $null) {
foreach ($item in $wmi) {
$arr += ($item.PartComponent.Substring($item.PartComponent.IndexOf(',') + 1).Replace('Name=', '').Replace("`"", ''))
}
}
$hash = #{ComputerName=$ComputerName;Members=$arr}
return $hash
}
end{}
}
$sPC = "Server2008R2"
$a = 1
$adapterInfo = Get-CimInstance -query "Select * From
Win32_NetworkAdapterConfiguration Where IPenabled = 'TRUE'" -ComputerName $sPC
$serialNumber = Get-CimInstance -query "Select * From Win32_Bios" -ComputerName $sPC
$shares = Get-CimInstance -query "Select * From Win32_Share" -ComputerName $sPC
$localGroups = get-groups $sPC
$myObject = New-Object -TypeName PSObject
$myObject | Add-Member -MemberType NoteProperty -Name ComputerName -Value $adapterInfo.PSComputerName
$myObject | Add-Member -MemberType NoteProperty -Name Description -Value $adapterInfo.Description
$adapterInfo | ForEach-Object {
$_.IPAddress |
ForEach-Object {
$myObject | Add-Member -MemberType NoteProperty -Name "IPAddress$($a)" -Value $_
$a++
}
}
$myObject | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $serialNumber.SerialNumber
foreach ($share in $shares) {
if ($share.Name -ne "C$" -and
$share.Name -ne "D$" -and
$share.Name -ne "ADMIN$" -and
$share.Name -ne "IPC$") {
$myObject | Add-Member -MemberType NoteProperty -Name "Share $($share.Name)" -Value $share.Path
}
}
foreach ($group in $localGroups) {
$members = ((Get-LocalGroupMembers -ComputerName $sPC -GroupName $group.Name).Members) -join "; "
if ($members.Length -ne 0) {
$myObject | Add-Member -MemberType NoteProperty -Name "$($group.Name) Members" -Value $members
}
}
$myObject | Export-Csv -Path "<Path to Folder>\$($sPC).csv" -NoTypeInformation
I am looking for command/script to check how many users (local & domain) are currently logged in and have been active within the last 30 minutes so that I can decide to go for machine reboot or not.
Hopefully without Get-ADUser.
I came across two scripts but on executing they are returning only my information where on server I can see more than 5 user are logged in.
Script1
function Get-LoggedOnUser {
#Requires -Version 2.0
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
Position=0,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String[]]$ComputerName
)#End Param
Begin
{
Write-Host "`n Checking Users . . . "
$i = 0
}#Begin
Process
{
$ComputerName | Foreach-object {
$Computer = $_
try
{
$processinfo = #(Get-WmiObject -class win32_process -ComputerName $Computer -EA "Stop")
if ($processinfo)
{
$processinfo | Foreach-Object {$_.GetOwner().User} |
Where-Object {$_ -ne "NETWORK SERVICE" -and $_ -ne "LOCAL SERVICE" -and $_ -ne "SYSTEM"} |
Sort-Object -Unique |
ForEach-Object { New-Object psobject -Property #{Computer=$Computer;LoggedOn=$_} } |
Select-Object Computer,LoggedOn
}#If
}
catch
{
"Cannot find any processes running on $computer" | Out-Host
}
}#Forech-object(ComputerName)
}#Process
End
{
}#End
}#Get-LoggedOnUser
Script2
$regexa = '.+Domain="(.+)",Name="(.+)"$';
$regexd = '.+LogonId="(\d+)"$';
$logontype = #{"0"="Local System";
"2"="Interactive";
"3"="Network";
"4"="Batch";
"5"="Service";
"7"="Unlock";
"8"="NetworkCleartext";
"9"="NewCredentials";
"10"="RemoteInteractive";
"11"="CachedInteractive";
};
$logon_sessions = #(gwmi win32_logonsession);
$logon_users = #(gwmi win32_loggedonuser);
$session_user = #{};
$logon_users |% {$_.antecedent -match $regexa > $nul;
$username = $matches[1] + "\" + $matches[2];
$_.dependent -match $regexd > $nul;
$session = $matches[1];
$session_user[$session] += $username};
$logon_sessions |%{$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime);
$loggedonuser = New-Object -TypeName psobject;
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid;
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid];
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()];
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage;
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime;
$loggedonuser}
Help is appreciated.
Thanks,
Sambhav
I believe you cannot get information about other user sessions if not running PowerShell as administrator.
I am searching for a simple command to see logged on users on server.
I know this one :
Get-WmiObject -Class win32_computersystem
but this will not provide me the info I need.
It returns :
domain
Manufactureer
Model
Name (Machine name)
PrimaryOwnerName
TotalPhysicalMemory
I run Powershell 3.0 on a Windows 2012 server.
Also
Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique
gives me not the exact answers I need.
I would love to see as well the idle time, or if they are active or away.
In search of this same solution, I found what I needed under a different question in stackoverflow:
Powershell-log-off-remote-session. The below one line will return a list of logged on users.
query user /server:$SERVER
Since we're in the PowerShell area, it's extra useful if we can return a proper PowerShell object ...
I personally like this method of parsing, for the terseness:
((quser) -replace '^>', '') -replace '\s{2,}', ',' | ConvertFrom-Csv
Note: this doesn't account for disconnected ("disc") users, but works well if you just want to get a quick list of users and don't care about the rest of the information. I just wanted a list and didn't care if they were currently disconnected.
If you do care about the rest of the data it's just a little more complex:
(((quser) -replace '^>', '') -replace '\s{2,}', ',').Trim() | ForEach-Object {
if ($_.Split(',').Count -eq 5) {
Write-Output ($_ -replace '(^[^,]+)', '$1,')
} else {
Write-Output $_
}
} | ConvertFrom-Csv
I take it a step farther and give you a very clean object on my blog.
I ended up making this into a module.
There's no "simple command" to do that. You can write a function, or take your choice of several that are available online in various code repositories. I use this:
function get-loggedonuser ($computername){
#mjolinor 3/17/10
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
Maybe you can do something with
get-process -includeusername
If you want to find interactively logged on users, I found a great tip here :https://p0w3rsh3ll.wordpress.com/2012/02/03/get-logged-on-users/ (Win32_ComputerSystem did not help me)
$explorerprocesses = #(Get-WmiObject -Query "Select * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
If ($explorerprocesses.Count -eq 0)
{
"No explorer process found / Nobody interactively logged on"
}
Else
{
ForEach ($i in $explorerprocesses)
{
$Username = $i.GetOwner().User
$Domain = $i.GetOwner().Domain
Write-Host "$Domain\$Username logged on since: $($i.ConvertToDateTime($i.CreationDate))"
}
}
Here is my Approach based on DarKalimHero's Suggestion by selecting only on Explorer.exe processes
Function Get-RdpSessions
{
param(
[string]$computername
)
$processinfo = Get-WmiObject -Query "select * from win32_process where name='explorer.exe'" -ComputerName $computername
$processinfo | ForEach-Object { $_.GetOwner().User } | Sort-Object -Unique | ForEach-Object { New-Object psobject -Property #{Computer=$computername;LoggedOn=$_} } | Select-Object Computer,LoggedOn
}
Another solution, also based on query user, but can handle variations in culture (as far as I can tell) and produces strongly-typed results (i.e. TimeSpan and DateTime values):
# Invoke "query user", it produces an output similar to this, but might be culture-dependant!
#
# USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
# >jantje rdp-tcp#55 2 Active . 3/29/2021 4:24 PM
# pietje 4 Disc 49+01:01 4/14/2021 9:26 AM
$result = (&query 'user' | Out-String -Stream)
# Take the header text and insert a '|' before the start of every HEADER - although defined as inserting a bar after
# every 2 or more spaces, or after the space at the start.
$fencedHeader = $result[0] -replace '(^\s|\s{2,})', '$1|'
# Now get the positions of all bars.
$fenceIndexes = ($fencedHeader | Select-String '\|' -AllMatches).Matches.Index
$timeSpanFormats = [string[]]#("d\+hh\:mm", "h\:mm", "m")
$entries = foreach($line in $result | Select-Object -Skip 1)
{
# Insert bars on the same positions, and then split the line into separate parts using these bars.
$fenceIndexes | ForEach-Object { $line = $line.Insert($_, "|") }
$parts = $line -split '\|' | ForEach-Object { $_.Trim() }
# Parse each part as a strongly typed value, using the UI Culture if needed.
[PSCustomObject] #{
IsCurrent = ($parts[0] -eq '>');
Username = $parts[1];
SessionName = $parts[2];
Id = [int]($parts[3]);
State = $parts[4];
IdleTime = $(if($parts[5] -ne '.') { [TimeSpan]::ParseExact($parts[5], $timeSpanFormats, [CultureInfo]::CurrentUICulture) } else { [TimeSpan]::Zero });
LogonTime = [DateTime]::ParseExact($parts[6], "g", [CultureInfo]::CurrentUICulture);
}
}
# Yields the following result:
#
# IsCurrent Username SessionName Id State IdleTime LogonTime
# --------- -------- ----------- -- ----- -------- ---------
# True jantje rdp-tcp#32 2 Active 00:00:00 3/29/2021 4:24:00 PM
# False pietje 4 Disc 48.11:06:00 4/14/2021 9:26:00 AM
$entries | Format-Table -AutoSize
Team!
I have pretty nice solution to get local session as [PSObject].
Function Get-LocalSession {
<#
.DESCRIPTION
Get local session. Pasre output of command - 'query session'.
#>
[OutputType([PSObject[]])]
[CmdletBinding()]
Param(
)
try {
#region functions
#endregion
$Result = #()
$Output = . query.exe 'session' | select-object -skip 1
#use regex to parse
$pattern = '^(?<This>.)(?<SessionName>[^\s]*)\s*(?<UserName>[a-z]\w*)?\s*(?<Id>[0-9]*)\s*(?<State>\w*)\s*((?<Type>\w*)\s*)?(?<Device>\w*)?'
foreach ( $line in $output ){
$match = [regex]::Matches( $line, $pattern )
if ( $match ){
$PSO = [PSCustomObject]#{
This = $match[0].groups['This'].Value
SessionName = $match[0].groups['SessionName'].Value
UserName = $match[0].groups['UserName'].Value
Id = $match[0].groups['Id'].Value
State = $match[0].groups['State'].Value
Type = $match[0].groups['Type'].Value
Device = $match[0].groups['Device'].Value
}
$Result += $PSO
}
Else {
write-host "Unable to process line [$line] in function [Get-LocalSession]!"
}
}
}
catch {
#Get-ErrorReporting -Trap $PSItem
write-host $PSItem
}
return $Result
}
#Run it
$SessionObject = Get-LocalSession
$SessionObject | format-table -autosize -property *
I have edited mjolinor script to remove duplicate records, and dummy account names such as system, network services,...etc
If you want to get all users
function get-loggedonuser ($computername){
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
if you want to have only domain users
function get-loggedonuser ($computername){
$HST= hostname
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(Get-WmiObject win32_logonsession -ComputerName $computername)
$logon_users = #(Get-WmiObject win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |ForEach-Object {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |ForEach-Object{
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*" -and $session_user[$_.logonid] -notlike "*$HST*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
This is what I just figured out and works out great!
Get-Process -IncludeUserName | Select-Object -Unique | Where-Object {$_.UserName -notlike 'NT AUTHORITY\SYSTEM' -and $_.UserName -notlike 'NT AUTHORITY\NETWORK SERVICE' -and $_.UserName -notlike 'NT AUTHORITY\LOCAL SERVICE'} | Format-Table -Wrap -AutoSize