Get disconnected sessions on all Domain Controllers

disconnected sessions

Get disconnected sessions on all Domain Controllers using PowerShell script. In this short article, I wanted to show you how to use Query User command to get user sessions and export final results to CSV file.

Domain Controllers

To get list of all Domain Controllers you need to use Get-ADDomainController command from ActiveDirectory module. Command gets one or more Active Directory domain controllers based on discoverable services criteria, search parameters or by providing a domain controller identifier, such as the NetBIOS name.

Disconnected sessions

To get user sessions locally we will use Query User command:

Next against all of those servers, we will use the same Query User command. To run it on remote machine you can use the following commands:

Query User /server:DC01

#or

Invoke-Command DC01 {Query User}

Now the difficult part is to convert IDLE time to a better format. As you can see above for user pawel.janowicz there is 4+11:16 value under the IDLE time column. That means 4 days, 11 hours and 16 minutes. It is not easy to sort results based on such value.

I used the switch option to create New-TimeSpan based on those number:

 # Use switch to calculate IDLE time
 $Time = Switch ($Query.'idle time'){
            {$_ -match '\.'}    {New-TimeSpan}
            {$_ -match 'none'}  {New-TimeSpan}
            {$_ -match "^\d+$"} {New-TimeSpan -Minutes $_} 
            {$_ -match '\+'}    {New-TimeSpan -Days $(($_ -SPLIT '\+')[0]) -Hours $(($_ -SPLIT '\+')[0]) -Minutes $(($_ -SPLIT '\:')[1]);Break}
            {$_ -match '\:'}    {New-TimeSpan -Hours $(($_ -SPLIT '\:')[0]) -Minutes $(($_ -SPLIT '\:')[1])}
            default {'Unknown'}
}

Now we can filter final results and for example, find users that IDLE time is greater than 20 days. This might be helpful if we would like to disconnect all of the affected users:

# Sessions with IDLE time grather than 20 days
$Results | Sort-Object IdleTimeSpan -Descending | Where-Object {$_.idletimespan -ge $(New-TimeSpan -Days 20)} | Format-Table -AutoSize    

Final output should look like this:

Below you can find PowerShell script for checking all user sessions on Domain Controllers.

#### MODULE #############################################################################################################################################################################
        
Try{
    Import-Module ActiveDirectory -ErrorAction Stop
}
Catch{
    Write-Warning $_.Exception.Message
    Read-Host "Script will end. Press enter to close the window"
    Exit
}

#### PARAMS #############################################################################################################################################################################
 
$DomainControllers = (Get-ADDomainController -Filter *).name                                                                                                 # Get all Domain Controllers
$RunTime           = (Get-Date).ToUniversalTime()                                                                                                            # Script start time
$FileDate          = Get-Date -Format "yyyyMMddHHmmss"                                                                                                       # Report date
$ReportPath        = "$env:userprofile\desktop\"                                                                                                             # Report location
$OutputCsv         = "$ReportPath\Sessions_$FileDate.csv"                                                                                                   # CSV path
$Results           = @()                                                                                                                                     # Define array
 
 
#### CHECK SESSIONS #####################################################################################################################################################################
 
