Foreach not iterating each server and command

$os = ((Get-WmiObject -Class Win32_OperatingSystem).Caption).Replace(",", "").Replace("(R)", "").Replace("Microsoft ", "").Replace(" Edition", "").Replace("Datacenter", "DC").Replace("Standard", "Std").Replace("Enterprise", "Ent").Replace("without", "w/o").Replace("Windows ", "Win ").Replace("Server ", "Srv ").Trim()
$servers = @(get-content "D:\Temp\UserTest.txt")
$users = (quser) -ireplace "\s{2,}","," | ConvertFrom-Csv | Where-Object {($_.USERNAME -like "*user1*") -or ($_.USERNAME -like "*user2*") -or ($_.USERNAME -like "*user3*") -or ($_.USERNAME -like "*user4*")}
$myOutput = "" | Select-Object @{n="Server";e={$env:COMPUTERNAME}}, @{n="Operating System";e={$os}}, @{n="User";e={($users.USERNAME).ToUpper() -replace ">", ""}}
foreach ($server in $servers) {
    if ($users -eq "*user1*" -or "*user2*" -or "*user3*" -or "*user4*") {
        Write-Output = $myOutput 
  } else {
        Write-Output = $null
        }
    }

Hello, 2nd time posting, so still fairly new and I know i’m just missing something with objects on this script, but can’t figure it out.
I want the output to include the quser output from all servers, but instead I get the quser output from the server I’m running the script on only. And 3 separate outputs of that because the text file contains 3 servers at the moment.

Server           Operating System     User    
------           ----------------     ----   
TSTEST2016-1     Win Srv 2016 Std     User1
=
TSTEST2016-1     Win Srv 2016 Std     User1
=
TSTEST2016-1     Win Srv 2016 Std     User1

I know the “write-output” parts are wrong, but i was just trying something at the moment
Everything works except the foreach iteration, and i don’t know why i have a hard time grasping this part.
Any help would be appreciated.

Hmm … your code looks a little messed up and is very hard to read. I’d recommend to start with something a little more structured.

$ServerList = Get-Content -Path 'D:\Temp\UserTest.txt'

$Result = 
foreach ($ComputerName in $ServerList) {
    $OS = Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ComputerName | Select-Object -Property Caption
    $UserList = @(quser /SERVER:$ComputerName | Select-Object -Skip 1) -split "\n" |  ForEach-Object { ($_ -split '\s+')[1] }

    [PSCustomObject]@{
        ComputerName    = $ComputerName
        OperatingSystem = $OS.Caption
        UserList        = $UserList -join ','
    }
}

$Result

From there you can start to modify it as needed.

1 Like
$myTable = 
foreach ($ComputerName in $ServerList) {
    $OS = Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ComputerName | Select-Object -Property Caption
    $Userlist = @(quser /SERVER:$ComputerName | Select-Object -Skip 1) -split "\n" |  ForEach-Object { ($_ -split '\s+')[1] }  | 
    Where-Object {($_ -like '*user1*') -or ($_ -like '*user2*') -or ($_ -like '*user3*') -or ($_ -like '*user4*') -or ($_ -like '*user5*') -or ($_ -like '*user6*') -or ($_ -like '*user7*') -or ($_ -like '*user8*') -or ($_ -like '*user9*')}

    [PSCustomObject]@{
        Servers         = $ComputerName.ToUpper()
        OperatingSystem = $OS.Caption
        Users           = $UserList.ToUpper() -join ','
    }
}
$myTable

This works great. Thanks, Olaf.
One last question, the command will run perfectly, and somewhat inadvertently so.
The “users” are people from my team, so just trying to automate where everyone is logged on or disconnected. I want to exclude where it’s not a user from my team, and it’s doing it, but it’s doing it because the parameters for users gives a null-valued expression. So it throws a lot of errors, but in the end, gives me the right output.

You cannot call a method on a null-valued expression.
At D:\~Scripts\Test\GetUsers.ps1:70 char:5
+     [PSCustomObject]@{
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

So the question is, how to ignore empty objects to not throw errors, but still give me the output of just my team?

I cannot test at the moment but to cleanup your code a little more you should provide a list of your users and check if the logged on user you get from a server is in this list of users. That’s much easier to read I think and in case easier to maintain. But the usernames have to be complete for this way of checking. You cannot use wildcards.

$myTable = 
foreach ($ComputerName in $ServerList) {
    $UserList = 'User1','User2','User3'
    $OS = Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ComputerName | Select-Object -Property Caption
    $Userlist = @(quser /SERVER:$ComputerName | Select-Object -Skip 1) -split "\n" |  ForEach-Object { ($_ -split '\s+')[1] }  | 
    Where-Object { $_ -in $UserList }

    [PSCustomObject]@{
        Servers         = $ComputerName.ToUpper()
        OperatingSystem = $OS.Caption
        Users           = $UserList.ToUpper() -join ','
    }
}
$myTable
1 Like

Sorry about that. Still learning, so I definitely make things too complicated and busy sometimes.
Thank you for the suggestion, that worked as well.

There’s no need to appolgize. We all started once. :wink:

1 Like