Updated – 28/01/2024 – The script was updated to use the Azure Resource Graph instead of Azure PowerShell to audit large environments. Please feel free to leave a comment below for additional improvement.
Updated – 06/06/2022 – The script was updated to include the source and destination port range. Please feel free to leave a comment below for additional improvement.
Updated – 11/02/2022 – The script was updated to include the source and destination addresses.
In this article, we will share with you a PowerShell script that will help you to get the list of Network Security Groups with RDP port open in all Azure subscriptions, and then export it to a comma-separated value (CSV) format. This comes in handy when working with many VMs in Azure, and you want to audit which Network Security Group (NSG) has an RDP port enabled.
Table of Contents
Introduction
A couple of days ago, we got the info about a new RDP vulnerability, known as RDP BlueKeep which can allow remote access to a virtual machine running RDP without NLA (Network Level Authentication) by sending a specially crafted data packet that RDP does not understand, the attacker can cause memory corruption and remotely execute code with the NT Authority/System access level. You can read about the RDP BlueKeep flaw here.
Changing the listening port will help to “hide” the Remote Desktop from hackers who are constantly scanning the network for computers listening on the default Remote Desktop Port (TCP 3389). This offers effective protection against the latest RDP worms and adds additional security to your environment.
Secure Azure VM Access
Azure Network Security Group (NSG) can help you limit network traffic to resources in a virtual network. NSG allows you to create rules (ACLs) at the desired level of granularity: network interfaces, individual VMs, or virtual subnets. You can control access by permitting or denying communication between the workloads within a virtual network, from systems on your network(s) via cross-premises connectivity, or direct Internet communication. Each network interface has zero, or one, associated network security group. Each network interface exists in a virtual network subnet. A subnet can also have zero, or one, associated network security group.
If you have Microsoft Defender for Cloud (MDC) enabled in your Azure subscription, then MDC can identify the list of ports open for you. The Just In Time (JIT) VM Access blade will show you the machines with RDP/SSH open and recommend enabling JIT.
Additionally, you should consider using Azure Bastion a fully managed service that provides more secure and seamless Remote Desktop Protocol (RDP) and Secure Shell Protocol (SSH) access to virtual machines (VMs) without any exposure through public IP addresses.
Related: Audit all Azure VMs that don’t have Network Security Group (NSG) associated.
Get the List of Network Security Groups
Here is the script that will do the job for you.
<#
.Synopsis
A script used to find all NSGs with RDP Port Open in all your Azure Subscriptions
.DESCRIPTION
A script used to get the list of all Network Security Groups (NSGs) with RDP Port open in all your Azure Subscriptions.
Finally, it will export the report into a csv file in your Azure Cloud Shell storage.
.Notes
Created : 2019-06-11
Updated : 2022-06-06
Version : 2.1
Author : Charbel Nemnom
Twitter : @CharbelNemnom
Blog : https://charbelnemnom.com
Disclaimer: This script is provided "AS IS" with no warranties.
#>
$azSubs = Get-AzSubscription
foreach ( $azSub in $azSubs ) {
Set-AzContext -Subscription $azSub | Out-Null
$azNsgs = Get-AzNetworkSecurityGroup
foreach ( $azNsg in $azNsgs ) {
Get-AzNetworkSecurityRuleConfig -NetworkSecurityGroup $azNsg | Where-Object { $_.DestinationPortRange -eq '3389' } | `
Select-Object @{label = 'NSG Name'; expression = { $azNsg.Name } }, @{label = 'Rule Name'; expression = { $_.Name } }, `
@{label = 'Source'; expression = { $_.SourceAddressPrefix } }, `
@{label = 'Source Port Range'; expression = { $_.SourcePortRange } }, `
@{label = 'Destination'; expression = { $_.DestinationAddressPrefix } }, `
@{label = 'Destination Port Range'; expression = { $_.DestinationPortRange } }, Access, Priority, Direction, `
@{label = 'Resource Group Name'; expression = { $azNsg.ResourceGroupName } } | Export-Csv -Path "$($home)\clouddrive\nsg-audit.csv" -NoTypeInformation -Append
}
}
Jump into the Azure Cloud Shell session (https://shell.azure.com) and run the script above:

Switch to the Cloud Shell storage account and download the CSV file.

And here is the final report is shown in CSV format:

Please note that you can accomplish the same thing using Azure CLI, however, we prefer to use Azure PowerShell.
Azure Cloud Shell is so powerful, that you don’t need to install Azure CLI or PowerShell modules locally on your machine to automate your tasks. I highly recommend checking the master Cloud Shell session recorded by my dear friend Thomas Maurer.
Azure Resource Graph
For small to medium deployment, you could use the Azure PowerShell or CLI as described in this article. However, it is not a good idea to do it on a large scale.
Azure Resource Manager (ARM) will throttle requests at 12k/h. The Microsoft Network resource provider will throttle at 10k per 5min read. By iterating, you will be able to read only a few resources, which will take so long.
For large-scale deployment, it’s recommended to use the Azure Resource Graph (ARG) instead to get the list of Network Security Groups with RDP ports open.
To use Resource Graph, take the following steps:
1) Azure PowerShell installed locally on your machine or using the Cloud Shell.
2) The Azure Resource Graph module for PowerShell. Please note that this module can be used with locally installed PowerShell, with Azure Cloud Shell, or with the PowerShell Docker image.
To install Azure Resource Graph and Azure Accounts PowerShell modules on your machine, you can run the following commands:
# Install and update to the latest Az PowerShell module
Install-Module -Name Az.Accounts,Az.ResourceGraph -AllowClobber -Force
# Check Az PowerShell modules version installed
Get-Module -Name Az.Accounts,Az.ResourceGraph -ListAvailable | Select Name, Version
Once you installed the “Az.Accounts” and “Az.ResourceGraph” module, you can run the following PowerShell script that uses the Azure PowerShell module to execute the Resource Graph query and store the results in a variable.
# Connect to your Azure account
Connect-AzAccount
# Resource Graph query to get NSGs with RDP port open for all subscriptions
$query = @"
Resources
| where type == "microsoft.network/networksecuritygroups"
| extend rules = parse_json(properties.securityRules)
| mv-expand rule = rules
| where rule.properties.direction == "Inbound" and rule.properties.access == "Allow" and rule.properties.destinationPortRange == "3389"
| project NSGName = name, RuleName = rule.name, AllowedPort = rule.properties.destinationPortRange, Priority = rule.properties.priority, Access = rule.properties.access, Direction = rule.properties.direction, SourceAddressPrefix = rule.properties.sourceAddressPrefix, SourcePortRange = rule.properties.sourcePortRange, DestinationAddressPrefix = rule.properties.destinationAddressPrefix, ResourceGroupName = resourceGroup
"@
# Execute the query and store the results in a variable
$nsgsWithRdpOpen = Search-AzGraph -Query $query
# Export the results to a CSV file
$nsgsWithRdpOpen | Export-Csv -Path "$($home)\nsg-audit.csv" -NoTypeInformation -Force
Here’s what the output results look like:

