Avatar of Hiep Nguyen
Hiep Nguyen
 asked on

script

Hello,

I'm looking for help to write a script to find out who are on the RDS and sign some of them off.  I like to use Task Schedule to run this script.  Currently, I run shutdown.exe /f /l to sign out individual user.  There must be a way to find out a list of users on the server and loop-through one by one to force them off the server.  Thank you.
Remote AccessShell ScriptingPowershellWindows 10Azure

Avatar of undefined
Last Comment
skullnobrains

8/22/2022 - Mon
skullnobrains

psloggedon ( which you can download from the technet ) will display such a list remotely. i believe you can loop through the output and logout whoever you want using your existing command.

there is also a powershell way as well, and most likely something using the output of the existing "net" command. those should be rather easy to find online. i'll help with the crafting if you go that way.

also note that the windows management console has a snippet that allows to do such tasks graphically, though i'm unsure you can filter and logout a list of users easily
oBdA

Try this PowerShell script; works remotely, can process multiple servers.
It's in test mode, the actual logoff part is commented out so that you can test it.
Function Get-UserSession {
[CmdletBinding()]
Param(
	[Parameter(Position=0, ValueFromPipeline=$true)]
	[String[]]$ComputerName = $ENV:ComputerName
)
	Process {
		$ComputerName | ForEach-Object {
			Try {
				Write-Verbose "Processing $($_) ..."
				$out = [ordered]@{}
				$out['ComputerName'] = $_
				$out['IPAddress'] = ([System.Net.Dns]::GetHostAddresses($_) | Where-Object {$_.AddressFamily -eq 'InterNetwork'})[0].IPAddressToString
				$output = & 'C:\Windows\system32\query.exe' user /server:$_ 2>&1
				If ($output -like '*No User exists*') {
					Write-Warning "[$($_)] $($output -join ' ')"
				} ElseIf ($output -like '*Error*') {
					Throw "[$($_)] $($output -join ' ')"
				} Else {
					$output |
						Select-Object -Skip 1 |
						Where-Object {$_ -match '(?<Current>.)(?<UserName>.{20})(?<SessionName>.{16})(?<ID>.{7})(?<State>.{8})\s+(?<IdleTime>\S+)\s+(?<LogonTime>.*)'} |
							ForEach-Object {
								ForEach ($prop in 'UserName', 'SessionName', 'ID', 'State', 'IdleTime', 'LogonTime') {$out[$prop] = $Matches[$prop].Trim()}
								[PSCustomObject]$out
							}
				}
			} Catch {
				$PSCmdlet.WriteError($_)
			}
		}
	}
}

$servers = "Server1", "Server2", "Server3"
$servers |
	Get-UserSession |
	ForEach-Object {
		Write-Host "Logging off $($_.UserName) from $($_.ComputerName)"
	#	& logoff.exe $_.SessionName /SERVER:$_.ComputerName
	}

Open in new window

Hiep Nguyen

ASKER
I copied to notepad and saved to logoff.vbs, then run it but I got error.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
oBdA

Again: this is a PowerShell script.
Save as .ps1 and run from a PowerShell console.
Jose Gabriel Ortega Castro

Yeah, you need to save the text file with the PS1 extension (it's not a .vbs) because the solution given by odba is Powershell and not a visual basic script).

https://prnt.sc/sjqxo8
skullnobrains

... or run "ps c:\path\to\whatever"
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Hiep Nguyen

ASKER
Thank you for your help.

How I debug or pause at each user to examine?
Should I run this on domain controller or on each RDS?
skullnobrains

Not my script but it seems to process a list of rdp servers so it should work pretty much anywhere on a domain providing you have adequate (admin ) privileges
oBdA

You can run that on any machine remotely against a list of other machines.
Define the list of servers to run it against in line 35.
You can enter a line like
		Read-Host 'Return to continue'

Open in new window

between lines 39 and 40 to stop for each user.

And as I said, the script is in test mode and will not logoff anyone as long as line 40 stays commented.
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
Hiep Nguyen

ASKER
Try "ps logoff.ps1"
ps : Cannot find a process with the name "logoff.ps1". Verify the process name and call the cmdlet again.
At line:1 char:1
+ ps logoff.ps1
+ ~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (logoff.ps1:String) [Get-Process], ProcessCommandException
    + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand

then try ".\logoff.ps1"
Get-UserSession : Exception calling "GetHostAddresses" with "1" argument(s): "No such host is known"
At C:\Users\Administrator\Desktop\logoff.ps1:37 char:2
+     Get-UserSession |
+     ~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-UserSession], MethodInvocationException
    + FullyQualifiedErrorId : SocketException,Get-UserSession
Hiep Nguyen

ASKER
Is there a command (from cmd) to list all users currently on the server?  I don't need to do this remotely.
oBdA

