How to Generate Remote Desktop Reports from Domain Controllers

23

Introduction

Remote Desktop Protocol (RDP) management is crucial for Windows system administrators managing enterprise networks. This comprehensive guide provides step-by-step instructions for generating detailed reports of computers with Remote Desktop enabled and tracking remote users across domain controllers.

What you’ll learn:

  • How to identify RDP-enabled computers in your domain
  • Generate comprehensive remote user access reports
  • Implement automated monitoring solutions
  • Apply security best practices for RDP management

Prerequisites

Before starting this tutorial, ensure you have:

  • Windows Server 2016/2019/2022 with Active Directory Domain Services
  • Domain Administrator privileges or equivalent permissions
  • PowerShell 5.1 or later installed
  • Remote Server Administration Tools (RSAT) installed on client machines
  • Basic understanding of Active Directory and Group Policy

Required Modules and Tools

# Verify required PowerShell modules
Get-Module -ListAvailable ActiveDirectory
Get-Module -ListAvailable GroupPolicy

Understanding Remote Desktop in Active Directory

RDP Configuration Locations

Remote Desktop settings in a domain environment are controlled through:

  1. Local Computer Policies: Registry settings on individual machines
  2. Group Policy Objects (GPOs): Centralized management through AD
  3. Windows Firewall Rules: Network accessibility controls
  4. User Rights Assignments: Permissions for remote logon

Key Registry Locations

HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\fDenyTSConnections
HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services

Lab Setup Guide

Step 1: Prepare Your Test Environment

# Install AD DS role (if not already installed)
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools

# Verify domain controller status
Get-ADDomainController
Create Test Organizational Units
# Create OUs for testing
New-ADOrganizationalUnit -Name "RDP-Enabled-Computers" -Path "DC=yourdomain,DC=com"
New-ADOrganizationalUnit -Name "RDP-Test-Users" -Path "DC=yourdomain,DC=com"
Add Test Computer Accounts
# Create test computer accounts
New-ADComputer -Name "TestPC01" -Path "OU=RDP-Enabled-Computers,DC=yourdomain,DC=com"
New-ADComputer -Name "TestPC02" -Path "OU=RDP-Enabled-Computers,DC=yourdomain,DC=com"

Step 2: Configure Remote Desktop via Group Policy

Create RDP Configuration GPO
# Create new GPO
New-GPO -Name "Enable-Remote-Desktop-Policy" -Domain "yourdomain.com"

# Link GPO to target OU
New-GPLink -Name "Enable-Remote-Desktop-Policy" -Target "OU=RDP-Enabled-Computers,DC=yourdomain,DC=com"

Configure RDP Settings in GPO

  • Navigate to: Computer Configuration > Policies > Administrative Templates > Windows Components > Remote Desktop Services > Remote Desktop Session Host > Connections
  • Enable: “Allow users to connect remotely by using Remote Desktop Services”

Method 1: PowerShell Scripts for RDP Reports

Script 1: Identify RDP-Enabled Computers

<#
.SYNOPSIS
Generate report of computers with Remote Desktop enabled

.DESCRIPTION
This script queries domain computers and checks RDP configuration status

.EXAMPLE
.\Get-RDPEnabledComputers.ps1 -OutputPath "C:\Reports\RDP-Report.csv"
#>

param(
[Parameter(Mandatory=$false)]
[string]$OutputPath = "C:\Temp\RDP-Enabled-Computers.csv",

[Parameter(Mandatory=$false)]
[string]$SearchBase = $null
)

# Import required modules
Import-Module ActiveDirectory

# Get all computer accounts from AD
if ($SearchBase) {
$Computers = Get-ADComputer -Filter * -SearchBase $SearchBase -Properties Name, DistinguishedName, OperatingSystem, LastLogonDate
} else {
$Computers = Get-ADComputer -Filter * -Properties Name, DistinguishedName, OperatingSystem, LastLogonDate
}

# Initialize results array
$Results = @()

Write-Host "Checking $($Computers.Count) computers for RDP status..." -ForegroundColor Green

