In this mini project, I implemented Azure VNet peering using Terraform, but instead of applying everything at once, I deliberately broke the setup into small, testable steps.
This approach makes it much easier to understand what’s happening, catch mistakes early, and build real confidence with Terraform and Azure networking.
Below is the exact flow I followed — and you can follow the same steps as a beginner.
Table of Contents
- Step 1: Create the Resource Group, Virtual Networks, and Subnets
- Step 2: Create VM1 in Subnet 1 (via a NIC)
- Step 3: Create VM2 in Subnet 2
- Step 4: Test Connectivity Before Peering (Expected to Fail)
- Step 5: Add VNet Peering (Both Directions)
- Step 6: Test Connectivity After Peering (Expected to Work)
- Key Takeaways for Beginners
- Why This Step-by-Step Approach Matters
Step 1: Create the Resource Group, Virtual Networks, and Subnets
We start by creating the network foundation:
- One resource group
- Two separate virtual networks
- One subnet inside each virtual network
At this stage, there is no connectivity between the networks.
What we created
vnet1→ address space10.0.0.0/16vnet2→ address space10.1.0.0/16- One
/24subnet in each VNet
resource "azurerm_resource_group" "rg" {
name = "rgminipro76876"
location = "Central US"
}
resource "azurerm_virtual_network" "vnet1" {
name = "vnet1minipro8768"
location = azurerm_resource_group.rg.location
address_space = ["10.0.0.0/16"]
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "sn1" {
name = "subnet1minipro878"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet1.name
address_prefixes = ["10.0.0.0/24"]
}
resource "azurerm_virtual_network" "vnet2" {
name = "vnet2minipro8768"
location = azurerm_resource_group.rg.location
address_space = ["10.1.0.0/16"]
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "sn2" {
name = "subnet2minipro878"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet2.name
address_prefixes = ["10.1.0.0/24"]
}
How to verify
- Run
terraform apply - Open Azure Portal
- Confirm:
- Both VNets exist
- Each VNet has its own subnet
- Address spaces do not overlap
At this point, nothing can talk to anything else yet — and that’s expected.
Step 2: Create VM1 in Subnet 1 (via a NIC)
In Azure, VMs don’t live directly inside subnets.
Instead, a Network Interface (NIC) is placed inside a subnet, and the VM attaches to that NIC.
Here, we:
- Create a NIC attached to
subnet1 - Create a VM that uses that NIC
VM1 and NIC1
resource "azurerm_network_interface" "nic1" {
name = "nic1minipro8789"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "ipconfignic1minipro989"
subnet_id = azurerm_subnet.sn1.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "vm1" {
name = "vm1minipro98908"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [
azurerm_network_interface.nic1.id
]
vm_size = "Standard_D2s_v3"
delete_os_disk_on_termination = true
storage_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
storage_os_disk {
name = "storageosdisk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "peer1vm"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
}
How to verify
- Run
terraform apply - In Azure Portal:
- VM1 exists
- NIC is attached
- NIC is in subnet1
- VM has no public IP
Step 3: Create VM2 in Subnet 2
Now we repeat the same pattern for the second network:
- NIC attached to
subnet2 - VM attached to that NIC
resource "azurerm_network_interface" "nic2" {
name = "nic2minipro8789"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "ipconfignic2minipro989"
subnet_id = azurerm_subnet.sn2.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "vm2" {
name = "vm2minipro98908"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [
azurerm_network_interface.nic2.id
]
vm_size = "Standard_D2s_v3"
delete_os_disk_on_termination = true
storage_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
storage_os_disk {
name = "storageosdisk2"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = "peer2vm"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
}
How to verify
- Run
terraform apply - Confirm:
- VM2 exists
- NIC2 is attached
- NIC2 belongs to subnet2
- VM2 also has no public IP
Step 4: Test Connectivity Before Peering (Expected to Fail)
Now we test whether the two VMs can communicate without peering.
Because:
- They are in different VNets
- There is no peering
- No public IPs
They should not be able to communicate.
How I tested
Using Azure Run Command (no SSH or Bastion needed):
- VM1 → Operations → Run command → RunShellScript
- Command:
ping -c 4 10.1.0.x
Result
4 packets transmitted, 0 received, 100% packet loss
✅ This is the correct and expected behavior
Step 5: Add VNet Peering (Both Directions)
VNet peering in Azure is not automatic.
You must create two peering connections:
- VNet1 → VNet2
- VNet2 → VNet1
resource "azurerm_virtual_network_peering" "peer1to2" {
name = "peer1to2minipro455"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet1.name
remote_virtual_network_id = azurerm_virtual_network.vnet2.id
}
resource "azurerm_virtual_network_peering" "peer2to1" {
name = "peer2to1minipro455"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet2.name
remote_virtual_network_id = azurerm_virtual_network.vnet1.id
}
How to verify
- Run
terraform apply - Azure Portal → Virtual Networks → Peering
- Status should show Connected
Step 6: Test Connectivity After Peering (Expected to Work)
Now we repeat the same test as before.
ping -c 4 10.1.0.x
Result
4 packets transmitted, 4 received, 0% packet loss
🎉 Success!
This proves:
- VNet peering is working
- Traffic stays on Azure’s private backbone
- No public IPs are required
Key Takeaways for Beginners
- VMs communicate via NICs, not directly via subnets
- VNets are isolated by default
- Peering must be created in both directions
- Always test:
- ❌ Before peering
- ✅ After peering
- Applying Terraform in small steps makes debugging much easier
Why This Step-by-Step Approach Matters
Instead of running one giant terraform apply and hoping for the best, this method:
- Builds real understanding
- Makes Azure networking concepts visual
- Helps you debug like a real DevOps engineer
If you can do this project, you already understand:
- VNets
- Subnets
- NICs
- VM placement
- VNet peering
- Real-world network isolation
That’s solid progress 👏

Leave a Reply