NSG blocking RDP/SSH despite public IP being assigned
SSH connection times out despite VM having a public IP
Focused incident mode — strips away everything but this runbook.
Symptoms
- SSH connection times out despite VM having a public IP
- az network nic list-effective-nsg shows Deny for port 22 or 3389
- Connection refused or timed out from a machine that previously connected
Evidence to grab
Collect these artifacts before you change anything — they prove (or rule out) each cause.
Effective NSG rules on the NIC
az network nic list-effective-nsg --resource-group <RG> --name <NIC-NAME> --query "securityRuleAccessResults" -o tablePublic IP on the VM
az vm show -g <RG> -n <VM-NAME> --query publicIps -o tsv
Diagnostic flow
Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.
Guided decision tree is ready.
Steps with commands, expected output, and pass/fail branches.
Ranked root causes
Evidence checklist
- Effective rule shows Deny for port 22 with priority < 100
- A recently added security rule with a lower priority number
Confirm before fixing
az network nic list-effective-nsg --resource-group <RG> --name <NIC-NAME> --query "securityRuleAccessResults[?access=='Deny' && direction=='Inbound']" -o jsonFind all inbound Deny rules
Fix
az network nsg rule update --nsg-name <NSG-NAME> --resource-group <RG> --name <RULE-NAME> --priority <PRIORITY> --access Allow --protocol Tcp --destination-port-ranges 22 --source-address-prefixes <MY-IP>Add or update a high-priority Allow rule for SSH
Expected: Successfully updated network security rule
Resolution runbook
Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.
az network nic list-effective-nsg --resource-group <RG> --name <NIC-NAME> --query "securityRuleAccessResults[?direction=='Inbound']" -o tableList effective inbound rules
az network nsg rule update --nsg-name <NSG-NAME> --resource-group <RG> --name <RULE-NAME> --access Allow --protocol Tcp --destination-port-ranges 22 --source-address-prefixes <MY-IP>/32Fix the Deny rule or add an Allow rule
az network watchers test-connectivity --resource-group <RG> --source <VM-NAME> --dest <VM-NAME> --dest-port 22 --query connectionStatus -o tsvVerify the fix
Prevention
- Use just-in-time (JIT) VM access to open RDP/SSH on demand rather than permanently.
- Document all NSG rules in source control and audit changes via Azure Policy.
- Apply NSGs at the subnet level to reduce per-NIC rule duplication.
- Use Azure Bastion for secure RDP/SSH access without exposing public ports.
Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.