I'm working on a script that allows me to fetch all of our Teams groups by their IDs and list the Id, Name, Owner(s), Member(s) and Guest(s).
The code works to a certain point, I get all the needed information, but it seems to be limiting it to 2 owners, 4 members and no guests...
When I run the code with adding it to a PSObject and simply do a write-host all the data is there, but I can't append it correctly to a CSV.
Code below, its either a limitation of the PSObject or I am doing something wrong/ missing something (hoping on the 2nd part ;) )
try
{
$host.Runspace.ThreadOptions = "ReuseThread"
# Get the credentials
Connect-AzureAD
# Connect to Microsoft Teams
Connect-MicrosoftTeams
# Get all the teams from tenant
[array]$teamColl = $null
[array]$ownerColl = $null
[array]$memberColl = $null
[array]$guestColl = $null
$teamColl=Get-Team
$date = Get-Date -Format "yyyy-MM-dd"
$OutputFile01 = "C:\temp\GetTeamsOwnersAndMembers-$date.csv"
# Clean file
Remove-Item $OutputFile01 -ErrorAction SilentlyContinue
$objectCollection=#()
$ownerCount = 0
$memberCount = 0
$guestCount = 0
# Loop through the teams
foreach($team in $teamColl)
{
$object = New-Object PSObject
# Get the Teams basic information
$object | Add-Member -type NoteProperty -Name ID -Value $team.GroupId
$object | Add-Member -type NoteProperty -Name TeamsName -Value $team.DisplayName
#$object | Add-Member -type NoteProperty -Name Description -Value $team.Description
# Get the Teams owners
$ownerColl = Get-TeamUser -GroupId $team.GroupId -Role Owner
$memberColl = Get-TeamUser -GroupId $team.GroupId -Role Member
$guestColl = Get-TeamUser -GroupId $team.GroupId -Role Guest
#Write-Host "$ownerColl"
#Write-Host "$memberColl"
#Write-Host "$guestColl"
# Loop through the owners
foreach($owner in $ownerColl)
{
$ownerCount++
$object | Add-Member -type NoteProperty -Name Owner_$ownerCount -Value $owner.User
}
# Loop through the members
foreach($member in $memberColl)
{
$memberCount++
$object | Add-Member -type NoteProperty -Name Member_$memberCount -Value $member.User
}
# Loop through the guests
foreach($guest in $guestColl)
{
$guestCount++
$object | Add-Member -type NoteProperty -Name Guest_$guestCount -Value $guest.User
}
# Reset counters
$ownerCount = 0
$memberCount = 0
$guestCount = 0
$objectCollection += $object
}
$objectCollection | Export-Csv $OutputFile01 -NoTypeInformation
}
catch [System.Exception]
{
Write-Host -ForegroundColor Red $_.Exception.ToString()
}
finally
{
Write-Host "Done"
}
Was able to solve it, I needed to use the -join to add the additional users :)
Working code:
try
{
$host.Runspace.ThreadOptions = "ReuseThread"
# Get the credentials
Connect-AzureAD
# Connect to Microsoft Teams
Connect-MicrosoftTeams
# Get all the teams from tenant
[array]$teamColl = $null
[array]$ownerColl = $null
[array]$memberColl = $null
[array]$guestColl = $null
$teamColl=Get-Team
$date = Get-Date -Format "yyyy-MM-dd"
$OutputFile01 = "C:\temp\GetTeamsOwnersAndMembers-$date.csv"
# Clean file
Remove-Item $OutputFile01 -ErrorAction SilentlyContinue
$GroupsCSV=#()
Write-Host -ForegroundColor Green "Processing Groups"
# Loop through the teams
foreach($team in $teamColl)
{
$ownerCount = 0
$memberCount = 0
$guestCount = 0
Write-Host -ForegroundColor Yellow -NoNewline "."
$ownerColl = Get-TeamUser -GroupId $team.GroupId -Role Owner
$ownerCollection=#()
# Loop through the owners
foreach($owner in $ownerColl)
{
$ownerCount++
$ownerCollection += $owner.User
}
$memberColl = Get-TeamUser -GroupId $team.GroupId -Role Member
$memberCollection=#()
# Loop through the members
foreach($member in $memberColl)
{
$memberCount++
$memberCollection += $member.User
}
$guestColl = Get-TeamUser -GroupId $team.GroupId -Role Guest
$guestCollection=#()
# Loop through the guests
foreach($guest in $guestColl)
{
$guestCount++
$guestCollection += $guest.User
}
# Create CSV file line
$GroupsRow = [pscustomobject]#{
GroupId = $team.GroupId
Name = $team.DisplayName
OwnerCount = $ownerCount
MemberCount = $memberCount
GuestCount = $guestCount
Owners = $ownerCollection -join " | "
Members = $memberCollection -join " | "
Guests = $guestCollection -join " | "
}
# Add to export array
$GroupsCSV+=$GroupsRow
}
# Export to CSV
Write-Host -ForegroundColor Green "`nCreating and exporting CSV file"
$GroupsCSV | Export-Csv -NoTypeInformation -Path $OutputFile01
}
catch [System.Exception]
{
Write-Host -ForegroundColor Red $_.Exception.ToString()
}
finally
{
Write-Host "Done"
}
I'm seeing output issues within the 'Not Found' column, even when DisplayName is not null it's marking mailbox as 'Mailbox Not Found'. Seems ok for the first 5 output lines until until it hits the first 'Cloud' DisplayName then it fills every cell with 'Mailbox Not Found' in the 'Not Found' column.
****if (($mb1 -eq 'No Cloud Mailbox') -and ($mb2 -eq 'No Onprem Mailbox')) { $mb3 = 'Mailbox Not Found' }****
Am I missing something? Any help would be appreciated.
#========
#Get date
#========
$date = Get-Date -format dd-MM-yy
#===========================
#Setting up global variables
#===========================
$allmbadinfo = #()
$mbadinfo = #()
$users = Get-Content D:\import\allrgs.txt
#=================
#Grab mailbox info
#=================
Foreach ($user in $users ) {
$mb1 = Get-RemoteMailbox $User
if ($mb1 -ne $null) { $mb1 = $mb1.DisplayName }
else {
$mb1 = 'No Cloud Mailbox' }
$mb2 = Get-Mailbox $User
if ($mb2 -ne $null) { $mb2 = $mb2.DisplayName }
else {
$mb2 = 'No Onprem Mailbox' }
if (($mb1 -eq 'No Cloud Mailbox') -and ($mb2 -eq 'No Onprem Mailbox')) { $mb3 = 'Mailbox Not Found' }
$imputlist = Write-Output $user
#================================================================================================
#Create new array object and populate information from variables, add table column names and data
#================================================================================================
$mbadinfo = New-Object PSObject
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Cloud' -Value $mb1
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Onprem' -Value $mb2
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Not Found' -Value $mb3
$mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Imput List' -Value $imputlist
$allmbadinfo += $mbadinfo
}
#======================
# Exporting data to csv
#======================
$allmbadinfo | Export-Csv D:\export\remotembxrgs-$date.csv -NoType -NoClobber
The code will become clearer if you introduce a few more variables so you are not re-using $mb1, $mb2 and $mb3.
The main problem with that is that once your variable $mb3 has been set to 'Mailbox Not Found', it will keep that value forever, since your last if does not state what it should become when the condition is not met.
Try this:
#================================
#Get date and read the users file
#================================
$date = Get-Date -format dd-MM-yy
$users = Get-Content D:\import\allrgs.txt
#=================
#Grab mailbox info
#=================
$allmbadinfo = foreach ($user in $users ) {
# test for remote mailbox
$mb1 = Get-RemoteMailbox $user -ErrorAction SilentlyContinue
$remote = if ($mb1) { $mb1.DisplayName } else { 'No Cloud Mailbox' }
# test for on premise mailbox
$mb2 = Get-Mailbox $user -ErrorAction SilentlyContinue
$onPrem = if ($mb2) { $mb2.DisplayName } else { 'No Onprem Mailbox' }
# if both are $null, set this to 'Mailbox Not Found'
$noMailBox = if (!$mb1 -and !$mb2) { 'Mailbox Not Found' } else { '' }
#================================================================================================
# Create new array object and populate information from variables, add table column names and data
#================================================================================================
# for Powershell v3.0 and newer:
# output an object to get collected in variable $allmbadinfo
[PsCustomObject]#{
'Cloud' = $remote
'Onprem' = $onPrem
'Not Found' = $noMailBox
'Input List item' = $user
}
# for PowerShell versions less than 3.0:
# $mbadinfo = New-Object PSObject
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Cloud' -Value $remote
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Onprem' -Value $onPrem
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Not Found' -Value $noMailBox
# $mbadinfo | Add-Member -MemberType 'NoteProperty' -Name 'Input List item' -Value $user
# output this object to get collected in variable $allmbadinfo
# $mbadinfo
}
#======================
# Exporting data to csv
#======================
$allmbadinfo | Export-Csv "D:\export\remotembxrgs-$date.csv" -NoType -NoClobber
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 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.
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