This is version 2.1, do you want additional features? Please feel free to leave a comment below.
That’s it there you have it!
__
Thank you for reading my blog.
If you have any questions or feedback, please leave a comment.
-Charbel Nemnom-
Any chance you could add the Source field?
Yes sure, the script was updated to include the source field and destination addresses.
Let me know if it works for you.
Awesome :)
Any way to filter for targets that have RDP open and an external IP?
Cheers,
Saint
Hello Saint, thanks for the comment!
Please note that I’ve updated the script to include the source and destination port range, as well as the source and destination IP addresses.
Is this what you are referring to?
Hi,
Can we audit NSGs with Public IPs associated?
Hello Azad, thanks for the comment!
Could you please clarify your question further?
NSGs can be associated at the Subnet or Network interface levels.
Do you mean, you want to audit if the NSGs contain Public IPs only as Source or Destination?
You could use resourceGraph instead of doing an individual GET on each nsg. For environments that have 1000s of NSGs this would take a long time to run where RG Graph would do it in a few seconds. Then you could put the output to a variable and finish off your script.
Hello Sam, thanks for the comment and feedback!
Yes, Azure Resource Graph is recommended for large-scale deployment.
The article was written a long time ago, now it’s updated to use the Azure Resource Graph instead of Azure PowerShell to audit large environments.
Hope it helps!