Remote Desktop Protocol (RDP) is a core feature for managing systems remotely—whether you’re in IT support, systems administration, or operating a managed services provider (MSP). Ensuring RDP is properly configured and monitoring the port it listens on are vital tasks for maintaining secure and functional endpoints. This post examines a PowerShell script that checks the status and port for Remote Desktop and optionally logs the result to a NinjaOne custom field. It’s a practical solution for automated reporting and auditing in modern IT environments.
Background
Remote Desktop is enabled by default in many enterprise settings, but its configuration can drift over time. Factors like group policy changes, security updates, or user modifications can disable RDP or alter its listening port—leaving systems inaccessible or vulnerable. In larger environments, especially those managed via RMM platforms like NinjaOne, checking each machine manually is infeasible. Automating this check through PowerShell scripting not only saves time but ensures accuracy and compliance.
This script is designed with NinjaOne integration in mind, making it especially valuable for MSPs and IT departments using that platform. It doesn’t just gather RDP status—it also supports automatic logging to a custom field, facilitating seamless asset documentation.
The Script
#Requires -Version 5.1 <# .SYNOPSIS Reports the status of Remote Desktop and the port it is listening on. .DESCRIPTION Reports the status of Remote Desktop and the port it is listening on. With the option to save the results to a custom field. By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). .EXAMPLE (No Parameters) ## EXAMPLE OUTPUT WITHOUT PARAMS ## [Info] Enabled | Port: 3389 PARAMETER: -RdpStatusCustomFieldName "RDPStatus" Name of a custom field to save the results to. .EXAMPLE -RdpStatusCustomFieldName "RDPStatus" ## EXAMPLE OUTPUT WITH RdpStatusCustomFieldName ## [Info] Enabled | Port: 3389 [Info] Attempting to set Custom Field 'RDPStatus'. [Info] Successfully set Custom Field 'RDPStatus'! .NOTES Minimum OS Architecture Supported: Windows 10, Windows Server 2016 Release Notes: Initial Release #> [CmdletBinding()] param ( [Parameter()] [String]$RdpStatusCustomFieldName ) begin { function Set-NinjaProperty { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String]$Name, [Parameter()] [String]$Type, [Parameter(Mandatory = $True, ValueFromPipeline = $True)] $Value, [Parameter()] [String]$DocumentName ) # Measure the number of characters in the provided value $Characters = $Value | ConvertTo-Json | Measure-Object -Character | Select-Object -ExpandProperty Characters # Throw an error if the value exceeds the character limit of 200,000 characters if ($Characters -ge 200000) { throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded: the value is greater than or equal to 200,000 characters.") } # Initialize a hashtable for additional documentation parameters $DocumentationParams = @{} # If a document name is provided, add it to the documentation parameters if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName } # Define a list of valid field types $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG" # Warn the user if the provided type is not valid if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type. Please check here for valid types: https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" } # Define types that require options to be retrieved $NeedsOptions = "Dropdown" # If the property is being set in a document or field and the type needs options, retrieve them if ($DocumentName) { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1 } } else { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1 } } # Throw an error if there was an issue retrieving the property options if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions } # Process the property value based on its type switch ($Type) { "Checkbox" { # Convert the value to a boolean for Checkbox type $NinjaValue = [System.Convert]::ToBoolean($Value) } "Date or Date Time" { # Convert the value to a Unix timestamp for Date or Date Time type $Date = (Get-Date $Value).ToUniversalTime() $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date $NinjaValue = $TimeSpan.TotalSeconds } "Dropdown" { # Convert the dropdown value to its corresponding GUID $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name" $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID # Throw an error if the value is not present in the dropdown options if (!($Selection)) { throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown options.") } $NinjaValue = $Selection } default { # For other types, use the value as is $NinjaValue = $Value } } # Set the property value in the document if a document name is provided if ($DocumentName) { $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1 } else { # Otherwise, set the standard property value $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2>&1 } # Throw an error if setting the property failed if ($CustomField.Exception) { throw $CustomField } } } process { if ($env:rdpStatusCustomFieldName -and $env:rdpStatusCustomFieldName -notlike "null") { $RdpStatusCustomFieldName = $env:rdpStatusCustomFieldName } # Terminal Server registry path $RdpPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' # Deny RDP Connections $DenyRdpConnections = Get-ItemProperty -Path $RdpPath -Name 'fDenyTSConnections' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty fDenyTSConnections -ErrorAction SilentlyContinue # RDP Port $RdpPort = Get-ItemProperty -Path "$RdpPath\WinStations\RDP-Tcp" -Name PortNumber -ErrorAction SilentlyContinue | Select-Object -ExpandProperty PortNumber -ErrorAction SilentlyContinue # 1 or $null = Disabled (Default) # 0 = Enabled $RdpEnabled = if ($DenyRdpConnections -eq 0) { "Enabled" }else { "Disabled" } # 3389 or $null = 3389 (Default) $RdpPort = if ($null -eq $RdpPort) { "3389" }else { "$RdpPort" } $Report = "$RdpEnabled | Port: $RdpPort" Write-Host "[Info] $Report" if ($RdpStatusCustomFieldName) { try { Write-Host "[Info] Attempting to set Custom Field '$RdpStatusCustomFieldName'." Set-NinjaProperty -Name $RdpStatusCustomFieldName -Value $Report Write-Host "[Info] Successfully set Custom Field '$RdpStatusCustomFieldName'!" } catch { Write-Host "[Error] $($_.Exception.Message)" exit 1 } } } end { }
Detailed Breakdown
At a high level, the script performs three tasks:
- Checks if RDP is enabled
- Determines which port RDP is using
- Optionally logs the results to a NinjaOne custom field
Step-by-step Flow
- Parameter Handling
The script accepts an optional parameter:
powershell
CopyEdit
[String]$RdpStatusCustomFieldName
This lets the user specify a NinjaOne custom field where results will be saved.
- Environment Variable Support
If the parameter is not passed, the script checks the environment variable $env:rdpStatusCustomFieldName—ensuring compatibility with injected runtime variables.
- Registry Lookups
Two critical registry keys are queried:
- fDenyTSConnections under HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server to determine RDP status.
- PortNumber under WinStations\RDP-Tcp to find the configured RDP port.
- Output Formatting
Based on the registry values, the script outputs a string like:
yaml
CopyEdit
[Info] Enabled | Port: 3389
- Custom Field Integration
If a custom field name is provided, the script calls the Set-NinjaProperty function, which:
- Converts and validates input based on field type.
- Handles dropdowns, checkboxes, dates, etc.
- Uses Ninja-Property-Set-Piped or Ninja-Property-Docs-Set to apply changes.
This portion is wrapped in a try/catch block to handle errors gracefully.
powershell
CopyEdit
Set-NinjaProperty -Name $RdpStatusCustomFieldName -Value $Report
Potential Use Cases
Scenario: MSP Conducting an RDP Audit
An MSP manages 500 Windows devices. The security team wants to ensure RDP is disabled on all machines not explicitly needing it. Instead of logging into each device, the MSP uses NinjaOne to deploy this script across the fleet. The script checks RDP status and port, then writes the output to a custom field like RDPStatus. A report is generated directly from NinjaOne, showing which machines have RDP enabled and what ports are exposed.
Comparisons
Manual Method
- Navigate to System Properties > Remote or check via GUI.
Or use individual PowerShell commands:
powershell
CopyEdit
Get-ItemProperty -Path ‘HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server’ -Name ‘fDenyTSConnections’
Limitations
- No automation
- No centralized logging
- Time-consuming for large networks
Third-party Tools
Some security tools can audit RDP settings but often lack native integration with RMM platforms. This script fills that gap by directly interfacing with NinjaOne, offering a streamlined and centralized approach.
Frequently Asked Questions
Q: Will the script work if RDP has never been configured?
Yes. The script defaults to Disabled | Port: 3389 if registry keys are missing or null.
Q: What happens if I enter an invalid custom field name?
The script will catch the error and display:
[Error] Field name not found or unsupported type.
Q: Can I run this outside of NinjaOne?
Absolutely. The script outputs results to the console regardless of NinjaOne integration.
Implications
Misconfigured RDP settings are a common vector for lateral movement and unauthorized access. This script provides early detection, allowing IT teams to address vulnerabilities proactively. Logging results into NinjaOne ensures permanent visibility, helpful for compliance and audits.
If RDP is unintentionally enabled, especially with non-default ports, it may expose machines to brute-force attacks. Running this script regularly can prevent such exposures.
Recommendations
- Automate Deployment: Schedule this script via NinjaOne automation policies to run weekly.
- Alerting: Combine with conditional alerts based on custom field values (e.g., RDP Enabled).
- Baseline Port: Validate that only port 3389 is used unless a custom configuration is in place.
- Documentation: Maintain a log of historical RDP status changes via versioned custom field reporting.
Final Thoughts
This PowerShell script exemplifies the power of automation in IT operations. By checking the status and port for Remote Desktop with PowerShell and optionally integrating with NinjaOne, it streamlines auditing and enhances endpoint security.
For MSPs and IT professionals managing numerous devices, such tooling is not just convenient—it’s essential. With NinjaOne’s custom field support, insights are centralized, searchable, and actionable, enabling faster responses and better visibility across all managed assets.