1

I am trying to write a Powershell script that will logoff a currently logged in user. I am using the Invoke-Command cmdlet with a scriptblock inside of the script.

I defined some parameters in the script that I am trying to pass to the script block but I can quite get it to work.

Here's the script:

param(
    [Parameter()]
    [string]$ComputerName,
    
    [Parameter()]
    [string]$Username
)


$ScriptBlock = {

     $ErrorActionPreference = 'Stop'
     try {
         ## Find all sessions matching the specified username
         $sessions = quser | Where-Object {$_ -match "$args[0]"}
         ## Parse the session IDs from the output
         $sessionIds = ($sessions -split ' +')[2]
         Write-Host "Found $(@($sessionIds).Count) user login(s) on computer."
         ## Loop through each session ID and pass each to the logoff command
         $sessionIds | ForEach-Object {
             Write-Host "Logging off session id [$($_)]..."
             logoff $_
         }
     } catch {
         if ($_.Exception.Message -match 'No user exists') {
             Write-Host "The user is not logged in."
         } else {
             throw $_.Exception.Message
         }
     }
 }


Invoke-Command -ComputerName $ComputerName -Argumentlist $Username -ScriptBlock $ScriptBlock

I am launching the script like this:

.\Logoff-User.ps1 -Computername some_server -Username some_user

Now this actually works but it logs off a random user (probably not random in all fairness).

The way I understand it is that the (the $Username) variable from the -ArgumentList is passed to the scriptblock and it seems to be interpreted correctly. I can print out the $args variable using the Write-Host further down and it returns the correct username.

Only using $args errors out but specifying the first position ($args[0]) works but disconnects a random user.

I am obviously doing something wrong but I don't understand why. The scripts probably not behaves that way I think it does.

Thanks!

Noct03
  • 37
  • 1
  • 7
  • 1
    Change `{$_ -match "$args[0]"}` to `{$_ -match "\b$([regex]::Escape($args[0]))\b"}` – Mathias R. Jessen Nov 11 '20 at 15:10
  • 1
    Why use `$args[0]` when you have defined a named parameter `$Username` for it? – Theo Nov 11 '20 at 15:22
  • @MathiasR.Jessen Thanks for the reply, I tried sur suggestion but it didn't work. The variable doesn't seem to contain the username but the "title" of a column in the output (Logging off session id [SESSION]) – Noct03 Nov 11 '20 at 15:46
  • @Theo Thanks for the reply. I tried it first but it didn't seem to work. The $session variable doesn't contain the actual username when I do that, it seems to contain the title of a column of the output (same as above). It contains SESSION instead of the username. – Noct03 Nov 11 '20 at 15:48
  • 1
    @Noct03 It seems then that the username you provide is not just the string containing the username, but probably the output of a Format-Table on an object. Check what you are sending to the script, it should be 2 **strings** – Theo Nov 12 '20 at 13:41
  • Thanks @Theo, it does seem like it since it's the first row of the output. I am not sure how I can only get the row containing the username though. It seems like the output is a "big string" (if that makes sense), hence there's no actual row. Is it because I am using a variable from the initial script inside of a script block? Thanks! – Noct03 Nov 12 '20 at 18:17
  • @Noct03 Please show us exactly how you obtain the username. Are you getting that with some construction like `Select-Object Username` without specifying the `-ExpandProperty` switch for instance? – Theo Nov 12 '20 at 20:17
  • @Theo It's actually a parameter ([param]) in the script, and I manually specify the username when launching the script. I run the script as follow: `.\Logoff-User.ps1 -Computername some_server -Username some_user`. Thanls – Noct03 Nov 12 '20 at 20:56
  • 1
    @Noct03 Yes, i know, but I suspect that you are not **sending** a string. Check what you have in `some_user`. It is definitively not just a username, probably an object or the string representation of an object. – Theo Nov 12 '20 at 22:15
  • Thanks @Theo, I was able to figure it out thanks to your help. – Noct03 Nov 13 '20 at 14:41

1 Answers1

0

I figured it out thanks to Theo. The $Username variable that I was using in the script was not correctly passed to the scriptblock. I had to redefine a parameter inside of the script block that would then use the -Argument from the Invoke-Command to pass the variable as a string (which the first script was also not doing.

Here's the final script:

param(
    [Parameter()]
    [string]$ComputerName,
    
    [Parameter()]
    [string]$Username
)

$ScriptBlock = {

     param($User)
     $ErrorActionPreference = 'Stop'
     try  {
         ## Find all sessions matching the specified username
         $sessions = quser | Where-Object {$_ -match $User}
         ## Parse the session IDs from the output
         $sessionIds = ($sessions -split ' +')[2]
         Write-Host "Found $(@($sessionIds).Count) user login(s) on computer."
         ## Loop through each session ID and pass each to the logoff command
         $sessionIds | ForEach-Object {
             Write-Host "Logging off session id [$($_)]..."
             logoff $_
         }
     } catch {
         if ($_.Exception.Message -match 'No user exists') {
             Write-Host "The user is not logged in."
         } else {
             throw $_.Exception.Message
         }
     }
 }

Invoke-Command -ComputerName $ComputerName -Argumentlist $Username -ScriptBlock $ScriptBlock

I was then able to run the script as needed:

.\Logoff-User.ps1 -Computername some_server -Username some_user

Thanks Theo and everyone else for your help!

Noct03
  • 37
  • 1
  • 7