$Results = Invoke-Command $DomainControllers {
 
                    # Define object
                    $Object = @{} | Select Servername,
                                           Username,
                                           ID,
                                           State,
                                           IdleTime,
                                           IdleTimeSpan,
                                           TimeSpan,
                                           LogonTime
 
                    # Add computername to object property
                    $Object.Servername   = $env:COMPUTERNAME
 
                    # Query all user sessions
                    $Queries = (QUERY USER $($ITEM.USERNAME)) -split "\n" -replace '\s\s+', ';' | ConvertFrom-Csv -Delimiter ';'
 
                    # Proceed if $Queries return values
                    If($Queries){
 
                        # Loop each query
                        Foreach ($Query in $Queries){
 
                                If($Query.ID -match 'DISC'){
 
                                    # Use switch to calculate IDLE time
                                    $Time = Switch ($Query.'state'){
                                        {$_ -match '\.'}    {New-TimeSpan}
                                        {$_ -match 'none'}  {New-TimeSpan}
                                        {$_ -match "^\d+$"} {New-TimeSpan -Minutes $_} 
                                        {$_ -match '\+'}    {New-TimeSpan -Days $(($_ -SPLIT '\+')[0]) -Hours $(($_ -SPLIT '\+')[0]) -Minutes $(($_ -SPLIT '\:')[1]);Break}
                                        {$_ -match '\:'}    {New-TimeSpan -Hours $(($_ -SPLIT '\:')[0]) -Minutes $(($_ -SPLIT '\:')[1])}
                                        
                                        default {'Unknown'}
                                    }
 
                                    # Add values to object properties
                                    $Object.Username     = $Query.username
                                    $Object.ID           = $Query.sessionname
                                    $Object.State        = $Query.id
                                    $Object.IdleTime     = $Query.state
                                    $Object.IdleTimeSpan = $Time
                                    $Object.TimeSpan     = $("$($Time.Days) days, $($Time.Hours)h, $($Time.Minutes)mins")
                                    $Object.LogonTime    = $Query.'idle time'
                                }
                                Else{
 
                                    # Use switch to calculate IDLE time
                                    $Time = Switch ($Query.'idle time'){
                                        {$_ -match '\.'}    {New-TimeSpan}
                                        {$_ -match 'none'}  {New-TimeSpan}
                                        {$_ -match "^\d+$"} {New-TimeSpan -Minutes $_} 
                                        {$_ -match '\+'}    {New-TimeSpan -Days $(($_ -SPLIT '\+')[0]) -Hours $(($_ -SPLIT '\+')[0]) -Minutes $(($_ -SPLIT '\:')[1]);Break}
                                        {$_ -match '\:'}    {New-TimeSpan -Hours $(($_ -SPLIT '\:')[0]) -Minutes $(($_ -SPLIT '\:')[1])}
                                        default {'Unknown'}
                                    }
 
                                    # Add values to object properties
                                    $Object.Username     = $Query.username
                                    $Object.ID           = $Query.id
                                    $Object.State        = $Query.state
                                    $Object.IdleTime     = $Query.'idle time'
                                    $Object.IdleTimeSpan = $Time
                                    $Object.TimeSpan     = $("$($Time.Days) days, $($Time.Hours)h, $($Time.Minutes)mins")
                                    $Object.LogonTime    = $Query.'Logon time'
                                }
 
                                # Return object
                                $Object
 
                        }
                }
 
} -ErrorAction SilentlyContinue | Select-Object Servername,Username,ID,State,IdleTimeSpan,TimeSpan,LogonTime
 
 
#### RESULTS ###########################################################################################################################################################################
 
$Results | Sort-Object IdleTimeSpan -Descending | Export-Csv $OutputCsv -Force -NoTypeInformation                                         # Export to CSV
$Results | Sort-Object IdleTimeSpan -Descending | Format-Table -AutoSize                                                                  # All results
$Results | Sort-Object IdleTimeSpan -Descending | Where-Object {$_.state -ne 'Active'} | Format-Table -AutoSize                           # All disconnected sessions
$Results | Sort-Object IdleTimeSpan -Descending | Where-Object {$_.idletimespan -ge $(New-TimeSpan -Hours 8)} | Format-Table -AutoSize    # Sessions with IDLE time grather than 8 hours
$Results | Sort-Object IdleTimeSpan -Descending | Where-Object {$_.idletimespan -ge $(New-TimeSpan -Days 20)} | Format-Table -AutoSize    # Sessions with IDLE time grather than 20 days
   

      
$EndTime = (Get-Date).ToUniversalTime()
$up      = $EndTime - $RunTime
$uptime  = "$($up.Days) days, $($up.Hours)h, $($up.Minutes)mins, $($up.Seconds)sec , $($up.Milliseconds)milisec"
 
"Script was running for: $Uptime" 


I hope this was informative for you 🙂 See you in the next articles.

2 thoughts on “Get disconnected sessions on all Domain Controllers

  1. Hi Pawel,

    Thanks for the script.

    I am trying to achive user sessions on RDS server with following data
    Server|status| user name| login date time| idle time| disconnect date time

    using with query user /server – i can get above information apart from disconnect time
    I also want to see the users log history.

    Thanks
    Naga

  2. I have around 800 servers in my list but the script is taking much lot of time to get executed, i have ran the script long back now it is more than 5 hours but still it is ruining, can you suggest on that one please

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.