Part 5: Control Paths with Route Tables

Break, diagnose, and repair Azure subnet traffic using user-defined routes.

Introduction

30 Second Summary

When a familiar road suddenly closes, the hardest part is finding the exact sign that sent you the wrong way. Everything around the road can still look normal.

In this project, you will use Azure Route Tables to block the private path between two virtual machines with a user-defined route. You will identify the winning decision in Effective routes before removing it to restore connectivity.

What You'll Build

You'll show a live routing experiment where the Azure portal exposes why vm-1 cannot reach vm-2 before your repair brings back four ping replies.

By the end of this project, you'll have:

  • A repeatable two-tier lab that you can restore from one Azure Resource Manager template. You can prove that vm-1 reaches vm-2 over their private addresses.
  • A controlled routing failure that blocks traffic to 10.20.2.0/24 while Azure NAT Gateway continues returning the controlled public IP.
  • A screenshot-ready Effective routes diagnosis that identifies the exact User source. It also reveals the winning prefix plus the None next hop.
  • Secret Mission: Add a broad drop route plus a narrower exception to prove longest-prefix matching with overlapping /16 and /24 routes.

Are there any prerequisites?

You need a Windows device plus an active Azure subscription with permission to create networking resources and virtual machines. This lab creates usage-based resources for less than one hour, so the cleanup section gives you options to reduce charges or delete every lab resource.

Before We Start

This checkpoint commits you to testing the private path from vm-1 at 10.20.1.4 to vm-2 at 10.20.2.4 inside the restored NextWork-Networking lab. Your evidence will come from Effective routes after a controlled failure on snet-web is repaired without changing the NSGs or NAT Gateway settings.

Restore the Part 4 Baseline

A routing experiment needs a known starting point. Any leftover setting in the existing lab could make a later failure ambiguous.

A fresh Azure Resource Manager template removes that state drift. The restored topology gives every later route test the same starting point.

In this step, get ready to:
  • Inventory the dedicated lab resource group before resetting it.
  • Deploy the complete Part 4 baseline from an ARM template.
  • Verify both private VMs plus their networking resources.
Reset the dedicated lab boundary

An Azure resource group is the boundary around this lab. Checking that boundary first protects unrelated resources before the old networking lab is removed.

  • Switch back to Microsoft Edge from earlier.
  • Use the Azure portal search bar to search for Resource groups.
  • Select Resource groups from the results.
  • Look for NextWork-Networking in the resource group list.

✔️ The group is absent

Your subscription has no previous copy of the dedicated lab group. You can create the clean boundary immediately.

  • Select Create on the Resource groups page.
  • Select the subscription you are using for this lab.
  • Enter NextWork-Networking in the resource group name field.
  • Select Central US in the region field.
  • Select Review + create.
  • Select Create after validation succeeds.

You should see the new NextWork-Networking resource group with Central US shown as its location.

ⓧ The group exists

Protect Unrelated Resources

Deleting a resource group can feel risky because Azure removes everything inside it. Continue only when this group contains resources from the networking playlist.

  • Select the existing NextWork-Networking resource group.
  • Review its resource list for the networking lab resources.
  • Stop if you find an unrelated resource that must be preserved.
  • Select Delete resource group.
  • Enter NextWork-Networking when Azure asks for confirmation.
  • Complete the deletion confirmation.

Azure may need several minutes to remove the group. That wait is expected because every resource inside the boundary must be deleted.

  • Return to the Resource groups page after deletion finishes.
  • Confirm NextWork-Networking no longer appears.
  • Select Create.
  • Select the subscription you are using for this lab.
  • Enter NextWork-Networking in the resource group name field.
  • Select Central US in the region field.
  • Select Review + create.
  • Select Create after validation succeeds.

You should see a fresh NextWork-Networking resource group in Central US.

Deploy the complete Part 4 baseline

An ARM template describes the complete network as one deployment. Azure can use that description to recreate the virtual network, security controls, NAT egress, private interfaces, and virtual machines together.

  • Use the Azure portal search bar to search for Template deployment.
  • Select Create for Template deployment.
  • Choose Build your own template in the editor.
  • Remove the sample content from the template editor.

The editor now needs the complete baseline definition. Use the second tab if you need to copy the exact azuredeploy.json template.

✔️ Awesome, I've got everything!