foreach ($Computer in $Computers) {
Write-Progress -Activity "Scanning Computers" -Status "Checking $($Computer.Name)" -PercentComplete (($Results.Count / $Computers.Count) * 100)

$ComputerInfo = [PSCustomObject]@{
ComputerName = $Computer.Name
DistinguishedName = $Computer.DistinguishedName
OperatingSystem = $Computer.OperatingSystem
LastLogonDate = $Computer.LastLogonDate
RDPEnabled = "Unknown"
RDPPort = "Unknown"
FirewallStatus = "Unknown"
LastChecked = Get-Date
OnlineStatus = "Offline"
}

# Test computer connectivity
if (Test-Connection -ComputerName $Computer.Name -Count 1 -Quiet) {
$ComputerInfo.OnlineStatus = "Online"

try {
# Check RDP registry setting
$RDPSetting = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
try {
$fDenyTSConnections = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -ErrorAction Stop
return $fDenyTSConnections.fDenyTSConnections
} catch {
return 999 # Error code
}
} -ErrorAction Stop

if ($RDPSetting -eq 0) {
$ComputerInfo.RDPEnabled = "Enabled"
} elseif ($RDPSetting -eq 1) {
$ComputerInfo.RDPEnabled = "Disabled"
} else {
$ComputerInfo.RDPEnabled = "Error"
}

# Get RDP port
$RDPPort = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
try {
$PortNumber = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "PortNumber" -ErrorAction Stop
return $PortNumber.PortNumber
} catch {
return "Error"
}
} -ErrorAction Stop

$ComputerInfo.RDPPort = $RDPPort

# Check Windows Firewall RDP rule
$FirewallStatus = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
try {
$RDPRule = Get-NetFirewallRule -DisplayName "*Remote Desktop*" -ErrorAction Stop | Where-Object {$_.Enabled -eq $true}
if ($RDPRule) {
return "Allowed"
} else {
return "Blocked"
}
} catch {
return "Error"
}
} -ErrorAction Stop

$ComputerInfo.FirewallStatus = $FirewallStatus

} catch {
$ComputerInfo.RDPEnabled = "Access Denied"
Write-Warning "Could not access $($Computer.Name): $($_.Exception.Message)"
}
}

$Results += $ComputerInfo
}

Write-Progress -Activity "Scanning Computers" -Completed

# Export results to CSV
$Results | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Host "Report saved to: $OutputPath" -ForegroundColor Green

# Display summary
$EnabledCount = ($Results | Where-Object {$_.RDPEnabled -eq "Enabled"}).Count
$DisabledCount = ($Results | Where-Object {$_.RDPEnabled -eq "Disabled"}).Count
$OfflineCount = ($Results | Where-Object {$_.OnlineStatus -eq "Offline"}).Count

Write-Host "`n=== RDP STATUS SUMMARY ===" -ForegroundColor Yellow
Write-Host "Total Computers Scanned: $($Computers.Count)" -ForegroundColor Cyan
Write-Host "RDP Enabled: $EnabledCount" -ForegroundColor Green
Write-Host "RDP Disabled: $DisabledCount" -ForegroundColor Red
Write-Host "Computers Offline: $OfflineCount" -ForegroundColor Gray

# Show RDP-enabled computers
$RDPEnabled = $Results | Where-Object {$_.RDPEnabled -eq "Enabled"}
if ($RDPEnabled) {
Write-Host "`n=== RDP-ENABLED COMPUTERS ===" -ForegroundColor Green
$RDPEnabled | Format-Table ComputerName, OperatingSystem, RDPPort, FirewallStatus -AutoSize
}

Script 2: Track Remote Desktop Users

<#
.SYNOPSIS
Generate report of remote desktop user sessions

.DESCRIPTION
This script queries domain controllers and member servers for active and historical RDP sessions

.EXAMPLE
.\Get-RemoteDesktopUsers.ps1 -Days 7 -OutputPath "C:\Reports\RDP-Users.csv"
#>

param(
[Parameter(Mandatory=$false)]
[int]$Days = 1,

[Parameter(Mandatory=$false)]
[string]$OutputPath = "C:\Temp\RDP-User-Sessions.csv",

[Parameter(Mandatory=$false)]
[string[]]$ComputerNames = @()
)

# Import required modules
Import-Module ActiveDirectory

# Get target computers
if ($ComputerNames.Count -eq 0) {
$Computers = Get-ADComputer -Filter {OperatingSystem -like "*Windows*"} -Properties Name, OperatingSystem
} else {
$Computers = $ComputerNames | ForEach-Object { Get-ADComputer -Identity $_ -Properties Name, OperatingSystem }
}

# Initialize results array
$AllSessions = @()

# Calculate date range
$StartDate = (Get-Date).AddDays(-$Days)

