Skip to main content

Challenge 07: Azure Virtual Network Manager

Estimated time and cost

90-120 minutes | ~$0 (AVNM is free; peerings created incur standard VNet peering costs) | Exam weight: 10-15%

Scenario

Contoso's network team manages over 50 VNets across multiple subscriptions and regions. Manually creating peerings, route tables, and security rules for every new VNet is unsustainable. They decide to implement Azure Virtual Network Manager (AVNM) to centrally manage connectivity (hub-spoke and mesh topologies) and enforce security admin rules across all VNets from a single control plane.

Learning objectives

After completing this challenge you will be able to:

  • Create an Azure Virtual Network Manager instance with defined scope and access
  • Organize VNets into network groups using static and dynamic membership
  • Build connectivity configurations for hub-spoke and mesh topologies
  • Author security admin rules that are enforced before NSGs
  • Deploy (commit) configurations to target regions
  • Verify effective connectivity and security admin rules on VNets

Prerequisites

  • An Azure subscription with Contributor access (Owner recommended for policy assignments)
  • Azure CLI installed and authenticated (az login)
  • The virtual-network-manager CLI extension (az extension add -n virtual-network-manager)
  • A resource group for this lab (or permission to create one)

Task 1: Create a Virtual Network Manager instance

Create the AVNM instance with appropriate scope and access types. The scope defines which subscriptions or management groups AVNM can manage, and the access types determine what configurations you can create.

Step 1: Create the resource group

az group create \
--name rg-avnm-lab \
--location eastus2

Step 2: Create the Virtual Network Manager instance

az network manager create \
--name avnm-contoso \
--resource-group rg-avnm-lab \
--location eastus2 \
--scope-accesses "Connectivity" "SecurityAdmin" \
--network-manager-scopes subscriptions="/subscriptions/<subscriptionID>"

Replace <subscriptionID> with your subscription ID.

Key parameters:

ParameterPurpose
--scope-accessesDefines configuration types: Connectivity, SecurityAdmin, or both
--network-manager-scopesSets the management boundary (subscription or management group)
Exam note

The scope determines which VNets AVNM can manage. You can scope to a management group to cover multiple subscriptions. A single AVNM instance can have both Connectivity and SecurityAdmin access enabled simultaneously.

Step 3: Verify the instance was created

az network manager show \
--name avnm-contoso \
--resource-group rg-avnm-lab \
--output table

Task 2: Create VNets and define network groups

Network groups are logical containers for VNets. Members can be added statically (manual) or dynamically (using Azure Policy conditions).

Step 1: Create the hub VNet

az network vnet create \
--name vnet-hub \
--resource-group rg-avnm-lab \
--location eastus2 \
--address-prefixes 10.0.0.0/16 \
--subnet-name default \
--subnet-prefixes 10.0.0.0/24 \
--tags Environment=Hub

Step 2: Create spoke VNets

az network vnet create \
--name vnet-spoke-01 \
--resource-group rg-avnm-lab \
--location eastus2 \
--address-prefixes 10.1.0.0/16 \
--subnet-name default \
--subnet-prefixes 10.1.0.0/24 \
--tags Environment=Prod Department=Engineering

az network vnet create \
--name vnet-spoke-02 \
--resource-group rg-avnm-lab \
--location eastus2 \
--address-prefixes 10.2.0.0/16 \
--subnet-name default \
--subnet-prefixes 10.2.0.0/24 \
--tags Environment=Prod Department=Finance

az network vnet create \
--name vnet-spoke-03 \
--resource-group rg-avnm-lab \
--location eastus2 \
--address-prefixes 10.3.0.0/16 \
--subnet-name default \
--subnet-prefixes 10.3.0.0/24 \
--tags Environment=Dev Department=Engineering

Step 3: Create a network group for spoke VNets

az network manager group create \
--name ng-spokes-prod \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--description "Production spoke virtual networks"

Step 4: Add static members to the network group

az network manager group static-member create \
--name vnet-spoke-01 \
--network-group ng-spokes-prod \
--network-manager avnm-contoso \
--resource-group rg-avnm-lab \
--resource-id "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/virtualNetworks/vnet-spoke-01"

