Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

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:
Before starting this tutorial, ensure you have:
# Verify required PowerShell modules
Get-Module -ListAvailable ActiveDirectory
Get-Module -ListAvailable GroupPolicy
Remote Desktop settings in a domain environment are controlled through:
HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\fDenyTSConnections
HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services
# Install AD DS role (if not already installed)
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
# Verify domain controller status
Get-ADDomainController
# 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"
# 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"
# 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
Computer Configuration > Policies > Administrative Templates > Windows Components > Remote Desktop Services > Remote Desktop Session Host > ConnectionsScript 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
}
<#
.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
}
dsa.msc
(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=4096))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.
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
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)"
}
}
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
}
}
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.