Write-Host "Collecting RDP session data from $($Computers.Count) computers for the last $Days days..." -ForegroundColor Green

foreach ($Computer in $Computers) {
Write-Progress -Activity "Collecting Session Data" -Status "Checking $($Computer.Name)" -PercentComplete (($AllSessions.Count / $Computers.Count) * 100)

if (Test-Connection -ComputerName $Computer.Name -Count 1 -Quiet) {
try {
# Get current active sessions
$ActiveSessions = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
try {
quser 2>$null | ForEach-Object {
if ($_ -match '\s*(\w+)\s+(\w*)\s+(\d+)\s+(\w+)\s+(.+)\s+(\d{2}\/\d{2}\/\d{4}\s+\d{1,2}:\d{2}\s*\w*)') {
[PSCustomObject]@{
Username = $matches[1]
SessionName = $matches[2]
ID = $matches[3]
State = $matches[4]
IdleTime = $matches[5]
LogonTime = $matches[6]
SessionType = "Active"
}
}
}
} catch {
@()
}
} -ErrorAction SilentlyContinue

# Get RDP logon events from Event Log
$LogonEvents = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
param($StartDate)

try {
# Event IDs: 4624 (Logon), 4647 (Logoff), 4634 (Logoff)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624, 4647, 4634
StartTime = $StartDate
} -ErrorAction Stop | Where-Object {
$_.Message -like "*Network Level*" -or
$_.Message -like "*RemoteInteractive*" -or
$_.Message -like "*Terminal Services*"
} | Select-Object TimeCreated, Id, @{Name="Username";Expression={
if ($_.Message -match 'Account Name:\s*([^\s]+)') { $matches[1] } else { "Unknown" }
}}, @{Name="SourceIP";Expression={
if ($_.Message -match 'Source Network Address:\s*([^\s]+)') { $matches[1] } else { "Unknown" }
}}
} catch {
@()
}
} -ArgumentList $StartDate -ErrorAction SilentlyContinue

# Process active sessions
foreach ($Session in $ActiveSessions) {
$SessionInfo = [PSCustomObject]@{
ComputerName = $Computer.Name
OperatingSystem = $Computer.OperatingSystem
Username = $Session.Username
SessionType = "Active RDP"
SessionState = $Session.State
LogonTime = $Session.LogonTime
LogoffTime = "Still Active"
SourceIP = "Unknown"
IdleTime = $Session.IdleTime
SessionID = $Session.ID
EventTime = Get-Date
}
$AllSessions += $SessionInfo
}

# Process historical events
foreach ($Event in $LogonEvents) {
$SessionInfo = [PSCustomObject]@{
ComputerName = $Computer.Name
OperatingSystem = $Computer.OperatingSystem
Username = $Event.Username
SessionType = switch ($Event.Id) {
4624 { "Remote Logon" }
4647 { "Remote Logoff" }
4634 { "Remote Logoff" }
default { "Unknown" }
}
SessionState = switch ($Event.Id) {
4624 { "Logged On" }
default { "Logged Off" }
}
LogonTime = if ($Event.Id -eq 4624) { $Event.TimeCreated } else { "Unknown" }
LogoffTime = if ($Event.Id -in 4647, 4634) { $Event.TimeCreated } else { "Unknown" }
SourceIP = $Event.SourceIP
IdleTime = "N/A"
SessionID = "N/A"
EventTime = $Event.TimeCreated
}
$AllSessions += $SessionInfo
}

} catch {
Write-Warning "Error accessing $($Computer.Name): $($_.Exception.Message)"
}
} else {
Write-Warning "$($Computer.Name) is offline"
}
}

Write-Progress -Activity "Collecting Session Data" -Completed

# Remove duplicate entries and sort by time
$UniqueSessions = $AllSessions | Sort-Object ComputerName, Username, EventTime | Get-Unique -AsString

# Export to CSV
$UniqueSessions | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Host "Report saved to: $OutputPath" -ForegroundColor Green

# Display summary
Write-Host "`n=== REMOTE DESKTOP SESSION SUMMARY ===" -ForegroundColor Yellow
Write-Host "Total Sessions Found: $($UniqueSessions.Count)" -ForegroundColor Cyan

$ActiveSessions = $UniqueSessions | Where-Object {$_.SessionType -eq "Active RDP"}
$HistoricalLogons = $UniqueSessions | Where-Object {$_.SessionType -eq "Remote Logon"}

