I need some way to be able to run the below script as Administrator.
Script to get the Security Event log:
$DateAfter = (Get-Date).AddDays(-1)
$DateBefore = (Get-Date)
$EventLogTest = Get-EventLog -LogName Security -InstanceId 4625 -Before $DateBefore -After $DateAfter -Newest 5
$WinEventTest = Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = 4625; StartTime = $DateAfter; EndTime = $DateBefore } -MaxEvents 5
Write-Host "$EventLogTest result is: "
$EventLogTest
Write-Host "$WinEventTest result is: "
$WinEventTest
I have compiled the below snippets, but somehow, the result is not displayed or nothing?
Combined Script:
$Role = "Domain Admins"
$CurrentLoginPrincipal = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent())
$IsDomainAdminGroupMember = $CurrentLoginPrincipal.IsInRole($Role)
$IsLocalComputerAdminMember = $CurrentLoginPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
If( -not ($IsDomainAdminGroupMember -and $IsLocalComputerAdminMember) ) {
Write-Warning "You are not running this as $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN). The script will be re-executed as Local Administrator"
Try {
Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"" -Verbose
}
Catch {
Write-Warning -Message "[PROCESS] Something wrong happened"
Write-Warning -Message $Error[0].Exception.Message
$out.Details = $_.Exception.Message
Write-Host " ERROR: $($out.Details)" -ForegroundColor Red
}
}
Else {
#a user running the script has the Domain Admins and Local PC Admin rights
Write-Host " $($CurrentLoginPrincipal.Identity.Name.ToString()) is currently member of $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN) " -ForegroundColor Green
}
$DateAfter = (Get-Date).AddDays(-1)
$DateBefore = (Get-Date)
$EventLogTest = Get-EventLog -LogName Security -InstanceId 4625 -Before $DateBefore -After $DateAfter -Newest 5
$WinEventTest = Get-WinEvent -FilterHashtable #{ LogName = 'Security'; Id = 4625; StartTime = $DateAfter; EndTime = $DateBefore } -MaxEvents 5
Write-Host "$EventLogTest result is: "
$EventLogTest
Write-Host "$WinEventTest result is: "
$WinEventTest
However, it is still not executing as Administrator to get the result displayed. How can I fix this?
First thing I noticed is that your if condition is wrong. It uses -and where that should be or (because either a Domain admin OR a local Administrator can run this)
Next, the arguments for Start-Process are incorrect. Personally, I like using the -ArgumentList as array.
Finally, in the catch block you use an undefined variable $out with an equally undefined property $out.Details. In the code below I have changed that to simply re-throw the exception.
Starting from where the if..else is:
if( -not ($IsDomainAdminGroupMember -or $IsLocalComputerAdminMember) ) {
Write-Warning "You are not running this as $($Role) or Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN). The script will be re-executed as Local Administrator"
# give the user some time to see this message
Start-Sleep 4
# Build base arguments for powershell.exe as string array
$argList = '-NoLogo', '-NoProfile', '-NoExit', '-ExecutionPolicy Bypass', '-File', ('"{0}"' -f $PSCommandPath)
# Add script arguments if any
$argList += $MyInvocation.BoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key)", "$($_.Value)"}
try {
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList -Verbose -ErrorAction Stop
# exit the current script.
exit # Use return if you want to keep this instance open aswell
}
catch {
throw
}
}
else {
#a user running the script has the Domain Admins and Local PC Admin rights
Write-Host " $($CurrentLoginPrincipal.Identity.Name.ToString()) is currently member of $($Role) and Local Administrator of $($ENV:COMPUTERNAME).$($ENV:USERDNSDOMAIN) " -ForegroundColor Green
}
I've got a Powershell script that stops an IIS website and corresponding app pool and then deletes the app logs (log4net logs). Here is the script snippet:
stop-website -name "MyWebsite"
stop-webapppool -name "MyWebsite"
del c:\inetpub\MyWebsite\logs\*.*
The problem is stop-website and stop-webapppool seem to return before the website is completely shutdown which results in the delete failing saying the file is being used by another process:
del : Cannot remove item C:\inetpub\MyWebsite\logs\App.log: The process cannot access the file 'C:\inetpub\MyWebsite\logs\App.log' because it is being used by another process.
If I add a 10 second sleep between the stop commands and the del command then the logs are deleted successfully. This is very hackish though and not reliable. Is there a way to force the stop-website/stop-webapppool commands to not return until the website/apppool is completely stopped?
Thanks.
Implemented solution from the below link. I will wait ~60 seconds and then kill the IIS process if it hasn't stopped.
https://greenfinch.ie/blog/powershellscript.html
"Stopping IIS site [$name]" >> $logFile
stop-website -name $name
"Stopping app pool [$name]" >> $logFile
stop-webapppool -name $name
$sleepTime = 5
$processId = $TRUE
while ($processId)
{
$processId = Get-WmiObject -Class win32_process -filter "name='w3wp.exe'" |
?{ ($_.CommandLine).Split("`"")[1] -eq $name } |
%{ $_.ProcessId }
if ($sleepTime -gt 60)
{
"Waited [$sleepTime] sec for process [$processId] to stop and it is still running. Killing it." >> $logFile
Stop-Process $processId
break
}
if ($processId)
{
"App pool [$name] is running with process ID: [$processId]. Sleeping for [$sleepTime] sec and then checking again." >> $logFile
Start-Sleep -s $sleepTime
$sleepTime = $sleepTime + 10
}
}
You can use these two commands to check the status of the website/app, say after 10 seconds, then use an If statement to delete logs only when the status returned is stopped
Get-WebsiteState -name "MyWebsite"
Get-WebAppPoolState -name "MyWebsite"
This loop should help you too
$currentRetry = 0;
$success = $false;
do{
$status = Get-WebAppPoolState -name "MyWebsite"
if ($status -eq "Stopped"){
<....your code here....>
$success = $true;
}
Start-Sleep -s 10
$currentRetry = $currentRetry + 1;
}
while (!$success -and $currentRetry -le 4)
Updated Apr 24, 2019
Based on comment and current cmdlet document, it appears the return type is indeed an object. Thus presumably can be handled as commented or the line snippet below. Author no longer have access to Windows Server environment therefore did not directly modify original answer nor able to test the update
if ($status.Value -eq "Stopped")
After you run 'Stop-WebAppPool' the state of the WebAppPool will be "Stopping" and it may take a few seconds before the state of the WebAppPool is actually "Stopped".
Here is a little function to help with the WebAppPoolState
function Stop-AppPool ($webAppPoolName,[int]$secs) {
$retvalue = $false
$wsec = (get-date).AddSeconds($secs)
Stop-WebAppPool -Name $webAppPoolName
Write-Output "$(Get-Date) waiting up to $secs seconds for the WebAppPool '$webAppPoolName' to stop"
$poolNotStopped = $true
while (((get-date) -lt $wsec) -and $poolNotStopped) {
$pstate = Get-WebAppPoolState -Name $webAppPoolName
if ($pstate.Value -eq "Stopped") {
Write-Output "$(Get-Date): WebAppPool '$webAppPoolName' is stopped"
$poolNotStopped = $false
$retvalue = $true
}
}
return $retvalue
}
you can run this function using e.g.
Stop-AppPool "MyWebsite" 30
and check the return-value to see if the WebAppPool has stopped within the given seconds
The simplest way to stop the app pool and get it into Stopped state is to use appcmd.exe. It will return when the app pool is really stopped or you'll get an error
Just do this on PowerShell:
& $env:windir\system32\inetsrv\appcmd.exe stop apppool /apppool.name:"YourAppPoolName"
When your AppPool is correctly stooped you'll get this message:
"YourAppPoolName" successfully stopped
I fix the #user4531 code It would be failed if the app pool is stopped before :
function Stop-AppPool ($webAppPoolName,[int]$secs) {
$retvalue = $false
$wsec = (get-date).AddSeconds($secs)
$pstate = Get-WebAppPoolState -Name $webAppPoolName
if($pstate.Value -eq "Stopped") {
Write-Output "WebAppPool '$webAppPoolName' is stopped already"
return $true
}
Stop-WebAppPool -Name $webAppPoolName
Write-Output "$(Get-Date) waiting up to $secs seconds for the WebAppPool '$webAppPoolName' to stop"
$poolNotStopped = $true
while (((get-date) -lt $wsec) -and $poolNotStopped) {
$pstate = Get-WebAppPoolState -Name $webAppPoolName
if ($pstate.Value -eq "Stopped") {
Write-Output "WebAppPool '$webAppPoolName' is stopped"
$poolNotStopped = $false
$retvalue = $true
}
}
return $retvalue
}
It can use like this :
Stop-AppPool "SSO" 30
Here is how I did it with Get-IISServerManager.
$manager = Get-IISServerManager
$site = $manager.Sites["mySiteName"]
if($site.State -ne "Stopped") {
$site.Stop()
}
while ($site.State -ne "Stopped") {
"waiting 1 second for site to stop..."
Start-Sleep -s 1
}
"site stopped"
I am using below script to connect to remote server and shut the cluster service and then deploy packages.
This is cluster service shut down script.
$SvcName = '${bamboo.ServiceName}'
$SvrNames = '${bamboo.deploy.hostname}'
#$SvcName = "'" + $SvcName + "'"
$SvrName = $SvrNames[0]
try {
$services = Get-WmiObject -Computer $SvrName -Authentication PacketPrivacy -Namespace 'root\mscluster' MSCluster_Resource |
Where {$_.Type -eq "Generic Service"} |
Where {$_.name -eq $SvcName}
if (-Not $Services) {
$SvcName + " is not installed on this computer."
} else {
Switch($services.state) {
'2' {
Write-Host "Cluster service $SvcName is online"
$SvcName = "'" + $SvcName + "'"
$cmd = "CLUSTER RES" + ' ' + $SvcName + ' ' + "/OFF"
$cmd1 = [scriptblock]::Create($cmd)
Invoke-Command -ComputerName $SvrName -ScriptBlock $cmd1
Start-Sleep -s 10
Write-Host "$SvcName is Offline"
}
'3' {
Write-Host "Cluster service $SvcName is Offline"
Write-Host $_.Exception
Write-Host $_.Exception.Message
Start-Sleep -s 10
break
}
'4'{
Write-Host "Cluster service $SvcName is in Falied state, Please login to $SvrNames and check event logs"
Start-Sleep -s 10
}
}
}
} catch {
$error[0].Exception
Write-Host $_.Exception
Write-Host $_.Exception.Message
break
}
Why does Bamboo does not fail when there is a clear exception or an error message in the deploy logs?
Do I need to do something different here?
$LASTEXITCODE doesn't work as well.
I've found another work around.
Most of the errors in PowerShell throws exceptions. For and unknown reason, Bamboo doesn't seem to take that into account when executing inline scripts.
We can fix that by doing our own exception catching inline, like:
try
{
Call here some fancy PowerShell functions
}
catch
{
Write-Host $_.Exception.GetType().FullName, $_.Exception.Message
exit 500
# or whatever code not 0
}
With that, I was able to have the deployment properly failed.
The inline check for $error[0] stated above also works but maybe not will all exceptions.
exit $LASTEXITCODE works for me (if the command which is failing is the previous command)
We have over 1500 servers. Windows 2003, 2008 and 2012. I have to gather the details of antivirus(Product Name & Version) on these servers.
There could be multiple antivirus product.
I am not sure powershell script will work on 2003 server.
So, far i tried below scripts but not got useful information.
$av = get-wmiobject -class "Win32_Product" -namespace "root\cimv2" `
-computername "." -filter "Name like '%antivirus%'"
Below script is working fine on client operating system.
$wmiQuery = "SELECT * FROM AntiVirusProduct"
$AntivirusProduct = Get-WmiObject -Namespace "root\SecurityCenter2" -Query $wmiQuery #psboundparameters # -ErrorVariable myError -ErrorAction 'SilentlyContinue'
Write-host $AntivirusProduct.displayName
Can anybody advise me on this?
I am trying to get the details of antivirus(Product & Version)
What do i need to do for win server 2003?
You were on the right path, the following Powershell script works.
function Get-AntiVirusProduct {
[CmdletBinding()]
param (
[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias('name')]
$computername=$env:computername
)
#$AntivirusProducts = Get-WmiObject -Namespace "root\SecurityCenter2" -Query $wmiQuery #psboundparameters # -ErrorVariable myError -ErrorAction 'SilentlyContinue' # did not work
$AntiVirusProducts = Get-WmiObject -Namespace "root\SecurityCenter2" -Class AntiVirusProduct -ComputerName $computername
$ret = #()
foreach($AntiVirusProduct in $AntiVirusProducts){
#Switch to determine the status of antivirus definitions and real-time protection.
#The values in this switch-statement are retrieved from the following website: http://community.kaseya.com/resources/m/knowexch/1020.aspx
switch ($AntiVirusProduct.productState) {
"262144" {$defstatus = "Up to date" ;$rtstatus = "Disabled"}
"262160" {$defstatus = "Out of date" ;$rtstatus = "Disabled"}
"266240" {$defstatus = "Up to date" ;$rtstatus = "Enabled"}
"266256" {$defstatus = "Out of date" ;$rtstatus = "Enabled"}
"393216" {$defstatus = "Up to date" ;$rtstatus = "Disabled"}
"393232" {$defstatus = "Out of date" ;$rtstatus = "Disabled"}
"393488" {$defstatus = "Out of date" ;$rtstatus = "Disabled"}
"397312" {$defstatus = "Up to date" ;$rtstatus = "Enabled"}
"397328" {$defstatus = "Out of date" ;$rtstatus = "Enabled"}
"397584" {$defstatus = "Out of date" ;$rtstatus = "Enabled"}
default {$defstatus = "Unknown" ;$rtstatus = "Unknown"}
}
#Create hash-table for each computer
$ht = #{}
$ht.Computername = $computername
$ht.Name = $AntiVirusProduct.displayName
$ht.'Product GUID' = $AntiVirusProduct.instanceGuid
$ht.'Product Executable' = $AntiVirusProduct.pathToSignedProductExe
$ht.'Reporting Exe' = $AntiVirusProduct.pathToSignedReportingExe
$ht.'Definition Status' = $defstatus
$ht.'Real-time Protection Status' = $rtstatus
#Create a new object for each computer
$ret += New-Object -TypeName PSObject -Property $ht
}
Return $ret
}
Get-AntiVirusProduct
Output:
Product GUID : {B0D0C4F4-7F0B-0434-B825-1213C45DAE01}
Name : CylancePROTECT
Real-time Protection Status : Enabled
Computername : HOSTNAME
Product Executable : C:\Program Files\Cylance\Desktop\CylanceSvc.exe
Reporting Exe : C:\Program Files\Cylance\Desktop\CylanceSvc.exe
Definition Status : Up to date
Product GUID : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
Name : Windows Defender
Real-time Protection Status : Unknown
Computername : HOSTNAME
Product Executable : windowsdefender://
Reporting Exe : %ProgramFiles%\Windows Defender\MsMpeng.exe
Definition Status : Unknown
Instead of relying on running processes, you could query the registry :
$computerList = "localhost", "localhost"
$filter = "antivirus"
$results = #()
foreach($computerName in $computerList) {
$hive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $computerName)
$regPathList = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
foreach($regPath in $regPathList) {
if($key = $hive.OpenSubKey($regPath)) {
if($subkeyNames = $key.GetSubKeyNames()) {
foreach($subkeyName in $subkeyNames) {
$productKey = $key.OpenSubKey($subkeyName)
$productName = $productKey.GetValue("DisplayName")
$productVersion = $productKey.GetValue("DisplayVersion")
$productComments = $productKey.GetValue("Comments")
if(($productName -match $filter) -or ($productComments -match $filter)) {
$resultObj = [PSCustomObject]#{
Host = $computerName
Product = $productName
Version = $productVersion
Comments = $productComments
}
$results += $resultObj
}
}
}
}
$key.Close()
}
}
$results | ft -au
Example output :
Host Product Version Comments
---- ------- ------- --------
localhost Avast Free Antivirus 10.4.2233
localhost Avast Free Antivirus 10.4.2233
Would this work for you? It's written in PowerShell v2, so if you have that installed on your 2003 servers, it will run on all the servers. This code will give you a CSV of this data from whichever machines run the script that have a service with the description including the word "virus" (which I thought better than "antivirus" because some services use "anti-virus" instead). If they all have access to a shared resource, you can prepend that shared resource directory to the $Filename variable and it will name each report starting with that computer's name and dump your reports there.
invoke-command -computername Server01, Server02 -filepath c:\Scripts\get_av_info.ps1
Assuming the script is saved as c:\Scripts\get_av_info.ps1, that should run it on whatever machines you specify, or if you have a CSV of all the machines you want to run the script, ForEach it. I didn't try this, so I can't verify the remote invoking.
$Date = (Get-Date).ToString('yyyy-MM-dd')
$localhost = $env:computername
$Filename = "C:\" + $localhost + "_" + $Date + "_AV_FileInfo.csv"
$AV = get-process | ?{$_.Description -like "*virus*"}
$Process = ForEach($a in $AV){
$ID = $($a.Id)
get-process -Id $ID -FileVersionInfo
}
$Process | select "CompanyName","FileBuildPart","FileDescription","FileName","FileVersion","ProductName","ProductPrivatePart","ProductVersion","SpecialBuild" | Export-Csv $Filename -NoTypeInformation
There are a LOT of options, I just picked ones I thought you'd want. You could probably also combine the reports to one by adding a shared resource to the Filename and having it -Append, but you would run the risk of multiple servers trying to write to the file at the same time and failing to report at all.
You'll need to refine your results, of course. If you don't change anything, any machine where you run this will just drop a CSV called "COMPUTERNAME_DATE_AV_FileInfo.csv" at the root of it's C:\ drive.
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