Disconnect user from server with ID

This topic contains 7 replies, has 3 voices, and was last updated by Profile photo of russell karr russell karr 2 weeks, 2 days ago.

  • Author
    Posts
  • #74641
    Profile photo of russell karr
    russell karr
    Participant

    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
    Profile photo of Simon B
    Simon B
    Participant

    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
      Profile photo of russell karr
      russell karr
      Participant

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

  • #74680
    Profile photo of Will Anderson
    Will Anderson
    Keymaster

    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
      Profile photo of russell karr
      russell karr
      Participant

      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
    Profile photo of Will Anderson
    Will Anderson
    Keymaster

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

  • #74689
    Profile photo of Will Anderson
    Will Anderson
    Keymaster

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

    • #74692
      Profile photo of russell karr
      russell karr
      Participant

      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!

You must be logged in to reply to this topic.