Write-Host "Currently Active Sessions: $($ActiveSessions.Count)" -ForegroundColor Green
Write-Host "Historical Logons (Last $Days days): $($HistoricalLogons.Count)" -ForegroundColor Blue

# Show unique users
$UniqueUsers = $UniqueSessions | Select-Object -ExpandProperty Username -Unique | Where-Object {$_ -ne "Unknown" -and $_ -ne ""}
Write-Host "Unique Remote Users: $($UniqueUsers.Count)" -ForegroundColor Magenta
Write-Host "Users: $($UniqueUsers -join ', ')" -ForegroundColor White

# Show active sessions if any
if ($ActiveSessions.Count -gt 0) {
Write-Host "`n=== CURRENTLY ACTIVE RDP SESSIONS ===" -ForegroundColor Green
$ActiveSessions | Format-Table ComputerName, Username, SessionState, LogonTime, IdleTime -AutoSize
}

Method 2: Active Directory Users and Computers

Using ADUC for Manual Reports

  1. Open Active Directory Users and Computers
dsa.msc
  1. Create Custom Queries
    • Right-click on domain → New → Query
    • Name: “RDP Enabled Computers”
    • Define Query: (&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=4096))
  2. Export Results
    • Select all results → Export List
    • Choose CSV format for further analysis

Method 3: Group Policy Management Console

Using GPMC for RDP Policy Reports

This PowerShell script is doing two main tasks, both related to finding and documenting Group Policies that affect Remote Desktop (RDP) in an Active Directory environment.

# Generate Group Policy Results Report
Get-GPOReport -Name "Enable-Remote-Desktop-Policy" -ReportType HTML -Path "C:\Reports\RDP-GPO-Report.html"

# Get all GPOs affecting RDP settings
$RDPGPOs = Get-GPO -All | Where-Object {
$_.DisplayName -like "*Remote Desktop*" -or
$_.DisplayName -like "*RDP*" -or
$_.DisplayName -like "*Terminal Services*"
}

$RDPGPOs | Format-Table DisplayName, CreationTime, ModificationTime, GpoStatus

This script creates a detailed report for a specific RDP GPO and lists all other GPOs in your domain that might influence Remote Desktop settings, showing when they were created/modified and if they’re active.

Advanced Reporting Techniques

Creating HTML Dashboard Reports