Continue if the template editor already contains both parts of the complete azuredeploy.json template shown in the other tab.

ⓧ I'd like to double check the full code

  • Paste Part 1 into the empty template editor:
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]",
      "metadata": {
        "description": "Location for all lab resources."
      }
    },
    "adminUsername": {
      "type": "string",
      "defaultValue": "azureuser",
      "metadata": {
        "description": "Administrator username for both private Linux VMs."
      }
    },
    "adminPassword": {
      "type": "secureString",
      "minLength": 12,
      "metadata": {
        "description": "Administrator password for both private Linux VMs."
      }
    },
    "vmSize": {
      "type": "string",
      "defaultValue": "Standard_B2s_v2",
      "metadata": {
        "description": "VM size for both lab machines."
      }
    }
  },
  "variables": {
    "vnetName": "vnet-nextwork-lab",
    "webSubnetName": "snet-web",
    "appSubnetName": "snet-app",
    "legacyNsgName": "nsg-lab",
    "webNsgName": "nsg-web",
    "appNsgName": "nsg-app",
    "publicIpName": "public-ip-nat",
    "natGatewayName": "nat-gateway",
    "imageReference": {
      "publisher": "Canonical",
      "offer": "0001-com-ubuntu-server-jammy",
      "sku": "22_04-lts-gen2",
      "version": "latest"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Network/networkSecurityGroups",
      "apiVersion": "2025-07-01",
      "name": "[variables('legacyNsgName')]",
      "location": "[parameters('location')]",
      "properties": {
        "securityRules": []
      }
    },
    {
      "type": "Microsoft.Network/networkSecurityGroups",
      "apiVersion": "2025-07-01",
      "name": "[variables('webNsgName')]",
      "location": "[parameters('location')]",
      "properties": {
        "securityRules": []
      }
    },
    {
      "type": "Microsoft.Network/networkSecurityGroups",
      "apiVersion": "2025-07-01",
      "name": "[variables('appNsgName')]",
      "location": "[parameters('location')]",
      "properties": {
        "securityRules": [
          {
            "name": "Allow-VM1-To-App-HTTP",
            "properties": {
              "priority": 100,
              "protocol": "Tcp",
              "access": "Allow",
              "direction": "Inbound",
              "sourceAddressPrefix": "10.20.1.4",
              "sourcePortRange": "*",
              "destinationAddressPrefix": "*",
              "destinationPortRange": "80"
            }
          },
          {
            "name": "Deny-Web-To-App-HTTP",
            "properties": {
              "priority": 200,
              "protocol": "Tcp",
              "access": "Deny",
              "direction": "Inbound",
              "sourceAddressPrefix": "10.20.1.0/24",
              "sourcePortRange": "*",
              "destinationAddressPrefix": "*",
              "destinationPortRange": "80"
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Network/publicIPAddresses",
      "apiVersion": "2024-05-01",
      "name": "[variables('publicIpName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "StandardV2",
        "tier": "Regional"
      },
      "zones": [
        "1",
        "2",
        "3"
      ],
      "properties": {
        "publicIPAddressVersion": "IPv4",
        "publicIPAllocationMethod": "Static",
        "idleTimeoutInMinutes": 4
      }
    },
    {
      "type": "Microsoft.Network/natGateways",
      "apiVersion": "2024-05-01",
      "name": "[variables('natGatewayName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "StandardV2"
      },
      "zones": [
        "1",
        "2",
        "3"
      ],
      "properties": {
        "idleTimeoutInMinutes": 4,
        "publicIpAddresses": [
          {
            "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpName'))]"
          }
        ]
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpName'))]"
      ]
    },
  • Place your cursor after the final comma in Part 1.
  • Paste Part 2 directly below Part 1 to complete the template:
    {
      "type": "Microsoft.Network/virtualNetworks",
      "apiVersion": "2025-07-01",
      "name": "[variables('vnetName')]",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/networkSecurityGroups', variables('webNsgName'))]",
        "[resourceId('Microsoft.Network/networkSecurityGroups', variables('appNsgName'))]",
        "[resourceId('Microsoft.Network/natGateways', variables('natGatewayName'))]"
      ],
      "properties": {
        "addressSpace": {
          "addressPrefixes": [
            "10.20.0.0/16"
          ]
        },
        "subnets": [
          {
            "name": "[variables('webSubnetName')]",
            "properties": {
              "addressPrefix": "10.20.1.0/24",
              "defaultOutboundAccess": false,
              "networkSecurityGroup": {
                "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('webNsgName'))]"
              },
              "natGateway": {
                "id": "[resourceId('Microsoft.Network/natGateways', variables('natGatewayName'))]"
              }
            }
          },
          {
            "name": "[variables('appSubnetName')]",
            "properties": {
              "addressPrefix": "10.20.2.0/24",
              "defaultOutboundAccess": false,
              "networkSecurityGroup": {
                "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('appNsgName'))]"
              },
              "natGateway": {
                "id": "[resourceId('Microsoft.Network/natGateways', variables('natGatewayName'))]"
              }
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Network/networkInterfaces",
      "apiVersion": "2025-07-01",
      "name": "vm-1-nic",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              "primary": true,
              "privateIPAddress": "10.20.1.4",
              "privateIPAddressVersion": "IPv4",
              "privateIPAllocationMethod": "Static",
              "subnet": {
                "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnetName'), variables('webSubnetName'))]"
              }
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Network/networkInterfaces",
      "apiVersion": "2025-07-01",
      "name": "vm-2-nic",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
      ],
      "properties": {
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              "primary": true,
              "privateIPAddress": "10.20.2.4",
              "privateIPAddressVersion": "IPv4",
              "privateIPAllocationMethod": "Static",
              "subnet": {
                "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnetName'), variables('appSubnetName'))]"
              }
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2023-09-01",
      "name": "vm-1",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/networkInterfaces', 'vm-1-nic')]"
      ],
      "properties": {
        "hardwareProfile": {
          "vmSize": "[parameters('vmSize')]"
        },
        "storageProfile": {
          "imageReference": "[variables('imageReference')]",
          "osDisk": {
            "createOption": "FromImage",
            "deleteOption": "Delete",
            "managedDisk": {
              "storageAccountType": "Standard_LRS"
            }
          }
        },
        "networkProfile": {
          "networkInterfaces": [
            {
              "id": "[resourceId('Microsoft.Network/networkInterfaces', 'vm-1-nic')]",
              "properties": {
                "primary": true
              }
            }
          ]
        },
        "osProfile": {
          "computerName": "vm-1",
          "adminUsername": "[parameters('adminUsername')]",
          "adminPassword": "[parameters('adminPassword')]",
          "linuxConfiguration": {
            "disablePasswordAuthentication": false,
            "provisionVMAgent": true
          }
        }
      }
    },
    {
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2023-09-01",
      "name": "vm-2",
      "location": "[parameters('location')]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/networkInterfaces', 'vm-2-nic')]"
      ],
      "properties": {
        "hardwareProfile": {
          "vmSize": "[parameters('vmSize')]"
        },
        "storageProfile": {
          "imageReference": "[variables('imageReference')]",
          "osDisk": {
            "createOption": "FromImage",
            "deleteOption": "Delete",
            "managedDisk": {
              "storageAccountType": "Standard_LRS"
            }
          }
        },
        "networkProfile": {
          "networkInterfaces": [
            {
              "id": "[resourceId('Microsoft.Network/networkInterfaces', 'vm-2-nic')]",
              "properties": {
                "primary": true
              }
            }
          ]
        },
        "osProfile": {
          "computerName": "vm-2",
          "adminUsername": "[parameters('adminUsername')]",
          "adminPassword": "[parameters('adminPassword')]",
          "linuxConfiguration": {
            "disablePasswordAuthentication": false,
            "provisionVMAgent": true
          }
        }
      }
    }
  ],
  "outputs": {
    "vm1PrivateIp": {
      "type": "string",
      "value": "10.20.1.4"
    },
    "vm2PrivateIp": {
      "type": "string",
      "value": "10.20.2.4"
    }
  }
}

What Does This Template Restore?

  • The template creates vnet-nextwork-lab with the snet-web and snet-app subnets.
  • The template attaches matching Network Security Groups plus the nat-gateway resource to both subnets.
  • The template gives each Ubuntu VM a static private address through its dedicated network interface.
  • The outputs preserve 10.20.1.4 for vm-1 plus 10.20.2.4 for vm-2.
  • Paste the complete template into the editor.
  • Select Save.
  • Select NextWork-Networking in the resource group field.
  • Keep Central US for the location parameter.
  • Keep azureuser for the administrator username.
  • Enter a private administrator password containing at least 12 characters.
  • Keep Standard_B2s_v2 for the VM size.
  • Select Review + create to validate the deployment.

You should see the template pass validation. This confirms Azure accepts the resource definitions plus your parameter values.

Paid Resources Start Here

Creating this deployment starts usage-based Azure resources. The cleanup section shows how to stop further charges when you finish the lab.

  • Select Create to start the deployment.

Provisioning two virtual machines plus the network can take several minutes. A quiet deployment page during this wait does not mean the process has stalled.

  • Wait for Azure to report that the deployment completed successfully.
  • Return to NextWork-Networking from the deployment page.

That is the longest setup task complete. Azure has rebuilt the private two-tier network from one repeatable definition.

Deployment Did Not Complete?

  • Check that the administrator password contains at least 12 characters.
  • Confirm the resource group location plus the template location parameter both show Central US.
  • Retry later if your subscription reports a capacity or quota problem for Standard_B2s_v2.

Share the deployment details without your password when you ask for help diagnosing the failed ARM deployment.

Verify the restored topology

A successful deployment status confirms that Azure processed the template. The resource views now confirm that the live topology matches the routing experiment's baseline.

  • Return to the NextWork-Networking resource list.
  • Confirm vm-1 shows Running.
  • Confirm vm-2 shows Running.
  • Confirm vnet-nextwork-lab appears in the resource list.
  • Confirm nat-gateway plus public-ip-nat appear in the resource list.
  • Confirm nsg-lab, nsg-web, and nsg-app appear in the resource list.

The resource list proves the deployment created each major component. Next, check the associations that control the actual packet path.

  • Open vnet-nextwork-lab from the resource group.
  • Select Address space.
  • Confirm the virtual network address space is 10.20.0.0/16.
  • Select Subnets.
  • Open snet-web.

Check the Web Subnet

  • The address prefix is 10.20.1.0/24.
  • The associated network security group is nsg-web.
  • The associated NAT Gateway is nat-gateway.
  • Default outbound access is disabled.
  • No route table is associated yet.
  • Return to the subnet list.
  • Open snet-app.

Check the Application Subnet

  • The address prefix is 10.20.2.0/24.
  • The associated network security group is nsg-app.
  • The associated NAT Gateway is nat-gateway.
  • Default outbound access is disabled.
  • No route table is associated.

Finding each private address takes a few clicks because Azure stores it on the VM's network interface. The network interface view also proves that neither VM has a public IP.

  • Return to NextWork-Networking.
  • Open vm-1.
  • Select Networking.
  • Open vm-1-nic.
  • Select IP configurations.
  • Open ipconfig1.

Check the First Private Interface

  • The private IPv4 address is 10.20.1.4 with static allocation.
  • The selected subnet is snet-web in vnet-nextwork-lab.
  • No public IP address is attached.
  • Return to NextWork-Networking.
  • Open vm-2.
  • Select Networking.
  • Open vm-2-nic.
  • Select IP configurations.
  • Open ipconfig1.

Check the Second Private Interface

  • The private IPv4 address is 10.20.2.4 with static allocation.
  • The selected subnet is snet-app in vnet-nextwork-lab.
  • No public IP address is attached.

Before the final check, which restored resources do you expect to see beside both running VMs?

  • Return to the NextWork-Networking resource list.
  • Confirm both VMs still show Running.
  • Confirm the virtual network, NAT Gateway, public IP, and three network security groups remain listed.

You should see vm-1 plus vm-2 running beside vnet-nextwork-lab, nat-gateway, public-ip-nat, nsg-lab, nsg-web, and nsg-app. The clean Part 4 baseline is ready for a controlled routing test.

Your private two-tier network is restored with controlled NAT egress. Next, you will prove that Azure's system route carries traffic between the two subnets.

Prove the System Route Works

The restored Azure Virtual Network gives vm-1 a private path to vm-2. Before you change its routing behavior, you need proof that this original path works.

Azure automatically adds a system route for the VNet address space. This step proves that route carries cross-subnet traffic before a user-defined route changes the decision.

In this step, get ready to:
  • Record the assigned IPv4 address for public-ip-nat.
  • Use one baseline script to test private reachability plus public egress from vm-1.
  • Inspect the Effective routes view for vm-1-nic.
Record the NAT address

The Azure NAT Gateway gives both private subnets a controlled public egress address. Recording that address gives you a reference for the public test.

  • In the Azure portal from earlier, return to NextWork-Networking.
  • Select public-ip-nat from the resource list.
  • On Overview, copy the assigned IPv4 address.
  • Record the address here: your NAT public IPv4 address.
  • Confirm the public IP uses StandardV2.
  • Confirm the public IP is attached to nat-gateway.

That is the first proof point locked in.

Run the private and public baseline

Azure Run Command uses the VM agent to execute a shell script inside vm-1. The script tests the private destination first.

The same script then reports the public address used for internet traffic. Matching that result to your recorded address proves the egress path uses nat-gateway.

  • Return to NextWork-Networking in the Azure portal.
  • Select vm-1 from the resource list.
  • Under Operations, select Run command.
  • Choose RunShellScript.
  • Load both baseline checks into the script editor by pasting the code below:
echo "PRIVATE PATH"
ping -c 4 10.20.2.4
echo "PUBLIC PATH"
curl -s https://api.ipify.org
echo

What Does This Script Prove?

  • The first label separates the private connectivity result from the public egress result.
  • The ping command sends four requests from vm-1 to 10.20.2.4.
  • The curl command asks ipify which public IPv4 address received the request.

Before you select Run, do you expect both paths to succeed from a VM with no public IP?

  • Select Run to execute the script.

Under PRIVATE PATH, you will see four replies from 10.20.2.4. The summary reports 0% packet loss.

The value under PUBLIC PATH matches your NAT public IPv4 address.

That is the baseline locked in. Private traffic crosses between the subnets while public traffic leaves through nat-gateway.

Baseline Results Do Not Match?

  • Confirm vm-1 still shows Running.
  • Confirm vm-2 still shows Running.
  • Check that the private destination in the script is exactly 10.20.2.4.
  • In vnet-nextwork-lab, select snet-web.
  • Confirm snet-web still uses nat-gateway if the public address does not match.

Help me diagnose the private or public baseline result.

Inspect the original system path

The successful ping proves that packets reach the application subnet. The Effective routes view reveals the route source that carried them.

This view also identifies the destination prefix. It shows the next hop Azure selected.

  • Return to vm-1 in the Azure portal.
  • Select Networking from the VM menu.
  • Select vm-1-nic.

Before you inspect the rows, which route source do you expect to control traffic to 10.20.2.4?

  • Select Effective routes.
  • Locate the row with source System plus address prefix 10.20.0.0/16.
  • Confirm its next hop type is Virtual network.
  • Check that no route with source User targets 10.20.2.0/24.

You will see the System route for 10.20.0.0/16 using the Virtual network next hop. You will not see a User route for 10.20.2.0/24.

Why Does the System Route Work?

The destination 10.20.2.4 belongs to the VNet address space 10.20.0.0/16. Azure uses the system Virtual network route to carry traffic between its subnets.

No user-defined route targets 10.20.2.0/24 yet. The system route therefore controls the private packet path.

Your working route baseline is now documented from the VM plus the NIC. Next, you will add one precise route that breaks only the application path while leaving NAT egress intact.

Break the Application Path with a UDR

Your successful baseline proved that Azure's system Virtual network route carries traffic from snet-web to snet-app.

That broad 10.20.0.0/16 route cannot express a selective drop for only the application subnet.

A more-specific user-defined route targets 10.20.2.0/24 with a None next hop. Its route table will attach to snet-web.

The NAT Gateway stays unchanged. The network security groups also stay unchanged.

In this step, get ready to:
  • Create the rt-web-paths route table.
  • Connect snet-web to the block-app-path route through the route table.
  • Rerun the baseline test to observe the isolated path failure.
Create the subnet route table

A route table holds custom forwarding decisions for its associated subnets. Azure combines those decisions with its default routes when it calculates an effective path.

This lab has no virtual network gateway. The gateway route propagation setting can stay at its default value.

  • Return to the Azure portal in Microsoft Edge.
  • Search for Route tables using the portal search bar.
  • Select Route tables from the search results.
  • Select Create.

You'll see the route table creation form.

  • Select NextWork-Networking in Resource group.
  • Enter rt-web-paths in Name.

The form now places the new route table inside the restored lab boundary.

  • Select Central US in Region.
  • Keep the gateway route propagation setting at its default value.

The form now shows the required resource group name. It also shows the required Azure region.

  • Submit the route table for validation by selecting Review + create.
  • Create rt-web-paths by selecting Create after validation passes.

Good start. Azure now has an rt-web-paths route table ready to hold the web subnet's custom path decisions.

Route table creation blocked?

  • Confirm that NextWork-Networking is selected as the resource group.
  • Confirm that Central US is selected as the region.

Azure may block creation if your account lacks permission for the resource group. Help me troubleshoot why Azure did not create rt-web-paths.

Add and associate the failure route

The None next hop discards traffic that matches a route's prefix. A subnet association determines which source subnet receives that decision.

  • Select Routes in rt-web-paths.
  • Select + Add.
  • Enter block-app-path in Route name.
  • Enter 10.20.2.0/24 in Address prefix.
  • Select None for Next hop type.
  • Create the route by selecting Add.

You'll see block-app-path in the route list with prefix 10.20.2.0/24.

What did you configure?

  • The 10.20.2.0/24 prefix covers the application subnet that contains vm-2.
  • The None next hop tells Azure to discard matching traffic.
  • The route has no effect until its route table is associated with a subnet.
  • Return to the Azure portal search bar.
  • Search for vnet-nextwork-lab.
  • Select vnet-nextwork-lab from the search results.
  • Select Subnets.
  • Select snet-web.
  • Select rt-web-paths in Route table.
  • Apply the association by selecting Save.

You'll return to the subnet list with rt-web-paths shown for snet-web.

  • Return to the Azure portal search bar.
  • Search for rt-web-paths.
  • Select rt-web-paths from the search results.
  • Select Subnets.

You'll see snet-web listed as an associated subnet. The custom route now applies to traffic leaving that subnet.

Subnet association missing?

  • Return to snet-web in vnet-nextwork-lab.
  • Confirm that rt-web-paths is selected in Route table.
  • Select Save again if the association was not applied.

The route table must use the same Azure location as its associated subnet. Help me troubleshoot why snet-web is missing from rt-web-paths.

Observe the designed failure

The same private and public checks keep every other part of the experiment constant. Their combined result isolates the route table as the only new variable.

  • Return to the Azure portal search bar.
  • Search for vm-1.
  • Select vm-1 from the search results.
  • Select Run command under Operations.
  • Choose RunShellScript.
  • Paste the exact baseline script into the script field:
echo "PRIVATE PATH"
ping -c 4 10.20.2.4
echo "PUBLIC PATH"
curl -s https://api.ipify.org
echo

What does this script test?

  • The PRIVATE PATH section tests cross-subnet traffic from vm-1 to 10.20.2.4.
  • The -c 4 option stops the ping after four requests.
  • The PUBLIC PATH section checks the public IPv4 address used for internet egress.
  • The final echo places the command result on its own line.

Before you select Run, do you expect both checks to change or only one?

  • Execute the script by selecting Run.

You'll see the ping section report 100% packet loss for 10.20.2.4. The public section still returns the recorded public-ip-nat IPv4 address.

That controlled split is the proof. block-app-path breaks only traffic to the application subnet.

The NAT Gateway remains available for public egress. The network security groups remain unchanged.

Private path still working?

  • Confirm that block-app-path uses the exact prefix 10.20.2.0/24.
  • Confirm that its next hop type is None.
  • Confirm that rt-web-paths lists snet-web under Subnets.

If the public address also changes or disappears, confirm that you pasted the complete baseline script. Help me troubleshoot the unexpected RunShellScript result.

  • Capture the RunShellScript output with both test sections visible.

You now have a repeatable routing failure with public egress still intact. Next, you'll use Effective routes to identify the exact prefix and next hop that won.

Diagnose the Failure with Effective Routes

Your controlled routing failure is now visible. The private ping from vm-1 to vm-2 fails.

NAT egress still works through nat-gateway. That isolates the user-defined route as the change affecting the private path.

The Effective routes view combines the routes available to vm-1-nic. Each row names the route source.

The address prefix identifies matching destinations. The next hop shows where those packets go.

In this step, get ready to:
  • Find the User route that controls traffic to 10.20.2.0/24.
  • Use the two prefix lengths to explain Azure's routing decision.
Find the active user route

The view is available because vm-1-nic is attached to the running vm-1. That makes the NIC the right place to inspect the failed outbound path.

  • Return to the Microsoft Azure portal tab from earlier in Microsoft Edge.
  • Open vm-1 from Virtual machines.
  • Confirm vm-1 shows Running.
  • Select Networking from the vm-1 menu.
  • Select vm-1-nic.
  • Select Effective routes.

The route list now shows the routing candidates available to traffic leaving vm-1-nic.

  • Locate the row whose Source is User.
  • Confirm its Address prefix is 10.20.2.0/24.
  • Confirm its Next hop type is None.

What Does This Row Prove?

The User source identifies a route supplied by the route table associated with snet-web.

The None next hop drops traffic covered by 10.20.2.0/24.

🙋‍♀️ No Route Rows Visible?

  • Return to vm-1.
  • Confirm its status is Running.
  • Select vm-1-nic again after the VM is running.
  • Reload Effective routes.

Help me troubleshoot an empty Effective routes view for vm-1-nic.

Compare the competing routes

A destination can match more than one route. Azure resolves that overlap with longest-prefix matching.

A longer prefix covers a smaller address range. The matching route with the longest prefix controls the packet path.

  • Locate the row whose Source is System.
  • Confirm its Address prefix is 10.20.0.0/16.
  • Confirm its Next hop type is Virtual network.

Before you compare the rows, which route do you think Azure selects for traffic from 10.20.1.4 to 10.20.2.4?

  • Compare the /24 prefix length with the /16 prefix length.
  • Capture both route rows in one screenshot.

You'll see that the User 10.20.2.0/24 route is more specific than the System 10.20.0.0/16 route. Azure therefore chooses the None next hop.

You've pinned down the failure. The winning route drops packets destined for 10.20.2.4.

Your evidence now identifies the exact route behind the failed ping. Next, you'll remove only block-app-path to test whether Azure falls back to the system route.

Remove the Bad Route and Prove Recovery

Your Effective routes diagnosis traced the failed ping to block-app-path. This user-defined route controls traffic to 10.20.2.0/24.

Deleting only the bad route tests whether Microsoft Azure falls back to the unchanged System route for 10.20.0.0/16 with next hop Virtual network.

The private test isolates the effect of that deletion. The public check confirms whether NAT Gateway egress stays stable.

In this step, get ready to:
  • Delete block-app-path while preserving the route table association.
  • Rerun the baseline script to test the repaired network path.
  • Refresh Effective routes to confirm the system path has taken over.
Remove the faulty decision

Removing a route changes the choices Azure evaluates for new packets. The original system route remains available.

Deleting a route can feel risky. This change removes only block-app-path.

The route table rt-web-paths stays attached to snet-web. The test remains focused on one route decision.

  • Select the Azure portal search bar.
  • Enter rt-web-paths.
  • Select rt-web-paths from the results.
  • Select Routes.
  • Find block-app-path in the route list.
  • Select the ellipsis next to block-app-path.
  • Select Delete.
  • Select Yes to confirm the deletion.

You removed the exact route behind the failure. The Routes list for rt-web-paths is now empty.

The route table remains associated with snet-web.

Prove the route fallback

The remaining effective routes now decide where private traffic goes. Reusing the identical script keeps the route deletion as the only changed variable.

  • Select the Azure portal search bar.
  • Enter vm-1.
  • Select vm-1 from the results.
  • Select Run command under Operations.
  • Choose RunShellScript.
  • Restore the baseline check by pasting this exact script into the script editor:
echo "PRIVATE PATH"
ping -c 4 10.20.2.4
echo "PUBLIC PATH"
curl -s https://api.ipify.org
echo

What Does This Recovery Script Prove?

  • The echo commands separate the private result from the public result.
  • The ping -c 4 10.20.2.4 command sends four requests across the private network path.
  • The curl -s https://api.ipify.org command reports the public IPv4 address used for egress.

Before you select Run, do you expect the private test to recover while the public address stays the same?

  • Select Run to execute the recovery test.

You'll see four replies with 0% packet loss in the private section. The public section returns the same IPv4 address assigned to public-ip-nat.

You have proved that deleting the faulty route restores private connectivity without disturbing NAT-controlled egress.

Recovery Output Looks Wrong?

  • Confirm that block-app-path no longer appears in the Routes list for rt-web-paths.
  • Confirm that vm-2 still shows Running.
  • Confirm that nat-gateway still uses public-ip-nat if the public output differs.

Help me diagnose why the recovery script still shows the wrong result.

The command output proves that traffic recovered. A refreshed Effective routes view reveals which route now carries it.

  • Select Networking for vm-1.
  • Select vm-1-nic.
  • Select Effective routes.

Before you refresh, which route source do you expect to remain for traffic to 10.20.2.4?

  • Refresh the Microsoft Edge page.

You'll no longer see a User route for 10.20.2.0/24. You'll still see the System route for 10.20.0.0/16 with next hop Virtual network.

You have repaired the private path by changing one route decision. The stable public IP proves the egress path stayed intact.

Secret mission

Win with the More-Specific Route

Create a broad route that drops traffic across the virtual network. Add a narrower application-subnet exception that restores the private path. Prove why `/24` beats `/16` in Effective routes.

Clean Up Your Resources

Clean Up Your Resources

Your Azure lab can continue generating usage charges while its resources exist. Decide whether to keep everything running, pause the lab for later, or delete it entirely.

Cost warning

The NAT Gateway resource nat-gateway incurs hourly charges from the moment it is created. Billing continues even without traffic.

Data processing can add usage charges. Bandwidth can add more.

Running Azure Virtual Machines accrue compute charges. Managed disks can incur storage charges. The public IP can also incur charges.

Resources you used:

  • The Azure resource group NextWork-Networking in Central US.
  • The Azure Virtual Network vnet-nextwork-lab with the subnets snet-web and snet-app.
  • The private virtual machines vm-1 and vm-2 with their network interfaces and managed OS disks.
  • The network security groups nsg-lab, nsg-web, and nsg-app.
  • The StandardV2 NAT Gateway nat-gateway with the static StandardV2 public IP public-ip-nat.
  • The route table rt-web-paths, which remains associated with snet-web and contains no custom routes.

Keep everything running

No action needed. Choose this if Part 6 starts immediately despite the ongoing usage charges.

  • Leave vm-1 and vm-2 Running.
  • Keep nat-gateway and public-ip-nat attached for controlled public egress.
  • Keep the empty rt-web-paths route table associated with snet-web.

Pause - I'll come back to this later

Shut down running compute resources to reduce active charges while preserving the lab's network configuration. Retained managed disks can still incur storage charges.

  • On vm-1's Overview page, stop the VM.
  • Wait until vm-1 shows Stopped (deallocated).
  • On vm-2's Overview page, stop the VM.
  • Wait until vm-2 shows Stopped (deallocated).
  • In the vnet-nextwork-lab subnet settings, dissociate nat-gateway from snet-web.
  • In the same subnet settings, dissociate nat-gateway from snet-app.
  • Delete nat-gateway from NextWork-Networking.
  • Delete public-ip-nat from NextWork-Networking.

You should no longer see nat-gateway or public-ip-nat in the resource list. Your VNet, NSGs, NICs, managed disks, and empty route table remain available for restoration.

Delete - I don't want to use this again

Deleting the resource group looks drastic because it removes the entire lab boundary. This permanently deletes every resource listed above.

  • Return to NextWork-Networking in Resource groups to confirm it contains only this lab's resources.
  • Select Delete resource group.
  • Enter NextWork-Networking in the confirmation field.
  • Complete the deletion confirmation.
  • Refresh Resource groups.

You should no longer see NextWork-Networking in the resource group list. That clears the complete Azure lab and stops its ongoing resource usage.

Nice Work!

Nice Work!

Routing experiment complete! You can now control the private path between vm-1 and vm-2 with Azure Route Tables.

Here's what you learned:

  • Restored the Part 4 baseline from one Azure Resource Manager template. Your private VMs retained controlled public egress through the StandardV2 NAT Gateway.
  • Proved the system Virtual network route carried traffic between snet-web and snet-app. Created a more-specific user-defined route that dropped traffic to 10.20.2.0/24.
  • Diagnosed the failure through Effective routes. You identified the exact User prefix plus its None next hop. Deleting the bad route restored four replies with 0% packet loss.
  • Completed the optional Secret Mission with drop-vnet plus allow-app-specific. The /24 Virtual network exception won over the /16 None route.

Ready to quiz yourself?