Remediation Guides

Management Ports Should Be Closed on Your Virtual Machines: Azure Fix Guide

26 September 2026 8 min read

“Management ports should be closed on your virtual machines” is a Microsoft Defender for Cloud recommendation raised when a network security group (NSG) lets SSH (22), RDP (3389) or WinRM (5985/5986) in from Any or Internet. Fix it by deleting or narrowing those inbound allow rules, then give admins just-in-time (JIT) access or Azure Bastion instead.

What Defender for Cloud is actually detecting

This is an Azure configuration finding, not a missing patch or a port scan result: Defender for Cloud evaluates the inbound rules on the NSGs attached to the VM’s subnet and network interface. The recommendation is backed by the built-in Azure Policy definition 22730e10-96f6-4aac-ad84-9383d35b5917 (effect AuditIfNotExists), which reads the security assessment bc303248-3d14-44c2-96a0-55f5c326b5fe and treats the VM as compliant only when that assessment is Healthy or NotApplicable.

Defender for Cloud recommendation Severity What it points at
Management ports should be closed on your virtual machines Medium Open remote management ports that expose the VM to Internet-based brute force attempts
Management ports of virtual machines should be protected with just-in-time network access control High Overly permissive inbound NSG rules for management ports on a VM that could use JIT
All network ports should be restricted on network security groups associated to your virtual machine High Any inbound rule that allows access from Any or Internet ranges, whatever the port

One permissive rule often triggers all three, so fixing it addresses each. Microsoft’s JIT feature treats 22 (SSH), 3389 (RDP), 5985 and 5986 (WinRM) as the management ports to protect, so check those first. If you need to decide where this sits in a long backlog, see our guide to triaging Defender for Cloud recommendations at scale.

Real-world risk

Microsoft’s own description is plain: open remote management ports expose the VM to Internet-based attacks that try to brute force credentials to gain admin access. Any public IP answering on 22 or 3389 will be found by automated scanning. Key-only SSH, or RDP with Network Level Authentication and strong unique passwords, holds up far better than a guessable local administrator password, but either service stays exposed to the next pre-authentication flaw, as BlueKeep (CVE-2019-0708) was for RDP and regreSSHion (CVE-2024-6387) for OpenSSH.

Azure does not open these ports by itself. Standard SKU public IPs (Basic SKU public IPs were retired on 30 September 2025) are secure by default and closed to inbound traffic until an NSG rule allows it. If this finding appears, someone added that rule, typically through the portal’s Allow selected ports option during VM creation or with az vm open-port.

How to confirm it

Run these in Azure Cloud Shell or a local Azure CLI session. Find the VM’s network interfaces and public IPs:

az vm show -g RG -n VM --query "networkProfile.networkInterfaces[].id" -o tsv
az vm list-ip-addresses -g RG -n VM -o table

List the inbound allow rules on each NSG involved (the subnet NSG and the NIC NSG):

az network nsg rule list -g RG --nsg-name NSG 
  --query "[?direction=='Inbound' && access=='Allow'].{name:name, priority:priority, source:sourceAddressPrefix, sources:sourceAddressPrefixes, port:destinationPortRange, ports:destinationPortRanges}" 
  -o json

Look for a source of *, Internet, 0.0.0.0/0 or ::/0 combined with 22, 3389, 5985, 5986 or a port range that contains them. To see the combined effect of both NSGs, list the effective rules (the VM must be running for this to return data):

az network nic list-effective-nsg -g RG -n NIC

Then ask Network Watcher IP flow verify whether an arbitrary Internet address could open RDP. For inbound tests, * is accepted as the remote port, and 203.0.113.10 is a documentation address:

az network watcher test-ip-flow -g RG --vm VM --direction Inbound --protocol TCP 
  --local 10.0.0.4:3389 --remote 203.0.113.10:*

An Allow result names the rule responsible. To read the Defender assessment for one VM, or to list every unhealthy VM across the subscriptions you can see (the second command needs the resource-graph CLI extension):

az security assessment show -n bc303248-3d14-44c2-96a0-55f5c326b5fe 
  --assessed-resource-id "/subscriptions/SUB/resourceGroups/RG/providers/Microsoft.Compute/virtualMachines/VM" 
  --query status

az graph query -q "SecurityResources | where type == 'microsoft.security/assessments' and name == 'bc303248-3d14-44c2-96a0-55f5c326b5fe' | where properties.status.code == 'Unhealthy' | project id, subscriptionId"

How to fix it

Step 1: delete or narrow the permissive rule

Save the rule first so you can roll back:

az network nsg rule show -g RG --nsg-name NSG -n RULE -o json > RULE-backup.json

If nobody needs direct access, delete it:

az network nsg rule delete -g RG --nsg-name NSG -n RULE

If a fixed admin network still needs direct access, replace Any with that network’s CIDR. –source-address-prefixes takes a space-separated list:

az network nsg rule update -g RG --nsg-name NSG -n RULE 
  --source-address-prefixes 198.51.100.0/24

The deletion in Azure PowerShell:

$nsg = Get-AzNetworkSecurityGroup -Name NSG -ResourceGroupName RG
Remove-AzNetworkSecurityRuleConfig -Name RULE -NetworkSecurityGroup $nsg
$nsg | Set-AzNetworkSecurityGroup

In the portal, open the NSG, select Inbound security rules, open the rule, and either restrict Source to specific IP addresses or change Action to Deny. Fix both the subnet NSG and the NIC NSG; a wide rule that another NSG happens to block today is exposed again when associations change.