function New-RDPDashboard {
param(
[string]$OutputPath = "C:\Reports\RDP-Dashboard.html"
)

# Get data from previous scripts
$RDPComputers = Import-Csv "C:\Temp\RDP-Enabled-Computers.csv"
$RDPSessions = Import-Csv "C:\Temp\RDP-User-Sessions.csv"

# Generate HTML content
$HTML = @"
<!DOCTYPE html>
<html>
<head>
<title>Remote Desktop Management Dashboard</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #2E86AB; color: white; padding: 20px; text-align: center; }
.summary { display: flex; justify-content: space-around; margin: 20px 0; }
.card { background-color: #f4f4f4; padding: 20px; border-radius: 8px; text-align: center; min-width: 150px; }
.enabled { background-color: #A8DADC; }
.disabled { background-color: #F1FAEE; }
.offline { background-color: #E63946; color: white; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #2E86AB; color: white; }
.status-enabled { background-color: #90EE90; }
.status-disabled { background-color: #FFB6C1; }
</style>
</head>
<body>
<div class="header">
<h1>Remote Desktop Management Dashboard</h1>
<p>Generated on $(Get-Date -Format "MMMM dd, yyyy 'at' HH:mm")</p>
</div>

<div class="summary">
<div class="card enabled">
<h3>$($RDPComputers | Where-Object {$_.RDPEnabled -eq "Enabled"} | Measure-Object | Select-Object -ExpandProperty Count)</h3>
<p>RDP Enabled</p>
</div>
<div class="card disabled">
<h3>$($RDPComputers | Where-Object {$_.RDPEnabled -eq "Disabled"} | Measure-Object | Select-Object -ExpandProperty Count)</h3>
<p>RDP Disabled</p>
</div>
<div class="card offline">
<h3>$($RDPComputers | Where-Object {$_.OnlineStatus -eq "Offline"} | Measure-Object | Select-Object -ExpandProperty Count)</h3>
<p>Computers Offline</p>
</div>
</div>

<h2>RDP-Enabled Computers</h2>
<table>
<tr>
<th>Computer Name</th>
<th>Operating System</th>
<th>RDP Status</th>
<th>RDP Port</th>
<th>Firewall Status</th>
<th>Last Logon</th>
</tr>
"@

foreach ($Computer in ($RDPComputers | Where-Object {$_.RDPEnabled -eq "Enabled"})) {
$StatusClass = if ($Computer.RDPEnabled -eq "Enabled") { "status-enabled" } else { "status-disabled" }
$HTML += @"
<tr class="$StatusClass">
<td>$($Computer.ComputerName)</td>
<td>$($Computer.OperatingSystem)</td>
<td>$($Computer.RDPEnabled)</td>
<td>$($Computer.RDPPort)</td>
<td>$($Computer.FirewallStatus)</td>
<td>$($Computer.LastLogonDate)</td>
</tr>
"@
}

$HTML += @"
</table>

<h2>Recent Remote Desktop Sessions</h2>
<table>
<tr>
<th>Computer Name</th>
<th>Username</th>
<th>Session Type</th>
<th>Logon Time</th>
<th>Source IP</th>
</tr>
"@

$RecentSessions = $RDPSessions | Sort-Object EventTime -Descending | Select-Object -First 20
foreach ($Session in $RecentSessions) {
$HTML += @"
<tr>
<td>$($Session.ComputerName)</td>
<td>$($Session.Username)</td>
<td>$($Session.SessionType)</td>
<td>$($Session.LogonTime)</td>
<td>$($Session.SourceIP)</td>
</tr>
"@
}

$HTML += @"
</table>

<footer style="margin-top: 40px; text-align: center; color: #666;">
<p>Report generated by Remote Desktop Management System</p>
</footer>
</body>
</html>
"@

$HTML | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "Dashboard saved to: $OutputPath" -ForegroundColor Green
}

# Generate the dashboard
New-RDPDashboard

Email Automation

function Send-RDPReport {
param(
[string]$SMTPServer = "mail.company.com",
[string]$From = "rdp-monitor@company.com",
[string[]]$To = @("admin@company.com"),
[string]$Subject = "Daily RDP Status Report - $(Get-Date -Format 'yyyy-MM-dd')"
)

# Attach reports
$Attachments = @(
"C:\Temp\RDP-Enabled-Computers.csv",
"C:\Temp\RDP-User-Sessions.csv",
"C:\Reports\RDP-Dashboard.html"
)

$Body = @"
Daily Remote Desktop Status Report

This automated report contains:
- List of all RDP-enabled computers in the domain
- Recent RDP user session activity
- Interactive dashboard for detailed analysis

Please review the attached files for complete details.

Generated on: $(Get-Date)
Generated by: RDP Monitoring System
"@

try {
Send-MailMessage -SmtpServer $SMTPServer -From $From -To $To -Subject $Subject -Body $Body -Attachments $Attachments
Write-Host "Report emailed successfully" -ForegroundColor Green
} catch {
Write-Error "Failed to send email: $($_.Exception.Message)"
}
}

Monitoring and Alerting

function Test-RDPSecurity {
param([string]$ComputerName)

$SecurityChecks = @{
"NLA_Enabled" = $false
"Default_Port_Changed" = $false
"Strong_Encryption" = $false
"Idle_Timeout_Set" = $false
}

try {
$RemoteRegistry = Invoke-Command -ComputerName $ComputerName -ScriptBlock {
$Checks = @{}

# Check NLA
$NLA = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication"
$Checks["NLA_Enabled"] = ($NLA.UserAuthentication -eq 1)

# Check Port
$Port = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "PortNumber"
$Checks["Default_Port_Changed"] = ($Port.PortNumber -ne 3389)

# Check Encryption
$Encryption = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "MinEncryptionLevel"
$Checks["Strong_Encryption"] = ($Encryption.MinEncryptionLevel -ge 3)

return $Checks
}

return $RemoteRegistry
} catch {
Write-Warning "Cannot check security settings for $ComputerName"
return $SecurityChecks
}
}

Conclusion

This comprehensive guide provides enterprise-level solutions for monitoring and reporting Remote Desktop configurations across Windows domain environments. By implementing these PowerShell scripts, Group Policy configurations, and automated reporting systems, system administrators can maintain complete visibility into RDP usage while ensuring security compliance.

80%
Awesome
  • Design
Leave A Reply

Your email address will not be published.