Introduction:
It’s no secret that managing your virtual networks in azure on a global scale has been a challenge for many and even more so for those that have not adopted an IaC approach. Having to manually make a change to several networks in Azure can be a daunting task depending on your environment. But Azure virtual network manager might just be able to help you with some of these configurations on a more controlled global scale.
If you’re already familiar with Azure virtual network manager and would like to only check out the newest features, check out the features still in public preview by clicking here.
What is Azure Virtual Network Manager:
Azure virtual network manager is a service for centralizing management of our vnet:s in Azure and deploy settings/configuration on a large scale, like standard user-defined routes and security rules. We will have a closer look and talk about the different capabilities and how they can be utilized!
"Deployment" and features:
When we first want to utilize Azure network manager we first need to create/deploy it as a resource, you can have several network managers in different subscriptions if needed, but unless you have a specific need to do so one azure network manager can scope your entire Azure tenant. Even if we select a broad scope for the manager, we will still be able to group networks inside so don't worry!
I'm choosing the "Azure" Management group as my scope.
One of the first options you will be presented with when creating your own virtual network manager is what kind of feature you wish to utilize for this specific manager (here is another scenario where you might want to separate for specific role-based needs). Generally, if you have the same people managing these settings just use one manager to keep it simple!
Three main feature to choose from.
Connectivity Configuration:
This configuration is to manage and deploy the network topology, yes we can actually choose and deploy an topology with network manager. We can choose from "Hub and spoke" and "Mesh" topology. What this configuration provides is automatic peering for our networks that we deploy the configuration to.
In the example below, I choose "Hub and spoke" I then select my hub network and add one or more network groups that I would like as my spokes. With this configuration all the vnet:s in my "sedc-corp-vnmgr" group will be peered with my hub network, I can also add gateway and mesh on top of this if I would like. What's nice here is that if I add more vnet:s (you can groups based on subnet also) to my network group in the future they will automatically get the peering to the hub network with the correct settings! You can of course also choose to delete existing peering's to ensure that its only our specified configuration for how the networks are peered applied.
Hub and spoke connection configuration.
Before we hop on to the next configuration I will explain more about the network groups. Network groups are how we group networks within virtual network manager, we can then use these network groups for deployment of our configuration. What's nice about network groups is we can add members dynamically with policies (you can always add manually if you need/want in the same group as dynamic). An example is that I could set up a network group that adds all the networks in a specific management group (Corp) and deploy a hub and spoke in my "corporate" network. I could also make the selection on tags saying that all vnet:s with the tag "Network" and value "Corp" will join a specific network group. This gives us flexibility in how we can deploy these configurations to our networks.
Security Admin Configuration:
Network security groups on 100+ Vnet:s? Need to deploy some rules to all of them? Well don't worry, virtual network manager kind of got you covered here too! In security admin configuration we can deploy allow or deny rule collections to our vnet:s in Azure, these work like nsg:s, but will not deploy into the nsg resources, instead we will find them directly on our vnet:s under "network manager".
RDP rule deployed to my VNET with destination my sedc-corp-vnmgr group.
Just like with nsg we can use service tags when setting up our rules, but we can also use our own configured network groups as source and destination for some added flexibility. I could for example say that I want to be able to use RDP to all my “Corp” spokes regardless, I could then make a network rule allowing inbound RDP (3389) from a management/admin network and chose the destination as my "sedc-corp-vnmgr" group to ensure that all my resources in that group can be accessed with RDP (even those added later). This is of course just an easy example to understand, you do not necessarily want to deploy something as broad as that in your environment.
Important to know is that these rules work together with any nsg:s deployed to your resources, so they will be taken into consideration when checking what rules apply for your resources. There is however a specific rule type that overrides your nsg:s and that is the "Always Allow" rule type, if you configure your security admin rule to "Always Allow" it will take priority over all other rules configured in nsg:s and security admin, even those of LOWER priority!
Example of a rule configuration.
Preview features:
These features below are still all in public preview.
User Defined Routing Configuration:
This is not much different from creating a udr yourself and associate it with subnets, but network manager will create and deploy several managed udr to a network group and associate them automatically with all the subnets in the network group. This makes things like ensuring our next hop is always the central firewall a lot easier, and remember new network added into the group will get this deployed and fixed for them automatically.
UDR for all my "Corp" spokes with next hop to my firewall.
IP Address Management (IPAM):
Now we can also use an Azure native IPAM solution that can even allocate the vnet:s and calculate the ranges used and still available based on the Vnet:s associated with the address pool.
In Azure IPAM we create an address pool as a container for our address spaces, you can have several address pools for managing purposes. In this example we create 10.0.0.0/16 for the region West Europe to use, we can then add all our vnet:s for West Europe to this address pool. Now we can see what addresses are used and what we have left to utilize. You can also use this to add static CIDR:s for other locations outside of Azure if you have a hybrid/multi-cloud environment. We can also check inside the pool what resources are located within our address pool, which is kind of neat :)
My West Europe address pool.
Other than this if you worked with IPAM:s before it's not revolutionary but a nice addition to the Azure network kit and I'm all for it!
Virtual Network Verifier:
Virtual network verifier, which unfortunately I have not been able to use in any real-life scenarios yet, just light weight lab-environment. I like the concept of a tool that helps us analyze and verify network connectivity step by step in Azure, it helps that it also visualizes it in a very nice way and with interactable steps to get more detail about network traffic.
In the picture below I have run a test analysis and specified the source, destination and what protocol I would like to test. The network verifier then runs an analysis on the configuration and reachability between my source and destination and can take private endpoint, nsg, asg and so on into consideration.
It does a clear mapping of my resources as seen below and we can see that I can't reach my destination it stops after the nsg is reached.
Visualization of the network flow.
If I then click into the connection part after the nsg I can see what actually happens, and as suspected some evil IT administrator have added a block rule for RDP from all sources (this VNET is not included in the "Always Allow" configuration from above). This is of course just a simple demonstration, but it can recognize things like VM state also (have you tried turning your VM on when trying to reach it ;) ).
Each step in the analysis have detailed information.
Unfortunately I have not have the opportunity yet to use it for more advance trouble shooting and when doing labs I ran into some issues when I had security configuration deployed to my vnet:s (error message not supported) which is a shame, but it is still in preview and as a concept I do love having more tools at our disposal for analyzing our network traffic/issues :)
JSON format:
If we would like to get more information about the entire flow, we can just view it in json format instead of visualization (it always gives you both, you just swap between them instantly.) The code below is from both a successful and an unsuccessful attempt, just hit the arrow to expand it!
JSON for SUCCESSFUL attempt
{
"resultOutcome": "AllPacketsReached",
"reachedTrace": [
{
"name": "firewall",
"resourceId": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Compute/virtualMachines/firewall",
"resourceType": "Microsoft.Compute/virtualMachines",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet has source IP address matching one of the network interface IPs.",
"explanationCode": "MATCHED_NETWORK_INTERFACE_IP_SOURCE",
"matchedNetworkInterfaceAddress": {
"networkInterface": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Network/networkInterfaces/firewall880",
"ipAddress": "10.0.1.4"
}
}
},
{
"name": "firewall880",
"resourceId": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Network/networkInterfaces/firewall880",
"resourceType": "Microsoft.Network/networkInterfaces",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet has source IP address matching one of the network interface IPs.",
"explanationCode": "MATCHED_NETWORK_INTERFACE_IP_SOURCE",
"matchedNetworkInterfaceAddress": {
"networkInterface": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Network/networkInterfaces/firewall880",
"ipAddress": "10.0.1.4"
}
}
},
{
"name": "MyFirewall",
"resourceId": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-centralnetwork-sedc/providers/Microsoft.Network/virtualNetworks/vnet-hub-sedc/subnets/MyFirewall",
"resourceType": "Microsoft.Network/virtualNetworks/subnets",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet matched a system route.",
"explanationCode": "MATCHED_SYSTEM_ROUTE",
"matchedSubnetRoute": {
"source": "Default",
"addressPrefix": "10.0.64.0/24",
"nextHopIpAddress": [],
"nextHopType": "VNetPeering"
}
}
},
{
"name": "vnet-alz4-prod",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-network-azl4/providers/Microsoft.Network/virtualNetworks/vnet-alz4-prod",
"resourceType": "Microsoft.Network/virtualNetworks",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet destination IP is destined to subnet prefix.",
"explanationCode": "MATCHED_SUBNET_PREFIX",
"matchedSubnetPrefix": {
"subnet": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-network-azl4/providers/Microsoft.Network/virtualNetworks/vnet-alz4-prod/subnets/default",
"subnetPrefix": "10.0.64.0/24"
}
}
},
{
"name": "default",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-network-azl4/providers/Microsoft.Network/virtualNetworks/vnet-alz4-prod/subnets/default",
"resourceType": "Microsoft.Network/virtualNetworks/subnets",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet forwarded to the next hop.",
"explanationCode": "FORWARDED"
}
},
{
"name": "testvm-vnmgr-nsg",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-servers/providers/Microsoft.Network/networkSecurityGroups/testvm-vnmgr-nsg",
"resourceType": "Microsoft.Network/networkSecurityGroups",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet matched a network security rule.",
"explanationCode": "MATCHED_NETWORK_SECURITY_RULE",
"matchedSecurityRule": {
"name": "defaultSecurityRules/AllowVnetInBound",
"action": "allow",
"destinationAddress": "VirtualNetwork",
"sourceAddress": "VirtualNetwork",
"destinationPort": "0-65535",
"sourcePort": "0-65535",
"protocol": "All"
}
}
},
{
"name": "testvm-vnmgr920",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-servers/providers/Microsoft.Network/networkInterfaces/testvm-vnmgr920",
"resourceType": "Microsoft.Network/networkInterfaces",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet has destination IP address matching one of the network interface IPs.",
"explanationCode": "MATCHED_NETWORK_INTERFACE_IP_DEST",
"matchedNetworkInterfaceAddress": {
"networkInterface": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-servers/providers/Microsoft.Network/networkInterfaces/testvm-vnmgr920",
"ipAddress": "10.0.64.4"
}
}
},
{
"name": "testvm-vnmgr",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-servers/providers/Microsoft.Compute/virtualMachines/testvm-vnmgr",
"resourceType": "Microsoft.Compute/virtualMachines",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet delivered to the resource.",
"explanationCode": "DELIVERED"
}
}
]
}
JSON for UNSUCCESSFUL attempt
{
"resultOutcome": "NoPacketsReached",
"unreachedTrace": [
{
"name": "firewall",
"resourceId": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Compute/virtualMachines/firewall",
"resourceType": "Microsoft.Compute/virtualMachines",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet has source IP address matching one of the network interface IPs.",
"explanationCode": "MATCHED_NETWORK_INTERFACE_IP_SOURCE",
"matchedNetworkInterfaceAddress": {
"networkInterface": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Network/networkInterfaces/firewall880",
"ipAddress": "10.0.1.4"
}
}
},
{
"name": "firewall880",
"resourceId": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Network/networkInterfaces/firewall880",
"resourceType": "Microsoft.Network/networkInterfaces",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet has source IP address matching one of the network interface IPs.",
"explanationCode": "MATCHED_NETWORK_INTERFACE_IP_SOURCE",
"matchedNetworkInterfaceAddress": {
"networkInterface": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-firewall-sedc/providers/Microsoft.Network/networkInterfaces/firewall880",
"ipAddress": "10.0.1.4"
}
}
},
{
"name": "MyFirewall",
"resourceId": "/subscriptions/a5a25a9d-907b-4d46-a14a-33bf5e72fef1/resourceGroups/rg-centralnetwork-sedc/providers/Microsoft.Network/virtualNetworks/vnet-hub-sedc/subnets/MyFirewall",
"resourceType": "Microsoft.Network/virtualNetworks/subnets",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet matched a system route.",
"explanationCode": "MATCHED_SYSTEM_ROUTE",
"matchedSubnetRoute": {
"source": "Default",
"addressPrefix": "10.0.64.0/24",
"nextHopIpAddress": [],
"nextHopType": "VNetPeering"
}
}
},
{
"name": "vnet-alz4-prod",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-network-azl4/providers/Microsoft.Network/virtualNetworks/vnet-alz4-prod",
"resourceType": "Microsoft.Network/virtualNetworks",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet destination IP is destined to subnet prefix.",
"explanationCode": "MATCHED_SUBNET_PREFIX",
"matchedSubnetPrefix": {
"subnet": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-network-azl4/providers/Microsoft.Network/virtualNetworks/vnet-alz4-prod/subnets/default",
"subnetPrefix": "10.0.64.0/24"
}
}
},
{
"name": "default",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-network-azl4/providers/Microsoft.Network/virtualNetworks/vnet-alz4-prod/subnets/default",
"resourceType": "Microsoft.Network/virtualNetworks/subnets",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet forwarded to the next hop.",
"explanationCode": "FORWARDED"
}
},
{
"name": "testvm-vnmgr-nsg",
"resourceId": "/subscriptions/3aacbf4d-6365-4755-93da-2c4f22d92a44/resourceGroups/rg-servers/providers/Microsoft.Network/networkSecurityGroups/testvm-vnmgr-nsg",
"resourceType": "Microsoft.Network/networkSecurityGroups",
"packet": {
"destinationAddress": "10.0.64.4",
"destinationPort": "3389",
"sourceAddress": "10.0.1.4",
"sourcePort": "0",
"protocol": "TCP"
},
"explanation": {
"description": "Packet matched a network security rule.",
"explanationCode": "MATCHED_NETWORK_SECURITY_RULE",
"matchedSecurityRule": {
"name": "securityRules/Deny-RDP",
"action": "deny",
"destinationAddress": "0.0.0.0/0,0.0.0.0/0",
"sourceAddress": "0.0.0.0/0,0.0.0.0/0",
"destinationPort": "3389-3389",
"sourcePort": "0-65535",
"protocol": "Tcp"
}
}
}
]
}
Summary:
Overall, I am very happy that Microsoft focuses on giving us more and more centralized network tools in Azure and I think virtual network manager is a very nice and strong edition to our kit! Some downside is still the strange pricing models, I don't like things being priced per subscription/instance since the entire design for Azure landing zone architecture is to segment into subscription for security, management and cost reasons, it then feels kind of bad when Microsoft says, "hey you pay for the amount of subscriptions you manage with this product". Other than the pricing and some support missing in network verifier, virtual network manager is a strong service that provides a lot of the things we would like to utilize for centralized network managing in Azure and I'm looking forward to seeing it evolve even more!
Comments