Step 2: give admins just-in-time access

JIT requires Microsoft Defender for Servers Plan 2 on the subscription and VMs deployed through Azure Resource Manager. In Defender for Cloud, open Workload protections, select Just-in-time VM access, mark the VMs on the Not configured tab and select Enable JIT on VMs. Defender suggests 22, 3389, 5985 and 5986; for each port you set the protocol, allowed source IPs and maximum request time. Enabling JIT from the VM’s own Configuration page uses defaults that allow source IPs of Any, so tighten them afterwards. The PowerShell equivalent for RDP:

$JitPolicy = (@{
    id="/subscriptions/SUB/resourceGroups/RG/providers/Microsoft.Compute/virtualMachines/VM";
    ports=(@{
        number=3389;
        protocol="*";
        allowedSourceAddressPrefix=@("198.51.100.0/24");
        maxRequestAccessDuration="PT3H"})})
$JitPolicyArr=@($JitPolicy)
Set-AzJitNetworkAccessPolicy -Kind "Basic" -Location "LOCATION" -Name "VM" -ResourceGroupName "RG" -VirtualMachine $JitPolicyArr

Order matters. Defender ensures deny-all-inbound rules exist for the selected ports, but existing rules for those ports take priority, so enabling JIT while the old allow rule remains closes nothing. Finish Step 1 first. An approved request opens the port for the requester’s IP for the approved time, then Defender restores the NSG.

Step 3 (alternative): use Azure Bastion

With Bastion, users connect over HTTPS (443) to the Bastion host, and the target VM needs no public IP. On the target VM subnet’s NSG, allow 3389 and 22 only from the AzureBastionSubnet range:

az network nsg rule create -g RG --nsg-name VM-SUBNET-NSG -n Allow-Bastion-Mgmt 
  --priority 200 --direction Inbound --access Allow --protocol Tcp 
  --source-address-prefixes AZUREBASTIONSUBNET-CIDR --destination-port-ranges 22 3389

If the VM’s public IP existed only for administration, dissociate it:

az network nic ip-config update -g RG --nic-name NIC --name IPCONFIG --public-ip-address null

For Windows VMs, pair the network fix with the host-side settings in our RDP hardening guide.

How to verify the fix and rescan

  • Run az network watcher test-ip-flow again. You want Deny, from DenyAllInBound or a JIT deny rule.
  • From a network outside Azure, test a new connection: Test-NetConnection PUBLIC-IP -Port 3389 in PowerShell or nc -vz PUBLIC-IP 22 on Linux. It should fail.
  • Re-run az security assessment show until the status code reads Healthy. It changes on Defender’s next evaluation, not the moment you save the rule.
  • If an external scanner also reported the open port, rescan the public IP.

What can break and how to roll back

  • Automation that connects directly: configuration management over SSH or WinRM, deployment pipelines, backup or monitoring agents, and vendor remote support. Identify who uses the port before deleting it; virtual network flow logs help if enabled.
  • Your own access: set up JIT or Bastion before removing the last allow rule, and keep Run Command or Serial Console as an out-of-band fallback.
  • Misleading tests: removing an NSG rule does not interrupt existing connections; only new connections are blocked. An RDP session that stays open proves nothing, so always test with a fresh connection.

To roll back, re-create the rule from RULE-backup.json with az network nsg rule create using the same name, priority, ports, protocol and source, or run az network nsg rule update with the previous source. To change JIT settings, open Just-in-time VM access, go to the Configured tab, right-click the VM and select Edit.

Common false positive reasons

  • Traffic is dropped at another layer. An Azure Firewall, a network virtual appliance or the other NSG may block what the flagged rule allows. For inbound traffic Azure processes the subnet NSG and then the NIC NSG, and both must allow the port. IP flow verify shows what the NSGs actually do, but tighten the rule anyway, because the other layer can change.
  • Stale evaluation. The rule was fixed recently and Defender has not re-assessed the VM yet.
  • Intended exposure. A public SFTP endpoint on 22 is reported correctly; that is accepted risk, not a false positive. Record it with an exemption marked Risk accepted or Mitigated. Exemptions need the Microsoft Cloud Security Benchmark initiative assigned on the subscription and Owner or Security Admin rights, and can take up to 24 hours to take effect.

FAQ

Does enabling JIT close the finding by itself?

Not while the old allow rule exists. Existing rules for the selected ports take priority over the JIT deny rules, so remove or narrow them first.

My SSH only accepts keys. Is port 22 open to the Internet still a finding?

Yes. The recommendation evaluates network rules, not how the service authenticates. Key-only SSH lowers the risk, but anyone can still reach the port.

Do I need to buy Defender for Servers to fix this?

No. Deleting or narrowing NSG rules costs nothing. JIT requires Defender for Servers Plan 2, and Bastion is a separately billed Azure service.

Is removing the public IP enough?

It stops direct Internet access, but a rule with source Any still admits every internal, peered and on-premises network, and it would expose the port again the moment a public IP is reattached. Clean up the rule too.

Tracking this finding across many hosts

Because the fix lives in the NSG rather than on the VM, the Resource Graph query above is the quickest way to watch it across subscriptions in Defender for Cloud. If you also run SITEY, a self-hosted vulnerability management platform that imports findings from 16 scanners, note that it merges duplicates per scanner, not across scanners, so the same exposed port reported by two different scanners stays as two findings. Its AI triage can suggest false positives with evidence, but a person makes the final call.

Sources

SITEY closes the loop, not just the report.Discover, validate, fix and verify in your own infrastructure.

See pricing