Disconnect user from server with ID

Welcome Forums General PowerShell Q&A Disconnect user from server with ID

This topic contains 7 replies, has 3 voices, and was last updated by

 
Participant
1 year, 4 months ago.

  • Author
    Posts
  • #74641

    Participant
    Points: 0
    Rank: Member

    Hi there,
    I've been working on script that will filter out users that have been active in for more than 60min or are disconnected.
    For the disconnect script, I used a simple open source script from a MS contributor, found here. The only issue I now have is filtering users based on ID. When my script runs through the server, it does not disconnect the specific user, but rather the entire server. I know the $_.ID variable is key to getting this to work, however; I am not sure how to obtain it from my script values. I've included my entire code below, the disconnect properties are at bottom.
    Thank you!

    function Disconnect-LoggedOnUser {
    
        param(
            [Parameter(
                Mandatory,
                ValueFromPipeline,
                ValueFromPipelineByPropertyName,
                Position=0
            )]
            [string[]]
                $ComputerName,
            [Parameter(
                Mandatory,
                ValueFromPipelineByPropertyName
            )]
            [int[]]
                $Id
        )
    
        begin {
            $OldEAP = $ErrorActionPreference
            $ErrorActionPreference = 'Stop'
        }
    
        process {
            foreach ($Computer in $ComputerName) {
                $Id | ForEach-Object {
                    Write-Verbose "Attempting to disconnect session $Id on $Computer"
                    try {
                        rwinsta $_ /server:$Computer
                        Write-Verbose "Session $Id on $Computer successfully disconnected"
                    } catch {
                        Write-Verbose 'Error disconnecting session displaying message'
                        Write-Warning "Error on $Computer, $($_.Exception.Message)"
                    }
                }
            }
        }
    
        end {
            $ErrorActionPreference = $OldEAP
        }
    }
    
    
    
    
    
    
    function Convert-QueryToObjects
    {
    	[CmdletBinding()]
    	[Alias('QueryToObject')]
    	[OutputType([PSCustomObject])]
    	param
    	(
    		[Parameter(Mandatory = $false,
    				   ValueFromPipeline = $true,
    				   ValueFromPipelineByPropertyName = $true,
    				   Position = 0)]
    		[Alias('ComputerName', 'Computer')]
    		[string]
    		$Name = $env:COMPUTERNAME
    	)
    	
    	Process
    	{
    		Write-Verbose "Running query.exe against $Name."
    		$Users = query user /server:$Name 2>&1
    		
    		if ($Users -like "*No User exists*")
    		{
    			# Handle no user's found returned from query.
    			# Returned: 'No User exists for *'
    			Write-Error "There were no users found on $Name : $Users"
    			Write-Verbose "There were no users found on $Name."
    		}
    		elseif ($Users -like "*Error*")
    		{
    			# Handle errored returned by query.
    			# Returned: 'Error ......'
    			Write-Error "There was an error running query against $Name : $Users"
    			Write-Verbose "There was an error running query against $Name."
    		}
    		elseif ($Users -eq $null -and $ErrorActionPreference -eq 'SilentlyContinue')
    		{
    			# Handdle null output called by -ErrorAction.
    			Write-Verbose "Error action has supressed output from query.exe. Results were null."
    		}
    		else
    		{
    			Write-Verbose "Users found on $Name. Converting output from text."
    			
    			# Conversion logic. Handles the fact that the sessionname column may be populated or not.
    			$Users = $Users | ForEach-Object {
    				(($_.trim() -replace ">" -replace "(?m)^([A-Za-z0-9]{3,})\s+(\d{1,2}\s+\w+)", '$1  none  $2' -replace "\s{2,}", "," -replace "none", $null))
    			} | ConvertFrom-Csv
    			
    			Write-Verbose "Generating output for $($Users.Count) users connected to $Name."
    			
    			# Output objects.
    			foreach ($User in $Users)
    			{
    				Write-Verbose $User
    				if ($VerbosePreference -eq 'Continue')
    				{
    					# Add '| Out-Host' if -Verbose is tripped.
    					[PSCustomObject]@{
    						ComputerName = $Name
    						Username = $User.USERNAME
    						SessionState = $User.STATE.Replace("Disc", "Disconnected")
    						SessionType = $($User.SESSIONNAME -Replace '#', '' -Replace "[0-9]+", "")
                            IdleTime = $User.'IDLE TIME'
                            ID = $User.ID
                            LogonTime =$User.'Logon Time'
    					} | Out-Host
    				}
    				else
    				{
    					# Standard output.
    					[PSCustomObject]@{
    						ComputerName = $Name
    						Username = $User.USERNAME
    						SessionState = $User.STATE.Replace("Disc", "Disconnected")
    						SessionType = $($User.SESSIONNAME -Replace '#', '' -Replace "[0-9]+", "")
                            IdleTime = $User.'IDLE TIME'
                            LogonTime = $User.'Logon Time'
                            ID = $User.ID
    					}
    				}
    			}
    		}
    	}
    }
    
    
    $Servers = Get-Content 'H:\demo\computernames.txt'
    $openservers =@()
    foreach ($Server in $Servers)
    {
        if (-not( Test-Connection $Server -Count 1 -Quiet )) { continue }
    
        if (-not( Convert-QueryToObjects $Server -ErrorAction SilentlyContinue))
        {
         
         $openservers += $server
         $openservers | Out-File 'H:\demo\session\openservers.txt'
     }
    
        else
        {  
            Convert-QueryToObjects -Name $Server | Where-Object  {@('Active','Disconnected') -contains $_.SessionState} | select @{Name='Server Name';Expression={$_.ComputerName}},
            @{Name='Username'; Expression={$_.Username}}, @{Name='Session State'; Expression={$_.SessionState}}, @{Name='Idle Time'; Expression={$_.IdleTime}}, @{Name='ID'; Expression={$_.ID}},@{Name='Logon Time';Expression={$_.LogonTime}}
    
    
        if((Convert-QueryToObjects -Name $Server|?{@('Disconnected') -contains $_.SessionState}) -or (Convert-QueryToObjects -Name $Server|Where-Object{($_.IdleTime -like "*:*") -and ($_.IdleTime -gt "00:59")}))
       
    
        {
    
            Disconnect-LoggedOnUser -ComputerName $Server -id 5 -Verbose
            Write-Output "--------------------------------"  
    
        }
    
        else { continue}
    
    
      }
      }
    
  • #74650

    Participant
    Points: 13
    Rank: Member

    Does this need to be scripted ? Why not just use group policy?

    Computer Configuration| Admin Templates | Windows Components | Remote Desktop Services | Remote Desktop Session Host | Session Time Limits

    User Configuration | Admin Templates | Windows Components | Remote Desktop Services | Remote Desktop Session Host | Session Time Limits

    • #74653

      Participant
      Points: 0
      Rank: Member

      @simon, thank you for your input, however; I am not permitted to use group policy.

  • #74680

    Keymaster
    Points: 12
    Team Member
    Rank: Member

    Hey there Russell,

    Have you looked at Warren Frame's Get-UserSession function?

    https://gallery.technet.microsoft.com/scriptcenter/Get-UserSessions-Parse-b4c97837

    You could probably write it up into a DSC config as well....hmmm...ideas...

    • #74681

      Participant
      Points: 0
      Rank: Member

      Hi Will, thank you for referring to the script.
      I've actually made a lot of progress since posting here.
      Thank you for that, I came up with something a little different. However, I am having issues with the script not overwriting the previous file and it attempting to disconnect the same session multiple times, instead of just once. I know this has to be a simple fix, but I am just not seeing it... thanks!

      $Servers = Get-Content 'H:\demo\computernames.txt'
      $openservers =@()
      foreach ($Server in $Servers)
      {
          if (-not( Test-Connection $Server -Count 1 -Quiet )) { continue }
      
          if (-not( Convert-QueryToObjects $Server -ErrorAction SilentlyContinue))
          {
      
           $openservers += $server
           $openservers | Out-File 'H:\demo\session\openservers.txt'
       }
      
          else
          {  
            Convert-QueryToObjects -Name $Server |Where-Object{ {@('Disconnected','Active') -contains $_.SessionState} | Select-Object {@{Name='Server Name';Expression={$_.ComputerName}},
              @{Name='Username'; Expression={$_.Username}}, @{Name='Session State'; Expression={$_.SessionState}}, @{Name='Idle Time'; Expression={$_.IdleTime}}, 
              @{Name='ID'; Expression={$_.ID}} }}| Export-Csv 'H:\demo\session\run11.csv' -NoTypeInformation -Append
      
      
          Import-Csv 'H:\demo\session\run11.csv' | Where-Object { ($_.SessionState -eq 'Disconnected') -or (($_.IdleTime -like "*:*") -and ($_.IdleTime -gt "00:59"))} |
          ForEach-Object {
              Disconnect-LoggedOnUser -ComputerName $_.ComputerName -Id $_.ID -Verbose 
          }
      
         }
      
         }
      
  • #74687

    Keymaster
    Points: 12
    Team Member
    Rank: Member

    You might want to take out the -Append on the Export-Csv on line 18. 🙂

  • #74689

    Keymaster
    Points: 12
    Team Member
    Rank: Member

    You might want to take out the -Append on the Export-Csv on line 18. 🙂 And add -Force to overwrite.

    • #74692

      Participant
      Points: 0
      Rank: Member

      Hi Will,
      Thank you for that.

      However, as a result of me removing

      -append

      the only thing exported to the CSV is the list of active users on the last server. The reason I added

      -append

      originally was b/c I wanted every server/user inputted into the csv.

      However, your suggestion did fix the multiple user issue. Thanks!

The topic ‘Disconnect user from server with ID’ is closed to new replies.