# Azure Virtual Network Manager Deep Dive: Network Groups, Connectivity, Security Admin Rules, and IPAM

![Neon Azure Virtual Network Manager architecture showing four IPAM-allocated VNets, tag-driven mesh connectivity, and centralized Security Admin enforcement.](assets/azure-virtual-network-manager-deep-dive-cover.png)

Azure Virtual Network Manager is what you reach for when one VNet becomes twenty, twenty becomes two hundred, and manual peerings, duplicate NSG rules, and spreadsheet-based address planning stop scaling. In this workshop we build a compact but realistic governance lab: four VNets allocated from centralized IPAM, tag-driven membership through Azure Policy, a regional connected-group mesh, and a Security Admin rule that overrides a local NSG.

The deliberate failures are the point. The Edge VNet starts with the tag typo `NetworkGroup=Mseh`, so it is excluded from the mesh. After repairing the tag, both proof ports work. We then deploy a central deny for TCP 8080 and prove that it blocks the connection even though the subnet NSG explicitly allows 8080 and 8443. Port 8443 remains open, giving us a clean control case.

**Reading time:** 30–35 minutes  
**Execution time:** 60–90 minutes, including Azure Policy and configuration propagation  
**Tools:** PowerShell 7.2+, Azure CLI, `virtual-network-manager` CLI extension  
**Region:** West Europe  
**Cost:** two short-lived `Standard_B1s` Ubuntu VMs, Standard public IPs for outbound access, and Azure Virtual Network Manager charges. Remove the lab when finished.

> This is a disposable workshop. Use a non-production subscription, bind every command to the exact active subscription ID, and remove only `rg-avnm-deep-dive-we`. The public IP resources provide explicit outbound connectivity; there is no inbound SSH rule.

## What we will prove

By the end of the lab you will have evidence that:

- four non-overlapping `/20` VNet prefixes came from one West Europe IPAM child pool;
- the reserved `10.140.240.0/20` range stayed unused;
- a `Microsoft.Network.Data` policy added only correctly tagged VNets to a network group;
- the tag typo produced exactly three members and no Core-to-Edge route;
- correcting the tag added Edge without manual membership or peering;
- the Core NIC received `ConnectedGroup` routes for the other VNets;
- both TCP 8080 and 8443 worked after the mesh repair;
- a Security Admin Deny stopped 8080 before NSG evaluation;
- TCP 8443 remained open through the local NSG; and
- cleanup removed the policy, configurations, IPAM allocations, networking, VMs, and resource group.