You don't pass a server name to the function:
Function Get-UserSession {
[CmdletBinding()]
Param(
	[Parameter(Position=0, ValueFromPipeline=$true)]
	[String[]]$ComputerName = $ENV:ComputerName
)
	Process {
		$ComputerName | ForEach-Object {
			Try {
				Write-Verbose "Processing $($_) ..."
				$out = [ordered]@{}
				$out['ComputerName'] = $_
				$out['IPAddress'] = ([System.Net.Dns]::GetHostAddresses($_) | Where-Object {$_.AddressFamily -eq 'InterNetwork'})[0].IPAddressToString
				$output = & 'C:\Windows\system32\query.exe' user /server:$_ 2>&1
				If ($output -like '*No User exists*') {
					Write-Warning "[$($_)] $($output -join ' ')"
				} ElseIf ($output -like '*Error*') {
					Throw "[$($_)] $($output -join ' ')"
				} Else {
					$output |
						Select-Object -Skip 1 |
						Where-Object {$_ -match '(?<Current>.)(?<UserName>.{20})(?<SessionName>.{16})(?<ID>.{7})(?<State>.{8})\s+(?<IdleTime>\S+)\s+(?<LogonTime>.*)'} |
							ForEach-Object {
								ForEach ($prop in 'UserName', 'SessionName', 'ID', 'State', 'IdleTime', 'LogonTime') {$out[$prop] = $Matches[$prop].Trim()}
								[PSCustomObject]$out
							}
				}
			} Catch {
				$PSCmdlet.WriteError($_)
			}
		}
	}
}

Get-UserSession |
	ForEach-Object {
		Write-Host "Logging off $($_.UserName) from $($_.ComputerName)"
	#	& logoff.exe $_.SessionName /SERVER:$_.ComputerName
	}

Open in new window

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Hiep Nguyen

ASKER
Thanks everyone.  I'm going to write a simple program to do this using 2 DOS commands:

query user
logoff

With shutdown /f /l, it will close all opening programs/files log off the user without any warning.
Can I really do the same with logoff command?
skullnobrains

shutdown can display a warning and allow a grace time. just use /t DELAY in the command. i am unsure logoff can do the same.

the tool i pointed above allows to list users locally or remotely. the output should be reasonably easy to process in a batch file
Hiep Nguyen

ASKER
https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/query-user 

Does any one know the size for each column "query user" returns?
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
skullnobrains

i would expect it to change based on the length of the  user names. let's hope i am wrong ;)
the psloggedon tool i mentionned above should be more script-friendly if you can use it
odba's script seems ok to me as well
oBdA

Why do you want to reinvent the wheel? My function above already parses this exact output.
Hiep Nguyen

ASKER
I'm not allow to run .ps1/.bat/.vbs (a lot more) on our system.
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
skullnobrains

How do you expect to run anything ? Better tell us what IS available. From above comments, i was assuming you wanted a batch script.

Btw, whoever expects to achieve security with such limitations should probably get a different job asap.
ASKER CERTIFIED SOLUTION
Hiep Nguyen

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
oBdA

So you are not allowed to run scripts, but you're expected to administer the RDS servers? And you can introduce and run your own executables? Wow. Just ... wow.
Anyway, PowerShell can easily send mails using Send-MailMessage.
And if whatever language you're using to create the program supports RegEx, you can just use the one from line 22.
Or try to "compile" your PowerShell scripts: https://github.com/MScholtes/PS2EXE

Can you successfully run something like the following:
powershell.exe -NoExit -Command "gci C:\Windows"

Open in new window

skullnobrains

try .cmd with a batch file syntax. it will likely work.

use a dummy extension such as zzz and open it with "cmd". i am unsure but this probably also works with powershell.

you can also try various hacks such as spawning your script using the start command in a separate window, using an autoexec file in a regular directory, creating a shell extension if you can access the registry ...

windows execution protection by extension name is something you can bypass easily if you just mess a little around.

worst case scenario, you can install a separate interpreter such as bash or perl.
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
Hiep Nguyen

ASKER
As an admin, of course I can run anything but the plan is to give the *program* to user to run probably in task scheduler.  Another concern is user can modify any script and it becomes a security issue.  That's reason why I don't want to use script in this case.  I really appreciate your help and input.
skullnobrains

Non admin users will no be able to modify the script if you setup adequate privileges. But they wont be able to run it as admin either unless you both code something and give them special privileges. My windows days are old but i recall impersonate client from thread.

If your users can run preconfigured scheduled tasks as admin, that should work. Likewise a service can do the trick if they are allowed to start them. Just make sure they cannot edit the script or run a different one.
Hiep Nguyen

ASKER
I got it to work the way that I wanted.  Thank you.
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
skullnobrains

selected solution adds complexity, a false assertion, and does not answer the initial question in any way. mostly, it is a repetition of the question.