I am trying to trouble shoot a GPO to deploy a printer and I need to see what network printers are on a remote machine for the current logged on user. When I do this
Get-WMIObject Win32_Printer -ComputerName PCNAME
I get a list of the local installed printers and when I try this
Get-WMIObject Win32_Printer -ComputerName PCNAME | where{$_.Name -like “*\\*”} | select sharename,name
I get nothing. Any help? I am using PowerShell 4.0 so the Get-Printer doesn't work.
I borrowed heavily from tukan's code in another thread (thanks... that code bridged a gap I had not yet been able to bridge) and modified it to my purposes.
The code below determines the logged-in user on the specified remote computer, then outputs the printers that user has listed in the Registry under HKU<SID>\Printers\Connections.
As a bonus I added a couple extra lines that output the user's currently-selected default printer.
#- BEGIN FUNCTION -------------------------------------------------------------------------------------------------
Function remote_registry_query($target, $key)
{
Try {
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users", $target)
ForEach ($sub in $registry.OpenSubKey($key).GetSubKeyNames())
{
#This is really the list of printers
write-output $sub
}
} Catch [System.Security.SecurityException] {
"Registry - access denied $($key)"
} Catch {
$_.Exception.Message
}
}
#- END FUNCTION ---------------------------------------------------------------------------------------------------
##############################
# EXECUTION STARTS HERE
##############################
# Prep variables and get information for the function call
# set the computer name
$computer = "computer_name"
# get the logged-in user of the specified computer
$user = Get-WmiObject –ComputerName $computer –Class Win32_ComputerSystem | Select-Object UserName
$UserName = $user.UserName
write-output " "
write-output " "
write-output "Logged-in user is $UserName"
write-output " "
write-output " "
write-output "Printers are:"
write-output " "
# get that user's AD object
$AdObj = New-Object System.Security.Principal.NTAccount($user.UserName)
# get the SID for the user's AD Object
$strSID = $AdObj.Translate([System.Security.Principal.SecurityIdentifier])
#remote_registry_query -target $computer -key $root_key
$root_key = "$strSID\\Printers\\Connections"
remote_registry_query -target $computer -key $root_key
# get a handle to the "USERS" hive on the computer
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users", $Computer)
$regKey = $reg.OpenSubKey("$strSID\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows")
# read and show the new value from the Registry for verification
$regValue = $regKey.GetValue("Device")
write-output " "
write-output " "
write-output "Default printer is $regValue"
write-output " "
write-output " "
[void](Read-Host 'Press Enter to continue…')
Related
First and foremost thanks for your time and help.
My issue here is that I am having an erratic behavior in getting the name of the remote logged users. I want to send a message to a certain list of computers or IPs, and if somebody is logged get who saw the message.
To do so I created a script which should read and get the computer names one by one, and see if is connected. If so, get the name of the user, send the message and then show in table and write on a txt file the results:
#We read the "Lista" file with IPs or computer names, works with both
$PCLIST = Get-Content 'C:\Users\XXXX\Desktop\Lista.txt'
#Add the date tot he txt files to be created
$Fecha = date
echo $Fecha > "C:\Users\XXXX\Desktop\ListOffline.txt"
echo $Fecha > "C:\Users\XXXX\Desktop\Done.txt"
#We check every computer and if ONLINE send the message
foreach ($computer in $PCLIST)
{
if ((Test-NetConnection -ComputerName $computer -WarningAction SilentlyContinue).PingSucceeded -eq $true) #If ping back is succesfull then write the message
{
$Usuario = (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName
if ((Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName) {" "} else {$Usuario = "Usuario Remoto"}
$output = #{ 'Computer_Name / IP' = $computer }
$output.Usuario_Conectado = (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName
msg * /server:$computer "Hey!!" $Usuario ", Something meaninfull :) " #Message1
msg * /server:$computer "And something more meaningfull even ;)" #Message2
echo "$computer, avisado y recibido por $Usuario" >> "C:\Users\XXXX\Desktop\Done.txt"
}
else
{
$output = #{'Computer_Name / IP' = $computer }
$output.Usuario_Conectado = "OFFLINE"
echo $Computer >> "C:\Users\XXXX\Desktop\ListOffline.txt"
}
[PSCustomObject]$output
}
The message part works as expected and the computers, when logged, can see the messages BUT:
I can't get the names of the remote logged users on the computer's list, I get mine and the rest as empty names. In some weird variation I could, but can't find what the problem is anymore. As pre-requisites I allowed Win RM and firewall config:
I went to the computers where this will be sent and modified the registry: "reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v "AllowRemoteRPC" /t "REG_DWORD" /d "1" /f"
and opened the sshnet to allow remote access to get the names: "netsh firewall set service remoteadmin enable"
In the image in the orange circles should go the remote users logged but I get empty names
I want to send the message IF the user is logged, as remote user or on the console, now the txt file of "Done" gets written even when the user don't receive any message...
So, there should be an issue on this call: "(Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName" to get the names, this worked but I can't get the names on the remote logged users computers anymore... any idea or suggestion? I can accept get those in other form if is available and easier.
Thanks and best,
Juan
If you're using command line utilities anyway, why not use quser as its designed to get the information you need:
Function FindLogons {
Param (
[string]$Computer
)
Try {
$Query = quser /server:$Computer 2>&1
If ($LASTEXITCODE -ne 0) {
$ErrorDetail = $Query.Exception.Message
Switch -Wildcard ($Query) {
'*[1722]*' {
$Status = 'Remote RPC not enabled'
}
'*[5]*' {
$Status = 'Access denied'
}
'No User exists for*' {
$Status = 'No logged on users found'
}
default {
$Status = 'Error'
}
}
$result = [pscustomobject]#{ErrorStatus = $Status;ErrorMessage = $ErrorDetail}
$result.PSObject.Properties | ForEach-Object {
Write-Host $_.Name "`t" $_.Value
}
Return
}
Else {
Write-Host "Query complete"
}
$Query | Select-Object -Skip 1 -ErrorAction Stop | ForEach-Object {
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
# If session is disconnected different fields will be selected
If ($CurrentLine[2] -eq 'Disc') {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $null;
Id = $CurrentLine[1];
State = $CurrentLine[2];
IdleTime = $CurrentLine[3];
LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join ' '
}
# LogonTime = $CurrentLine[4..6] -join ' ';
}
Else {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $CurrentLine[1];
Id = $CurrentLine[2];
State = $CurrentLine[3];
IdleTime = $CurrentLine[4];
LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join ' '
}
}
If ($SearchResult) {
$SearchResult
}
}
}
Catch {
Write-Host "Error: $_"
}
Write-Host "Done"
}
I am currently trying to force my WSUS to decline updates through a command line because it is overloaded i cannot access the mangement snap in. This is a code that i found online but the only problem i am having is that my server has dashes in the name and is messing up the code. I tried placing qoutations around the name and it still spits back an invalid character after ":" from 3rd line
EDIT: New Error
Invalid URI: The hostname could not be parsed. at Microsoft.UpdateServices.Administration.AdminProxy.CreateUpdateServer(Object[] args) at Microsoft.UpdateServices.Administration.AdminProxy.GetupdateServer(String serverName, Boolean useSecureConnection, Int32 portnumber) at Callsite.Target(Closure , Callsite , Type , Object , Object , Object )
#Change server name and port number and $True if it is on SSL
$Computer = $env:common-n-sccm2012
$Domain = $env:airplane.black.low.com
$FQDN = "$Computer" + "." + "$Domain"
[String]$updateServer1 = $FQDN
[Boolean]$useSecureConnection = $False
[Int32]$portNumber = 8530
# Load .NET assembly
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")
$count = 0
# Connect to WSUS Server
$updateServer = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($updateServer1,$useSecureConnection,$portNumber)
write-host "<<<Connected sucessfully >>>" -foregroundcolor "yellow"
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$u=$updateServer.GetUpdates($updatescope )
foreach ($u1 in $u )
{
if ($u1.IsSuperseded -eq 'True')
{
write-host Decline Update : $u1.Title
$u1.Decline()
$count=$count + 1
}
}
write-host Total Declined Updates: $count
trap
{
write-host "Error Occurred"
write-host "Exception Message: "
write-host $_.Exception.Message
write-host $_.Exception.StackTrace
exit
}
# EOF
I think you mean the error pops up at this line:
$Computer = $env:common-n-sccm2012
because you are trying to get an environment variable with dashes in its name. You may try to format it as:
$Computer = ${env:common-n-sccm2012}
HOWEVER
I think you are 'Environmentifying' hardcoded values and that is the actual problem.
It is hard to believe that you do in fact have Environment variables with names like common-n-sccm2012 and airplane.black.low.com.
To get the actual Fully Qualified Domain Name from the computer you are on, you could do this:
$Computer = $env:COMPUTERNAME # --> "common-n-sccm2012"
$Domain = $env:USERDNSDOMAIN # --> "airplane.black.low.com"
$FQDN = "$Computer" + "." + "$Domain" # --> "common-n-sccm2012.airplane.black.low.com"
( or in one line: $FQDN = "$env:COMPUTERNAME.$env:USERDNSDOMAIN" )
You can also obtain this FQDN using WMI:
$FQDN = (Get-WmiObject win32_computersystem).DNSHostName + "." + (Get-WmiObject win32_computersystem).Domain
You can read about Environment Variables here
I've recently created a little script that allows me to get the disk size and free space of 2 servers at each school site when I provide the script with the schools 4 digit site code.
First it pulls the information on the sites from a .csv file, and then uses that information to put together a string for the DC FQDN hostname, and the .10 server.
Then it requests the password for my elevated access account used to get the information on the disks.
I am having an issue where when the script creates the script block and then uses Invoke-Command and sends the script block to the servers, and provides back the PowerShell object with the information.
The error provided is as per below:
[{ServerName}] Connecting to remote server {ServerName} failed with the
following error message : WinRM cannot process the request. The following
error with errorcode 0x80090311 occurred while using Kerberos authentication:
There are currently no logon servers available to service the logon request.
Possible causes are:
-The user name or password specified are invalid.
-Kerberos is used when no authentication method and no user name are specified.
-Kerberos accepts domain user names, but not local user names.
-The Service Principal Name (SPN) for the remote computer name and port does
not exist.
-The client and remote computers are in different domains and there is no trust
between the two domains.
After checking for the above issues, try the following:
-Check the Event Viewer for events related to authentication.
-Change the authentication method; add the destination computer to the WinRM
TrustedHosts configuration setting or use HTTPS transport.
Note that computers in the TrustedHosts list might not be authenticated.
-For more information about WinRM configuration, run the following command:
winrm help config. For more information, see the about_Remote_Troubleshooting
Help topic.
+ CategoryInfo : OpenError: ({ServerName}:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : AuthenticationFailed,PSSessionStateBroken
Things I've tried:
Resetting my password
Altering the Authentication type to Basic
Getting others to try the same thing - some have the same issue, others do not
Other users on my workstations also have the same issue
I re-imaged my workstation and it worked for a bit, but then stopped again as it appeared to stop after the device installed software updates, so I'm in the middle of uninstalling those updates, however two of them won't allow me to uninstall, I assume they're forced installs by Microsoft and required to be installed (The uninstall button disappears when selected) - KB4019472 and KB4049065.
Device is running Windows 10 1607 v14393.1944, PowerShell v5.1.
There is a one-way trust between the domain I am in and the domains the DC1 and MS10 (.10) are in, the domains trust us, but we don't trust the domains.
The account I use is local admin on the device via a nested AD Group, across all domains.
I'm not very understanding of Kerberos, so any help would be amazing.
The script is below:
Note: I've had to remove some parts, so I've filled the area with what would be there (i.e. {String} where there would just be standard text, and {FQDNServerName} where there would be a FQDN server name written as text, or {Region} where I would have had the region written as text}).
$csvSchoolsLoc = "{FQDNServerName}\SharedReports$\SchoolsExport.csv"
$Schools = Import-Csv $csvSchoolsLoc -Delimiter "`t" -Header LocCode,SchoolName,SchoolAddress,SchoolPhoneNumber,SchoolFaxNumber,SchoolOfficerInCharge,DistrictCode,DistrictNumeric,RegionCode,RegionNumeric,LSD,WANLinkType,RouterName,RouterIP,RouterStatus,OneSchemaGraphUrl,OneSchemaSiteUrl,SCCMSiteID,SiteAdminNetwork,ProxyServerIP,PrimaryDcName,PrimaryDcIP,PrimaryDcOS,PrimaryDcVersion,PrimaryDcPatch,Style
#Gets the users credentials for their GBN ZZ account - this is used throughout the script for authentication
$username = "{Region}\zz-$env:USERNAME"
$mycreds = Get-Credential -UserName $username -Message "Enter your password for {region}\zz-$env:USERNAME"
Clear-Host
Write-Host "What is the schools 4 digit site code?" -ForegroundColor Magenta
$Global:SiteCode = Read-Host
Function Main {
Clear-Host
$SchoolName = $schools | Where-Object {$_.LocCode -eq $SiteCode} | ForEach-Object SchoolName
$Region = $schools | Where-Object {$_.LocCode -eq $SiteCode} | ForEach-Object RegionCode
Write-Host "Getting details for: " -ForegroundColor Gray -NoNewline; Write-Host "$SchoolName - $SiteCode - ($Region)"-ForegroundColor Yellow
$DC1 = "{String}$($Region)$($SiteCode)001.$region.{String}.{String}.{String}"
$MS10 = "{String}$($Region)$($SiteCode)010.$region.{String}.{String}.{String}"
if (Test-Connection -ComputerName $DC1 -Count 2 -Delay 1 -Quiet) {
$DC1Run = $true
} else {
$DC1Run = $false
}
if (Test-Connection -ComputerName $MS10 -Count 2 -Delay 1 -Quiet) {
$MS10Run = $true
} else {
$MS10Run = $false
}
$ScriptBlock = {
$DiskCTotal = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='C:'" -Impersonation 3 | ForEach-Object {$_.size / 1GB}
$DiskCFree = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='C:'" -Impersonation 3 | ForEach-Object {$_.freespace / 1GB}
$DiskZTotal = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='Z:'" -Impersonation 3 | ForEach-Object {$_.size / 1GB}
$DiskZFree = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='Z:'" -Impersonation 3 | ForEach-Object {$_.freespace / 1GB}
return #{
'ZFreeSpace' = $DiskZFree
'CFreeSpace' = $DiskCFree
'ZTotalSize' = $DiskZTotal
'CTotalSize' = $DiskCTotal
}
}
if (($DC1Run -eq $true) -and ($MS10Run -eq $true)) {
$ServerDC1 = Invoke-Command -ComputerName $DC1 -Credential $mycreds -ScriptBlock $ScriptBlock
$ServerMS10 = Invoke-Command -ComputerName $MS10 -Credential $mycreds -ScriptBlock $ScriptBlock
#Clear-Host
Write-Host -ForegroundColor Yellow "$SchoolName - $SiteCode - ($Region)"
Write-Host -ForegroundColor Cyan "Server $DC1 - Domain Controller"
Write-Host "$([math]::round($ServerDC1.CFreeSpace,2)) GB free on C Drive (Total Size $([math]::round($ServerDC1.CTotalSize,2)) GB)"
Write-Host "$([math]::round($ServerDC1.ZFreeSpace,2)) GB free on Z Drive (Total Size $([math]::round($ServerDC1.ZTotalSize,2)) GB)"
Write-Host ""
Write-Host -ForegroundColor Cyan "Server $MS10 - Distribution Point"
Write-Host "$([math]::round($ServerMS10.CFreeSpace,2)) GB free on C Drive (Total Size $([math]::round($ServerMS10.CTotalSize,2)) GB)"
Write-Host "$([math]::round($ServerMS10.ZFreeSpace,2)) GB free on Z Drive (Total Size $([math]::round($ServerMS10.ZTotalSize,2)) GB)"
} else {
#Clear-Host
Write-Host -ForegroundColor Yellow "$SchoolName - $SiteCode - ($Region)"
Write-Host -ForegroundColor Cyan "Server $DC1 - Domain Controller"
if ($DC1Run) {
Write-Host "DC1 connection status is running" -ForegroundColor Green
} else {
Write-Host "DC1 connection status is down" -ForegroundColor Red
}
Write-Host ""
Write-Host -ForegroundColor Cyan "Server $MS10 - Distribution Point"
if ($MS10Run) {
Write-Host "MS10 connection status is running" -ForegroundColor Green
} else {
Write-Host "MS10 connection status is down" -ForegroundColor Red
if ($DC1Run -eq $true) {
$RDP = Read-Host -Prompt "Would you like to RDP to $DC1 'Y'"
if ($RDP -eq "Y") {
Start-Process -FilePath "$env:windir\System32\mstsc.exe" -ArgumentList "/v:$DC1" -Wait -WindowStyle Maximized
}
}
}
}
Write-Host ""
Write-Host "What is the next schools 4 digit site code? -or- Press Enter to retry the above site again" -ForegroundColor Magenta
$Entry = Read-Host
if ($Entry -eq "") {
# Do nothing
} else {
$Global:SiteCode = $Entry
}
}
$x = 0
do {
Main
} until ($x -gt 0)
EDIT: The uninstall of the software updates did not fix the issue, so unless it's something to do with those 2 updates that I can't uninstall it doesn't appear to be Software Updates.
It turns out that the domains I am trying to reach were not in my TrustedHosts config for WinRM.
By using the following command, I was able to add the domains (of which I have numerous) to the TrustedHosts using the '*' wildcard.
NOTE: I have replaced part of the domain with {String} where it would normally have part of the domain name for confidentiality reasons.
winrm set winrm/config/client #{TrustedHosts="<local>,*.{string}.edu.au"}
Is there a script that can log out my disconnected RDP session from any server? This is causing a lot of pain and constant ad account lockouts.
Any help would be awesome.
I have got the answer and I am writing this answer to help someone in need as I had to figure this out myself. I created a script using online resources to find out disconnected RDP sessions on all Windows Server in my AD environment. I run a query on each Windows Server and create a CSV formatted list, I then use that list to log out my ID from those servers, so I don't have any disconnected sessions.
I did this to make sure my AD account doesn't get locked out due to some disconnected RDP sessions when its time to change my password.
You are free to modify this script as per your need.
Script Code is below:
param (
#get current logged on username
[string]$UserName = $env:USERNAME
)
# Import the Active Directory module for the Get-ADComputer CmdLet
Import-Module ActiveDirectory
# Query Active Directory for enabled windows servers computer accounts and sort by name
$Servers = Get-ADComputer -Filter {(OperatingSystem -like "*windows*server*") -and (Enabled -eq "True")} | Sort Name
# Initiating variables
$SessionList = $NULL
$queryResults = $NULL
$SError = $null
$SDown = $null
$z = 0
# Get total number of servers
$count = $Servers.count
# Start looping through each server at a time
ForEach ($Server in $Servers) {
# initiate counter for showing progress
$z = $z + 1
$ServerName = $Server.Name
# Start writing progress
Write-Progress -Activity "Processing Server: $z out of $count servers." -Status " Progress" -PercentComplete ($z/$Servers.count*100)
# check if server is pingable before running the query on the server
if (Test-Connection $Server.Name -Count 1 -Quiet) {
Write-Host "`n`n$ServerName is online!" -BackgroundColor Green -ForegroundColor Black
Write-Host ("`nQuerying Server: `"$ServerName`" for disconnected sessions under UserName: `"" + $UserName.ToUpper() + "`"...") -BackgroundColor Gray -ForegroundColor Black
# Store results in array
[array]$queryResults += (
# Query server for specific username
query user $UserName /server:$ServerName |
foreach {
# Look for lines with Disc string to filter out active sessions
if ($_ -match "Disc") {
# format the output in CSV by replacing more than 2 spaces with a comman
write-output ("`n$ServerName," + (($_.trim() -replace ' {2,}', ',')))
}
}
)
}
# If server is not pingable show error message
else {
# Make list of server that are down.
[array]$SDown += ($ServerName)
Write-Host "`nError: Unable to connect to $ServerName!" -BackgroundColor red -ForegroundColor white
Write-Host "Either the $ServerName is down or check for firewall settings on server $ServerName!" -BackgroundColor Yellow -ForegroundColor black
}
}
# If there are some non pingable server then display the list
if ($SDown -ne $null -and $SDown) {
Write-Host "`nScript was unable to connect to the following server:" -ForegroundColor White -BackgroundColor Red
$SDown
}
# Check if any disconnected session are stored in the array
if ($queryResults -ne $null -and $queryResults) {
# Convert the CSV fromat to table format with headers
$QueryResultsCSV = $queryResults | ConvertFrom-Csv -Delimiter "," -Header "ServerName","UserName","SessionID","CurrentState","IdealTime","LogonTime"
# Show the results on console
$QueryResultsCSV |ft -AutoSize
# Go through each Disconnected session stored in the array
$QueryResultsCSV | foreach {
# Grabb session ID and ServerName
$Sessionl = $_.SessionID
$Serverl = $_.ServerName
# Show message on the console
Write-Host "`nLogging off"$_.username"from $serverl..." -ForegroundColor black -BackgroundColor Gray
sleep 2
# Logout user using session ID
logoff $Sessionl /server:$Serverl /v
}
}
else {
# if array is empty display message that no session were found
Write-Host `n`n`n`n("*" * $LineSize)
Write-Host "You are all good! No ghost sessions found!" -BackgroundColor Green -ForegroundColor Black
Write-Host ("*" * $LineSize)
}
# Pause at the end so you can capture the output
$null = Read-Host "`n`nScript execution finished, press enter to exit!"
Screenshots:
When the script is running on through all server, shows you online and offline servers:
List of servers that Script was unable to connect:
The script lists the servers where it found disconnected RDP sessions.
When script start to log your disconnected sessions off and it pauses at the end.
Thank you for your sample code. I have created a simplified code to logoff all disconnected users in the same server
$hostname = hostname
if (Test-Connection -ComputerName $hostname -Quiet -Count 1){
$result = query session /server:$hostname
$rows = $result -split "`n"
foreach ($row in $rows) {
if ($row -NotMatch "services|console" -and $row -match "Disc") {
$sessionusername = $row.Substring(19,20).Trim()
$sessionid = $row.Substring(39,9).Trim()
Write-Output "Logging Off RDP Disconnected Sessions User $sessionusername"#, $session[2], $session[3]"
logoff $sessionid /server:$hostname
}
}
}
I am trying to formulate a Powershell command to remotely log off a user. We have a terminal server with a very unstable program that sometimes locks sessions. We have to remotely log off a user but I'm trying to write a Powershell statement that will log off the person who ran the script. I Googled around, and found this command:
Invoke-Command -ComputerName MyServer -Command {shutdown -l}
However, the command returns "incorrect function." I can run other commands successfully in the brackets, such as Get-Process.
The idea is for me to put that into a script that users can run to log themselves off of the server (since when it locks, they cannot access the start menu or ALT+CTRL+END to do it through the GUI).
The flow would be this:
Bob logs into MyServer via RDP but his session freezes. On his local desktop, he can run MyScript (containing a command similar to above) which will log off his session on MyServer.
Perhaps surprisingly you can logoff users with the logoff command.
C:\> logoff /?
Terminates a session.
LOGOFF [sessionname | sessionid] [/SERVER:servername] [/V] [/VM]
sessionname The name of the session.
sessionid The ID of the session.
/SERVER:servername Specifies the Remote Desktop server containing the user
session to log off (default is current).
/V Displays information about the actions performed.
/VM Logs off a session on server or within virtual machine.
The unique ID of the session needs to be specified.
The session ID can be determined with the qwinsta (query session) or quser (query user) commands (see here):
$server = 'MyServer'
$username = $env:USERNAME
$session = ((quser /server:$server | ? { $_ -match $username }) -split ' +')[2]
logoff $session /server:$server
Here's a great scripted solution for logging people out remotely or locally. I'm using qwinsta to get session information and building an array out of the given output. This makes it really easy to iterate through each entry and log out only the actual users, and not the system or RDP listener itself which usually just throws an access denied error anyway.
$serverName = "Name of server here OR localhost"
$sessions = qwinsta /server $serverName| ?{ $_ -notmatch '^ SESSIONNAME' } | %{
$item = "" | Select "Active", "SessionName", "Username", "Id", "State", "Type", "Device"
$item.Active = $_.Substring(0,1) -match '>'
$item.SessionName = $_.Substring(1,18).Trim()
$item.Username = $_.Substring(19,20).Trim()
$item.Id = $_.Substring(39,9).Trim()
$item.State = $_.Substring(48,8).Trim()
$item.Type = $_.Substring(56,12).Trim()
$item.Device = $_.Substring(68).Trim()
$item
}
foreach ($session in $sessions){
if ($session.Username -ne "" -or $session.Username.Length -gt 1){
logoff /server $serverName $session.Id
}
}
In the first line of this script give $serverName the appropriate value or localhost if running locally. I use this script to kick users before an automated process attempts to move some folders around. Prevents "file in use" errors for me. Another note, this script will have to be ran as an administrator user otherwise you can get accessed denied trying to log someone out. Hope this helps!
Adding plain DOS commands, if someone is so inclined. Yes, this still works for Win 8 and Server 2008 + Server 2012.
Query session /server:Server100
Will return:
SESSIONNAME USERNAME ID STATE TYPE DEVICE
rdp-tcp#0 Bob 3 Active rdpwd
rdp-tcp#5 Jim 9 Active rdpwd
rdp-tcp 65536 Listen
And to log off a session, use:
Reset session 3 /server:Server100
This is oldschool and predates PowerShell, but I have used the qwinsta / rwinsta combo for YEARS to remotely log off stale RDP sessions. It's built in on at least Windows XP and forward (possibly earlier)
Determine the session ID:
qwinsta /SERVER:<NAME>
Remove the session in question:
rwinsta <SESSION_ID> /SERVER:<NAME>
You can use Invoke-RDUserLogoff
An example logging off Active Directory users of a specific Organizational Unit:
$users = Get-ADUser -filter * -SearchBase "ou=YOUR_OU_NAME,dc=contoso,dc=com"
Get-RDUserSession | where { $users.sAMAccountName -contains $_.UserName } | % { $_ | Invoke-RDUserLogoff -Force }
At the end of the pipe, if you try to use only foreach (%), it will log off only one user. But using this combination of foreach and pipe:
| % { $_ | command }
will work as expected.
Ps. Run as Adm.
Try the Terminal Services PowerShell Module:
Get-TSSession -ComputerName comp1 -UserName user1 | Stop-TSSession -Force
I've modified Casey's answer to only logoff disconnected sessions by doing the following:
foreach($Server in $Servers) {
try {
query user /server:$Server 2>&1 | select -skip 1 | ? {($_ -split "\s+")[-5] -eq 'Disc'} | % {logoff ($_ -split "\s+")[-6] /server:$Server /V}
}
catch {}
}
Log off all users from a machine:
try {
query user /server:$SERVER 2>&1 | select -skip 1 | foreach {
logoff ($_ -split "\s+")[-6] /server:$SERVER
}
}
catch {}
Details:
the try/catch is used when there are no users are on the server, and the query returns an error. however, you could drop the 2>&1 part, and remove the try/catch if you don't mind seeing the error string
select -skip 1 removes the header line
the inner foreach logs off each user
($_ -split "\s+") splits the string to an array with just text items
[-6] index gets session ID and is the 6th string counting from the reverse of the array, you need to do this because the query output will have either 8 or 9 elements depending if the users connected or disconnected from the terminal session
Below script will work well for both active and disconnected sessions as long as user has access to run logoff command remotely. All you have to do is change the servername from "YourServerName" on 4th line.
param (
$queryResults = $null,
[string]$UserName = $env:USERNAME,
[string]$ServerName = "YourServerName"
)
if (Test-Connection $ServerName -Count 1 -Quiet) {
Write-Host "`n`n`n$ServerName is online!" -BackgroundColor Green -ForegroundColor Black
Write-Host ("`nQuerying Server: `"$ServerName`" for disconnected sessions under UserName: `"" + $UserName.ToUpper() + "`"...") -BackgroundColor Gray -ForegroundColor Black
query user $UserName /server:$ServerName 2>&1 | foreach {
if ($_ -match "Active") {
Write-Host "Active Sessions"
$queryResults = ("`n$ServerName," + (($_.trim() -replace ' {2,}', ','))) | ConvertFrom-Csv -Delimiter "," -Header "ServerName","UserName","SessionName","SessionID","CurrentState","IdealTime","LogonTime"
$queryResults | ft
Write-Host "Starting logoff procedure..." -BackgroundColor Gray -ForegroundColor Black
$queryResults | foreach {
$Sessionl = $_.SessionID
$Serverl = $_.ServerName
Write-Host "Logging off"$_.username"from $serverl..." -ForegroundColor black -BackgroundColor Gray
sleep 2
logoff $Sessionl /server:$Serverl /v
}
}
elseif ($_ -match "Disc") {
Write-Host "Disconnected Sessions"
$queryResults = ("`n$ServerName," + (($_.trim() -replace ' {2,}', ','))) | ConvertFrom-Csv -Delimiter "," -Header "ServerName","UserName","SessionID","CurrentState","IdealTime","LogonTime"
$queryResults | ft
Write-Host "Starting logoff procedure..." -BackgroundColor Gray -ForegroundColor Black
$queryResults | foreach {
$Sessionl = $_.SessionID
$Serverl = $_.ServerName
Write-Host "Logging off"$_.username"from $serverl..."
sleep 2
logoff $Sessionl /server:$Serverl /v
}
}
elseif ($_ -match "The RPC server is unavailable") {
Write-Host "Unable to query the $ServerName, check for firewall settings on $ServerName!" -ForegroundColor White -BackgroundColor Red
}
elseif ($_ -match "No User exists for") {Write-Host "No user session exists"}
}
}
else {
Write-Host "`n`n`n$ServerName is Offline!" -BackgroundColor red -ForegroundColor white
Write-Host "Error: Unable to connect to $ServerName!" -BackgroundColor red -ForegroundColor white
Write-Host "Either the $ServerName is down or check for firewall settings on server $ServerName!" -BackgroundColor Yellow -ForegroundColor black
}
Read-Host "`n`nScript execution finished, press enter to exit!"
Some sample outputs. For active session:
For disconnected sessions:
if no sessions found:
Check out this solution as well to query all AD servers for your username and logoff only disconnected sessions. The script will also tell you if there were error connecting or querying the server.
Powershell to find out disconnected RDP session and log off at the same time
I am sure my code will be easier and works faster.
$logon_sessions = get-process -includeusername | Select-Object -Unique -Property UserName, si | ? { $_ -match "server" -and $_ -notmatch "admin" }
foreach ($id in $logon_sessions.si) {
logoff $id
}
here is what i came up with. combining multiple answers
#computer list below
$computers = (
'computer1.domain.local',
'computer2.domain.local'
)
foreach ($Computer in $computers) {
Invoke-Command -ComputerName $Computer -ScriptBlock {
Write-Host '______ '$Env:Computername
$usertocheck = 'SomeUserName'
$sessionID = ((quser | Where-Object { $_ -match $usertocheck }) -split ' +')[2]
If([string]::IsNullOrEmpty($sessionID)){
Write-Host -ForegroundColor Yellow "User Not Found."
} else {
write-host -ForegroundColor Green 'Logging off ' $usertocheck 'Session ID' $sessionID
logoff $sessionID
}
}
}
Below simple script will logoff all the disconnected user in the same computer
$hostname = hostname
if (Test-Connection -ComputerName $hostname -Quiet -Count 1){
$result = query session /server:$hostname
$rows = $result -split "`n"
foreach ($row in $rows) {
if ($row -NotMatch "services|console" -and $row -match "Disc") {
$sessionusername = $row.Substring(19,20).Trim()
$sessionid = $row.Substring(39,9).Trim()
Write-Output "Logging Off RDP Disconnected Sessions User $sessionusername"#, $session[2], $session[3]"
logoff $sessionid /server:$hostname
}
}
}
Output will be
Logging Off RDP Disconnected Sessions User xyz