az network manager group static-member create \
--name vnet-spoke-02 \
--network-group ng-spokes-prod \
--network-manager avnm-contoso \
--resource-group rg-avnm-lab \
--resource-id "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/virtualNetworks/vnet-spoke-02"
![Challenge 07 - Network Topology](/img/az-700/challenge-07-topology.svg)


Assign the policy to the subscription scope:

```bash
az policy assignment create \
--name "avnm-prod-vnets-assignment" \
--policy "avnm-prod-vnets" \
--scope "/subscriptions/<subscriptionID>"
Exam note

Dynamic membership uses the Microsoft.Network.Data policy mode and the addToNetworkGroup effect. Policies must be defined at or above the scope where they apply. Static and dynamic membership can coexist in the same network group.


Task 3: Create a hub-spoke connectivity configuration

A connectivity configuration defines how VNets in a network group connect. For hub-spoke, AVNM automatically creates peerings between the hub and each spoke.

Step 1: Create the hub-spoke connectivity configuration

az network manager connect-config create \
--configuration-name "cc-hub-spoke-prod" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--connectivity-topology "HubAndSpoke" \
--hub '[{"resourceId": "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/virtualNetworks/vnet-hub", "resourceType": "Microsoft.Network/virtualNetworks"}]' \
--applies-to-groups '[{"networkGroupId": "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/networkManagers/avnm-contoso/networkGroups/ng-spokes-prod", "groupConnectivity": "DirectlyConnected", "useHubGateway": "False", "isGlobal": "False"}]' \
--delete-existing-peering True \
--description "Hub-spoke topology for production spokes with direct spoke connectivity"

Key parameters explained:

ParameterPurpose
--connectivity-topologyHubAndSpoke or Mesh
--hubSpecifies the hub VNet (JSON format)
--applies-to-groupsNetwork groups that act as spokes
groupConnectivity: DirectlyConnectedEnables spoke-to-spoke communication
--delete-existing-peeringRemoves manually created peerings that conflict

Step 2: Verify the configuration

az network manager connect-config show \
--configuration-name "cc-hub-spoke-prod" \
--name avnm-contoso \
--resource-group rg-avnm-lab
Exam note

Setting groupConnectivity to DirectlyConnected allows spoke VNets to communicate directly without routing through the hub. Without this, spokes can only reach the hub. The useHubGateway option allows spokes to use a VPN/ExpressRoute gateway deployed in the hub VNet.


Task 4: Create a security admin configuration

Security admin rules are evaluated before NSGs and cannot be overridden by network administrators at the VNet level. This makes them ideal for organization-wide security baselines.

Step 1: Create the security admin configuration

az network manager security-admin-config create \
--configuration-name "sac-baseline" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--description "Baseline security rules for all production VNets"

Step 2: Create a rule collection

A rule collection groups related rules and targets specific network groups:

az network manager security-admin-config rule-collection create \
--configuration-name "sac-baseline" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--rule-collection-name "rc-deny-dangerous-inbound" \
--applies-to-groups '[{"networkGroupId": "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/networkManagers/avnm-contoso/networkGroups/ng-spokes-prod"}]' \
--description "Block dangerous inbound protocols from the internet"

Step 3: Create a rule to deny inbound SSH from the internet

az network manager security-admin-config rule-collection rule create \
--configuration-name "sac-baseline" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--rule-collection-name "rc-deny-dangerous-inbound" \
--rule-name "deny-ssh-from-internet" \
--kind "Custom" \
--protocol "Tcp" \
--access "Deny" \
--priority 100 \
--direction "Inbound" \
--sources address-prefix="*" address-prefix-type="IPPrefix" \
--destinations address-prefix="*" address-prefix-type="IPPrefix" \
--dest-port-ranges 22 \
--description "Block SSH from all external sources"

Step 4: Create a rule to deny inbound RDP from the internet

az network manager security-admin-config rule-collection rule create \
--configuration-name "sac-baseline" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--rule-collection-name "rc-deny-dangerous-inbound" \
--rule-name "deny-rdp-from-internet" \
--kind "Custom" \
--protocol "Tcp" \
--access "Deny" \
--priority 110 \
--direction "Inbound" \
--sources address-prefix="*" address-prefix-type="IPPrefix" \
--destinations address-prefix="*" address-prefix-type="IPPrefix" \
--dest-port-ranges 3389 \
--description "Block RDP from all external sources"

Step 5: Create an Always Allow rule for internal monitoring

az network manager security-admin-config rule-collection rule create \
--configuration-name "sac-baseline" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--rule-collection-name "rc-deny-dangerous-inbound" \
--rule-name "always-allow-monitoring" \
--kind "Custom" \
--protocol "Tcp" \
--access "AlwaysAllow" \
--priority 50 \
--direction "Inbound" \
--sources address-prefix="10.0.0.0/8" address-prefix-type="IPPrefix" \
--destinations address-prefix="*" address-prefix-type="IPPrefix" \
--dest-port-ranges 443 \
--description "Ensure monitoring from internal network is never blocked"
Exam note

Security admin rule actions and their evaluation order:

  1. Always Allow -- Traffic is permitted regardless of lower-priority admin rules or NSGs
  2. Allow -- Traffic is allowed at the admin level but can still be blocked by NSGs
  3. Deny -- Traffic is blocked regardless of what NSGs allow

Admin rules are evaluated before NSGs. A Deny admin rule overrides any NSG Allow rule. This is how organizations enforce non-negotiable security policies.


Task 5: Deploy (commit) configurations to target regions

Configurations have no effect until they are deployed. Deployment commits the configuration to specific Azure regions.

Step 1: Commit the connectivity configuration

az network manager post-commit \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--commit-type "Connectivity" \
--configuration-ids "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/networkManagers/avnm-contoso/connectivityConfigurations/cc-hub-spoke-prod" \
--target-locations "eastus2"

Step 2: Commit the security admin configuration

az network manager post-commit \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--commit-type "SecurityAdmin" \
--configuration-ids "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/networkManagers/avnm-contoso/securityAdminConfigurations/sac-baseline" \
--target-locations "eastus2"

Key parameters:

ParameterPurpose
--commit-typeEither Connectivity or SecurityAdmin
--configuration-idsFull resource ID of the configuration to deploy
--target-locationsAzure regions where the configuration takes effect
Important

The command is az network manager post-commit, not az network manager commit. Deployment can take several minutes to propagate. You can deploy multiple configurations of the same type in one commit by providing multiple configuration IDs.


Task 6: Verify effective connectivity and security rules

After deployment, verify that the configurations have been applied to the target VNets.

Step 1: Check effective connectivity configuration on a spoke VNet

az network manager list-effective-connectivity-config \
--resource-group rg-avnm-lab \
--virtual-network-name vnet-spoke-01

This should return the hub-spoke topology configuration showing the hub VNet and group connectivity settings.

Step 2: Check effective security admin rules on a spoke VNet

az network manager list-effective-security-admin-rules \
--resource-group rg-avnm-lab \
--virtual-network-name vnet-spoke-01

This shows all security admin rules applied to the VNet, including the deny-SSH and deny-RDP rules.

Step 3: Verify peerings were created automatically

az network vnet peering list \
--resource-group rg-avnm-lab \
--vnet-name vnet-spoke-01 \
--output table

You should see an AVNM-managed peering to vnet-hub. These peerings are marked as managed and cannot be manually deleted while the configuration is deployed.

Step 4: Verify peerings on the hub side

az network vnet peering list \
--resource-group rg-avnm-lab \
--vnet-name vnet-hub \
--output table

The hub should have peerings to all spoke VNets in the network group.

Exam note

AVNM-managed peerings display a managedBy property. You cannot delete or modify them directly; you must remove the VNet from the network group or delete the connectivity configuration. If you need to remove a deployment, commit with an empty configuration ID list for that region.


Break & fix

Scenario 1: Configuration created but not deployed

Symptom: You created a connectivity configuration and added VNets to the network group, but no peerings appear and VNets cannot communicate.

Root cause: Configurations must be explicitly deployed (committed) to target regions. Creating a configuration only defines the desired state; it does not apply it.

Fix: Deploy the configuration using az network manager post-commit:

az network manager post-commit \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--commit-type "Connectivity" \
--configuration-ids "/subscriptions/<subscriptionID>/resourceGroups/rg-avnm-lab/providers/Microsoft.Network/networkManagers/avnm-contoso/connectivityConfigurations/cc-hub-spoke-prod" \
--target-locations "eastus2"

Scenario 2: Security admin rule blocking legitimate traffic

Symptom: An NSG rule explicitly allows HTTPS inbound, but traffic is still being blocked. The application was working before AVNM was deployed.

Root cause: A security admin Deny rule for the same port takes precedence over NSG Allow rules. Admin rules are evaluated before NSGs.

Diagnosis:

az network manager list-effective-security-admin-rules \
--resource-group rg-avnm-lab \
--virtual-network-name vnet-spoke-01

Fix: Either change the admin rule action from Deny to Allow (letting NSGs make the final decision), or use AlwaysAllow if the traffic must never be blocked:

az network manager security-admin-config rule-collection rule update \
--configuration-name "sac-baseline" \
--network-manager-name avnm-contoso \
--resource-group rg-avnm-lab \
--rule-collection-name "rc-deny-dangerous-inbound" \
--rule-name "deny-ssh-from-internet" \
--access "Allow"

Then redeploy the security admin configuration.

Scenario 3: Dynamic membership condition too broad

Symptom: Dev/test VNets are receiving production security rules and connectivity configurations that should only apply to production VNets.

Root cause: The Azure Policy condition for dynamic membership is too broad. For example, matching on VNet name prefix vnet- includes both prod and dev VNets.

Diagnosis: Check which VNets are in the network group:

az network manager group static-member list \
--network-group ng-spokes-prod \
--network-manager avnm-contoso \
--resource-group rg-avnm-lab

Fix: Tighten the policy condition to use a more specific tag value or combine multiple conditions:

{
"if": {
"allOf": [
{ "field": "type", "equals": "Microsoft.Network/virtualNetworks" },
{ "field": "tags['Environment']", "equals": "Prod" },
{ "field": "tags['ManagedByAVNM']", "equals": "true" }
]
},
"then": {
"effect": "addToNetworkGroup",
"details": {
"networkGroupId": "<networkGroupResourceId>"
}
}
}

Clean up resources

Remove the resource group and all resources within it:

az group delete \
--name rg-avnm-lab \
--yes \
--no-wait

If you created policy definitions and assignments, remove them as well:

az policy assignment delete --name "avnm-prod-vnets-assignment"
az policy definition delete --name "avnm-prod-vnets"

Key concepts summary

ConceptDescription
Network Manager scopeSubscription or management group boundary for management
Network groupLogical collection of VNets (static or dynamic membership)
Connectivity configurationDefines topology (Hub-spoke or Mesh)
Security admin configurationRules enforced before NSGs across the organization
Deployment (commit)Applies configurations to target regions
Always AllowCannot be overridden by any other admin rule or NSG
Allow (admin rule)Permits at admin level; NSGs can still block
Deny (admin rule)Blocks traffic regardless of NSG rules

Knowledge check

1. You create a security admin rule with action 'Deny' for port 22 inbound. A VNet in the network group has an NSG rule that allows port 22 inbound. What happens to SSH traffic?

2. You created a connectivity configuration for hub-spoke topology and added VNets to the network group, but no peerings appear on the VNets. What is the most likely cause?

3. Which Azure Policy mode must be used when creating a policy for dynamic AVNM network group membership?

4. In a hub-spoke connectivity configuration, what does setting 'groupConnectivity' to 'DirectlyConnected' achieve?

5. What is the correct CLI command to deploy an AVNM configuration to a target region?

6. A security admin rule has the action set to 'AlwaysAllow' for HTTPS inbound from 10.0.0.0/8. Another admin rule with higher priority number denies all inbound traffic on port 443. What happens to HTTPS traffic from 10.0.0.0/8?