Microsoft describes Virtual Network Manager as a centralized service for grouping network resources and deploying connectivity, security, routing, and IP address management at scale. A configuration has no effect until it is deployed to a region. That separation between *definition* and *deployment* is central to this workshop. See the [Azure Virtual Network Manager overview](https://learn.microsoft.com/en-us/azure/virtual-network-manager/overview).

## Architecture

| Component | Configuration |
|---|---|
| Resource group | `rg-avnm-deep-dive-we` |
| Network manager | `avnm-governance-we` |
| Scope | Exact active subscription |
| Configuration access | Connectivity and SecurityAdmin |
| Root IPAM pool | `10.140.0.0/14` |
| West Europe child pool | `10.140.0.0/16` |
| Reserved static CIDR | `10.140.240.0/20` |
| VNets | Core, App1, App2, Edge; 4,096 addresses each (`/20`) |
| Test VMs | Ubuntu 24.04 `Standard_B1s` in Core and Edge |
| Dynamic group | `ng-avnm-mesh-we` |
| Connectivity | Regional mesh, overlap disallowed, no global mesh |
| Security Admin | Inbound TCP 8080 Deny, priority 100 |
| Control traffic | TCP 8443 allowed by NSG |

The scripts pin Network Manager and IPAM operations to the `2025-05-01` API family, Compute to `2025-04-01`, effective Security Admin evidence to `2025-03-01`, Policy to `2025-11-01`, and role assignments to `2022-04-01` where REST evidence is required. CLI commands use the current `virtual-network-manager` extension.

## 1. Preflight the subscription and address space

Open PowerShell 7 and authenticate. Select the subscription by immutable ID, not display name. In this environment two subscriptions can share the same friendly name, which makes name-based selection unsafe.

```powershell
az login
$subscriptionId = az account show --query id -o tsv
az account set --subscription $subscriptionId

$common = @{
  SubscriptionId    = $subscriptionId
  Location          = 'westeurope'
  ResourceGroupName = 'rg-avnm-deep-dive-we'
}

./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage Preflight
```

The preflight checks PowerShell, Azure CLI, the Network Manager extension, provider registration, VNet overlap, the Ubuntu image, and B1s regional availability. It logs the subscription name but deliberately redacts the ID.

![Sanitized PowerShell and Azure CLI preflight for Azure Virtual Network Manager.](screenshots/01-cli-powershell-preflight.png)

## 2. Create the Network Manager scope

Create the exact lab resource group and a subscription-scoped Network Manager with Connectivity and SecurityAdmin access:

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage Foundation
```

Scope controls what the manager can see and configure; the network group controls which resources receive a particular configuration. Those are different boundaries. In production, use management groups when the operating model spans subscriptions, then restrict dynamic membership policies carefully.

![Azure Virtual Network Manager overview showing scope coverage and regional deployment status.](screenshots/02-network-manager-scope-and-access.png)

## 3. Carve out IPAM pools

Run the IPAM stage:

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage Ipam
```

The root pool represents the organization-level address plan. The West Europe child pool is a regional slice. We also allocate `10.140.240.0/20` as a static CIDR named `reserved-platform-services`; no VNet can accidentally consume it.

Azure Virtual Network Manager IPAM can automatically allocate non-overlapping CIDRs, monitor utilization, and hold static reservations for addresses used elsewhere or reserved for future platforms. Microsoft’s [IPAM model](https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-ip-address-management) distinguishes root pools, child pools, resource allocations, and static CIDRs.

![Root and West Europe child IPAM pools in Azure Portal.](screenshots/03-root-and-child-ipam-pools.png)

## 4. Allocate four VNets automatically

The Networks stage requests 4,096 addresses for each VNet from the child pool. Azure chooses four non-overlapping `/20` prefixes. The script then derives the first `/24` in each allocation as a workload subnet.

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage Networks
```

The result in this verified run was:

| VNet | Allocated prefix | Workload subnet |
|---|---:|---:|
| `vnet-avnm-core-we` | `10.140.224.0/20` | `10.140.224.0/24` |
| `vnet-avnm-app1-we` | `10.140.192.0/20` | `10.140.192.0/24` |
| `vnet-avnm-app2-we` | `10.140.208.0/20` | `10.140.208.0/24` |
| `vnet-avnm-edge-we` | `10.140.128.0/20` | `10.140.128.0/24` |

Do not hard-code these values in reusable automation: IPAM is the authority. Read the allocated prefix from the VNet response, then derive subnets from that allocation.

![West Europe IPAM allocation view with four VNet prefixes and an unused static reservation.](screenshots/04-ipam-usage-and-reserved-cidr.png)

![Sanitized CLI validation of four IPAM-allocated VNet prefixes and the reserved block.](screenshots/05-ipam-four-vnet-allocations.png)

## 5. Add two private-admin test VMs

Only Core and Edge need compute. Each VM has a Standard public IP to make outbound package access explicit, but the NSGs contain no inbound SSH rule. Guest configuration uses Azure VM Run Command.

On Edge, the guest script starts simple proof listeners on TCP 8080 and 8443:

```bash
sudo systemctl enable --now avnm-proof-8080.service
sudo systemctl enable --now avnm-proof-8443.service
```

Both subnet NSGs deliberately allow TCP 8080 and 8443 from `10.140.0.0/14`. That local allow is essential: later, when 8080 fails, we can attribute the failure to Security Admin rather than to the NSG.

![Sanitized Azure CLI inventory of Core and Edge VMs and NSGs with no SSH allow.](screenshots/06-core-edge-vms-and-nsgs.png)

## 6. Create a deliberate tag mistake

All VNets receive `Workshop=AVNM-DeepDive`. Core, App1, and App2 receive `NetworkGroup=Mesh`. Edge receives the typo `NetworkGroup=Mseh`.

![Azure Portal tag view showing the intentional Edge value NetworkGroup=Mseh.](screenshots/07-intentional-edge-tag-typo.png)

This is more realistic than manually excluding a VNet: governance at scale often fails because metadata is incomplete, misspelled, or inconsistent.

## 7. Build dynamic network-group membership with Azure Policy

Run the dynamic group stage:

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage DynamicGroup
```

The definition uses Policy mode `Microsoft.Network.Data` and the `addToNetworkGroup` effect. The conditions require all of the following:

```json
{
  "allOf": [
    { "field": "type", "equals": "Microsoft.Network/virtualNetworks" },
    { "field": "tags[Workshop]", "equals": "AVNM-DeepDive" },
    { "field": "tags[NetworkGroup]", "equals": "Mesh" },
    { "field": "id", "like": "/subscriptions/<redacted>/resourceGroups/rg-avnm-deep-dive-we/*" }
  ]
}
```

The exact resource-group condition is a safety boundary: an unrelated VNet with the same tags elsewhere in the subscription cannot join this lab group. Microsoft documents that `addToNetworkGroup` is specific to `Microsoft.Network.Data` policy definitions in the [dynamic network-group effect guidance](https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effect-add-to-network-group).

![Azure Portal policy view showing three dynamic network-group members.](screenshots/08-dynamic-network-data-policy.png)

Policy evaluation is asynchronous. The automation polls Resource Graph for the generated `microsoft.network/networkgroupmemberships` resources instead of sleeping for a fixed period.

![Network group overview showing exactly three members before the tag repair.](screenshots/09-initial-three-member-network-group.png)

## 8. Deploy the regional mesh

Create and deploy a regional mesh connectivity configuration:

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage Mesh
```

The configuration uses one group, disables global mesh, disallows connected-group overlap, leaves high-scale private endpoints disabled, and does not delete unrelated peerings.

![Regional mesh configuration showing one network group, global mesh disabled, and overlap disallowed.](screenshots/10-regional-mesh-configuration.png)

In a mesh, Virtual Network Manager creates a *connected group*. It does not add ordinary VNet peering objects. Microsoft’s [connectivity configuration documentation](https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-connectivity-configuration) says the effective route next-hop type appears as `ConnectedGroup`.

At this moment Core has ConnectedGroup routes for App1 and App2 only. It has no route for Edge, so both probes fail despite the NSG allow.

## 9. Inspect effective ConnectedGroup routes

Use the evidence script rather than reading guest OS routes, because Azure platform routes are authoritative:

```powershell
./scripts/Get-AzureVirtualNetworkManagerEvidence.ps1 @common -Evidence Routing
```

After the repair, the Core NIC shows three active routes—one for each other VNet.

![Core NIC effective route table showing three active ConnectedGroup routes.](screenshots/11-connectedgroup-effective-routes.png)

## 10. Prove the expected failure

Before repairing Edge, the automation invokes a shell probe from Core through VM Run Command. The Edge private address is never printed.

![Expected Core-to-Edge failures on TCP 8080 and 8443 while Edge is excluded.](screenshots/12-expected-core-to-edge-failure.png)

Troubleshooting order matters:

1. Check the resource tags.
2. Query dynamic membership with Resource Graph.
3. Confirm that the regional connectivity configuration is deployed.
4. Inspect the NIC effective routes for `ConnectedGroup`.
5. Only then inspect NSGs and guest listeners.

That sequence prevents wasting time on NSGs when the route does not exist.

## 11. Repair membership without a peering

Correct the tag and poll for membership plus data-plane convergence:

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage RepairMembership
```

The group grows from three to four members. Azure redeploys the same desired mesh configuration, and Core receives the Edge route. No manual membership and no peering object are added.

![Corrected Edge NetworkGroup tag and the network group showing four members.](screenshots/13-corrected-tag-and-four-member-group.png)

Both ports now succeed:

![Successful Core-to-Edge TCP 8080 and 8443 probes after repairing dynamic membership.](screenshots/14-repaired-mesh-connectivity.png)

## 12. Deploy Security Admin above the NSGs

Now create the central security configuration:

```powershell
./scripts/Start-AzureVirtualNetworkManagerWorkshop.ps1 @common -Stage SecurityAdmin
```

The rule collection targets the mesh network group. The custom rule is:

| Field | Value |
|---|---|
| Name | `Deny-Unmanaged-App-Port` |
| Priority | `100` |
| Direction | Inbound |
| Protocol | TCP |
| Source | `10.140.0.0/14` |
| Destination | `10.140.0.0/14` |
| Destination port | `8080` |
| Action | Deny |

Security Admin rules are evaluated before NSG rules. A Security Admin `Deny` or `Always Allow` terminates evaluation; an ordinary Security Admin `Allow` continues to NSG evaluation. This distinction is documented in Microsoft’s [Security Admin rule evaluation guidance](https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-security-admins).

![Local NSG allowing TCP 8080 and 8443 above a deployed Security Admin Deny for 8080.](screenshots/15-nsg-allow-and-security-admin-deny.png)

## 13. Prove central precedence with REST and live traffic

The evidence script calls the effective Security Admin rules API at the VNet and combines it with the defined rule metadata, returning only safe fields. It then runs the same two probes.

```powershell
./scripts/Get-AzureVirtualNetworkManagerEvidence.ps1 @common -Evidence Security
./scripts/Get-AzureVirtualNetworkManagerEvidence.ps1 @common -Evidence Connectivity
```

Expected final result:

```text
Rule                       DefinedPriority EffectiveAccess Direction Port Protocol
Deny-Unmanaged-App-Port    100             Deny            Inbound   8080 Tcp

PORT=8080 RESULT=BLOCKED
PORT=8443 RESULT=OPEN
```

![Effective Security Admin REST evidence with 8080 blocked and the 8443 control remaining open.](screenshots/16-security-admin-rest-and-connectivity.png)

If both ports fail, check the mesh and guest listener. If both ports work, check the Security Admin regional deployment rather than the NSG. If 8080 fails and 8443 works, the lab has produced the intended proof.

## 14. Production design guidance

For a production rollout:

- scope network managers with management groups and separate platform ownership from application ownership;
- use tag standards enforced by policy, not informal naming conventions;
- deploy connectivity and Security Admin configurations progressively by region;
- reserve address ranges for future platforms and on-premises networks before allocations begin;
- delegate IPAM Pool User at the narrowest practical pool;
- treat `Deny` and `Always Allow` as centrally governed controls with change review;
- monitor dynamic group membership drift in Resource Graph;
- validate effective routes and rules after every configuration deployment;
- avoid enabling “delete existing peerings” until the current peering estate is inventoried; and
- use workload-private egress such as Azure Firewall or NAT Gateway rather than public IPs on production VMs.

Connected groups solve connectivity orchestration, not application authorization. NSGs, Security Admin, guest firewalls, identity, and application controls still form separate layers.

## 15. Cleanup in the required dependency order

Network Manager configurations must be undeployed before their definitions are deleted. The cleanup script first commits `None` for SecurityAdmin and Connectivity in West Europe, polls deployment state, then removes the policy, rules, configurations, group, VMs, VNets, IPAM reservation, pools, Network Manager, and exact resource group.

```powershell
./scripts/Remove-AzureVirtualNetworkManagerWorkshop.ps1 @common -Force
./scripts/Get-AzureVirtualNetworkManagerEvidence.ps1 @common -Evidence Cleanup
```

Microsoft’s [Network Manager component-removal checklist](https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-remove-components-checklist) explains why configuration deployments must be removed before deleting the configuration resources.

![Sanitized cleanup verification showing the workshop resource group and subscription policy artifacts are gone.](screenshots/17-cleanup-verification.png)

## Final result

This lab demonstrated all four control points that make Virtual Network Manager valuable at scale:

1. **IPAM** allocated non-overlapping address spaces from a governed pool.
2. **Dynamic network groups** turned tags into policy-driven membership.
3. **Connectivity configurations** created a regional mesh and `ConnectedGroup` routes without manual peerings.
4. **Security Admin** enforced a central deny before the workload NSG.

The strongest operational lesson is that the effective configuration is the truth. Tags express intent, Policy translates intent into membership, regional deployment applies connectivity or security, and effective routes/rules prove what the data plane actually received.

## Download the lab bundle

The downloadable ZIP contains the workshop Markdown, public PowerShell scripts, guest setup script, cover, sanitized screenshots, publishing brief, and validation transcript. It excludes Azure CLI profiles, tokens, subscription or tenant IDs, principal IDs, public IP values, raw ARM IDs, raw Portal captures, temporary REST responses, and generated SSH keys.

