4

I'm using the following script to run through all of the servers in a specified Active Directory OU and log out the specified user. This runs perfectly well for the first 35 servers but always errors out on the very last server it iterates through. The error is:

Program 'quser.exe' failed to run: Object reference not set to an instance of an object. At line:6 char:24
+             $result = (quser $userAccount)
+                        ~~~~~~~~~~~~~~~~~~.
At \\path\to\script.ps1:79 char:5
+     invoke-command -ComputerName $server -ScriptBlock $scriptBlock -A ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Program 'quser....~~~~~~~~~~~~~~.:String) [], RuntimeException
    + FullyQualifiedErrorId : Program 'quser.exe' failed to run: Object reference not set to an instance of an object.At line:6 char:24
+             $result = (quser $userAccount)
+                        ~~~~~~~~~~~~~~~~~~.

My reading of the error is that it thinks $userAccount has no value. Is that correct and, if so, can anyone point at what I'm missing? Thank you in advance!

Write-Host " "
Write-Host "This script will log out all active sessions for the specified user. Proceed with caution." -ForegroundColor Black -BackgroundColor Yellow
Write-Host " "

$servers = [System.Collections.ArrayList]::new()
foreach ($server in (Get-ADComputer -SearchBase "OU=FictionalDepartment,DC=Company,DC=net" -Filter "OperatingSystem -like 'Windows Server*'" -Properties Name | select -ExpandProperty name))
{
    $servers.add($server) | Out-Null
}
$servers.Sort()

$userAccount = Read-Host "Enter account to log out"
Write-Host " "

foreach ($server in $servers)
{
    $scriptBlock = {
        param($userAccount)

        $ErrorActionPreference = 'Stop'
        try
        {
            $result = (quser $userAccount)
            $session = ((quser $userAccount)[1] -split "\s+")[2]
            logoff $session
            Write-Host "   The user was logged into session #$session. They have been LOGGED OFF." -ForegroundColor Yellow
        }
        catch
        {
            if ($_.Exception.Message -match 'No user exists')
            {
                Write-Host "   User is not logged in." -ForegroundColor Green
            }
            else
            {
                throw $_.Exception.Message
            }
        }
    }
    Write-Host "$server"
    Invoke-Command -ComputerName $server -ScriptBlock $scriptBlock -ArgumentList $userAccount
    $session = $null
}
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • What happens if you remove the `-ArgumentList` parameter from `Invoke-Command` and, in your script block, remove the `param(...)` block and use `$using:userAccount` instead of `$userAccount` ? As aside, all the `$scriptblock` variable should be outside the loop. – Santiago Squarzon Jan 19 '22 at 03:55
  • Can you just query that specific system and see if anything is returned? – Abraham Zinala Jan 19 '22 at 05:44
  • @SantiagoSquarzon, same effect using that method. Thanks for the tip -- I've moved the $scriptBlock outside of the for-loop. – Thom Stevenson Jan 19 '22 at 15:23
  • 1
    @AbrahamZinala if I run "quser /SERVER:", I do not receive the error. Perhaps I will modify the script to just run it like that instead of using Invoke-Command. – Thom Stevenson Jan 19 '22 at 15:26
  • Id recommend that. Especially if you're not 100% sure that quser.exe isn't on the remote system. – Abraham Zinala Jan 19 '22 at 17:29
  • I'd also suggest taking advantage of PowerShells stream capability, and save the results of you ADQuery directly to your `$servers` variable. So, something like: `$servers = (Get-ADComputer -SearchBase "OU=Fictional,DC=Company,DC=net" -Filter "OperatingSystem -like 'Windows Server*'").Name` – Abraham Zinala Jan 19 '22 at 17:38

1 Answers1

0

Based on feedback, I modified the script so that it no longer uses Invoke-Command, but parses the active sessions by running quser <user> /SERVER:<server> as follows:

Write-Host "Will log specified user out of all servers..." -ForegroundColor Black -BackgroundColor Yellow
Write-Host " "

$servers = [System.Collections.ArrayList]::new()
foreach ($server in (Get-ADComputer -SearchBase "OU=Fictional,DC=Company,DC=net" -Filter "OperatingSystem -like 'Windows Server*'" -Properties Name | select -ExpandProperty name))
    {
    $servers.add($server) | Out-Null
    }
$servers.Sort()

$userAccount = Read-Host "Enter account to scan for"
Write-Host ""

foreach ($server in $servers)
    {
    Write-Host "$server"
    $ErrorActionPreference = 'Stop'
    try
        {
        $session = ((quser $userAccount /SERVER:$server)[1] -split "\s+")[3]
        logoff /SERVER:$server $session
        Write-Host "   The user was logged into session #$session. They have been LOGGED OFF." -ForegroundColor Yellow
        }
    catch
        {
        if ($_.Exception.Message -match 'No user exists')
            {
            Write-Host "   User is not logged in." -ForegroundColor Green
            }
        }
    }