Generate VNet configs with subnets, NSGs, peering, and connectivity. Use when the user wants to design or set up Azure networking infrastructure.
You are an Azure Virtual Network and networking expert. Generate production-ready network architectures.
Determine from user input or $ARGUMENTS:
Design the network layout:
Standard multi-tier architecture:
VNet: 10.0.0.0/16
├── GatewaySubnet (for VPN/ExpressRoute)
│ └── 10.0.0.0/27
├── AzureFirewallSubnet
│ └── 10.0.1.0/26
├── AzureBastionSubnet
│ └── 10.0.2.0/26
├── App subnets (for VMs, App Services, Container Apps)
│ ├── 10.0.10.0/24 (web tier)
│ ├── 10.0.11.0/24 (app tier)
│ └── 10.0.12.0/24 (AKS)
├── Data subnets (for databases, caches)
│ ├── 10.0.20.0/24 (SQL/Cosmos)
│ └── 10.0.21.0/24 (Redis/Storage)
└── Private Endpoint subnet
└── 10.0.30.0/24
Bicep template:
param location string = resourceGroup().location
param vnetName string
param addressPrefix string = '10.0.0.0/16'
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [addressPrefix]
}
dhcpOptions: {
dnsServers: [] // Use Azure-provided DNS or specify custom
}
subnets: [
{
name: 'GatewaySubnet'
properties: {
addressPrefix: '10.0.0.0/27'
}
}
{
name: 'AzureFirewallSubnet'
properties: {
addressPrefix: '10.0.1.0/26'
}
}
{
name: 'AzureBastionSubnet'
properties: {
addressPrefix: '10.0.2.0/26'
networkSecurityGroup: { id: bastionNsg.id }
}
}
{
name: 'web-subnet'
properties: {
addressPrefix: '10.0.10.0/24'
networkSecurityGroup: { id: webNsg.id }
serviceEndpoints: [
{ service: 'Microsoft.Sql' }
{ service: 'Microsoft.Storage' }
{ service: 'Microsoft.KeyVault' }
]
natGateway: { id: natGateway.id }
}
}
{
name: 'app-subnet'
properties: {
addressPrefix: '10.0.11.0/24'
networkSecurityGroup: { id: appNsg.id }
delegations: [
{
name: 'appServiceDelegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
natGateway: { id: natGateway.id }
}
}
{
name: 'aks-subnet'
properties: {
addressPrefix: '10.0.12.0/22' // Larger for AKS pods
networkSecurityGroup: { id: aksNsg.id }
natGateway: { id: natGateway.id }
}
}
{
name: 'data-subnet'
properties: {
addressPrefix: '10.0.20.0/24'
networkSecurityGroup: { id: dataNsg.id }
serviceEndpoints: [
{ service: 'Microsoft.Sql' }
{ service: 'Microsoft.Storage' }
]
privateEndpointNetworkPolicies: 'Disabled'
}
}
{
name: 'private-endpoints'
properties: {
addressPrefix: '10.0.30.0/24'
privateEndpointNetworkPolicies: 'Disabled'
}
}
]
}
}
Web tier NSG:
resource webNsg 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: '${vnetName}-web-nsg'
location: location
properties: {
securityRules: [
{
name: 'AllowHTTPS'
properties: {
priority: 100
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: 'Internet'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '443'
}
}
{
name: 'AllowHTTP'
properties: {
priority: 110
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: 'Internet'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '80'
}
}
{
name: 'AllowAzureLoadBalancer'
properties: {
priority: 200
direction: 'Inbound'
access: 'Allow'
protocol: '*'
sourceAddressPrefix: 'AzureLoadBalancer'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '*'
}
}
{
name: 'DenyAllInbound'
properties: {
priority: 4096
direction: 'Inbound'
access: 'Deny'
protocol: '*'
sourceAddressPrefix: '*'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '*'
}
}
]
}
}
App tier NSG:
resource appNsg 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: '${vnetName}-app-nsg'
location: location
properties: {
securityRules: [
{
name: 'AllowFromWebSubnet'
properties: {
priority: 100
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: '10.0.10.0/24'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '8080'
}
}
{
name: 'AllowFromAppGateway'
properties: {
priority: 110
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: 'GatewayManager'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '65200-65535'
}
}
{
name: 'DenyAllInbound'
properties: {
priority: 4096
direction: 'Inbound'
access: 'Deny'
protocol: '*'
sourceAddressPrefix: '*'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '*'
}
}
]
}
}
Data tier NSG:
resource dataNsg 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: '${vnetName}-data-nsg'
location: location
properties: {
securityRules: [
{
name: 'AllowSqlFromAppSubnet'
properties: {
priority: 100
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: '10.0.11.0/24'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '1433'
}
}
{
name: 'AllowRedisFromAppSubnet'
properties: {
priority: 110
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourceAddressPrefix: '10.0.11.0/24'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '6380'
}
}
{
name: 'DenyAllInbound'
properties: {
priority: 4096
direction: 'Inbound'
access: 'Deny'
protocol: '*'
sourceAddressPrefix: '*'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '*'
}
}
]
}
}
Private Endpoint for Azure SQL:
resource sqlPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
name: 'sql-private-endpoint'
location: location
properties: {
subnet: { id: privateEndpointSubnet.id }
privateLinkServiceConnections: [
{
name: 'sql-connection'
properties: {
privateLinkServiceId: sqlServer.id
groupIds: ['sqlServer']
}
}
]
}
}
resource sqlDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.database.windows.net'
location: 'global'
}
resource sqlDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
parent: sqlDnsZone
name: '${vnetName}-link'
location: 'global'
properties: {
virtualNetwork: { id: vnet.id }
registrationEnabled: false
}
}
resource sqlDnsRecord 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01' = {
parent: sqlPrivateEndpoint
name: 'sqlDnsGroup'
properties: {
privateDnsZoneConfigs: [
{
name: 'config'
properties: {
privateDnsZoneId: sqlDnsZone.id
}
}
]
}
}
Common Private DNS Zone names:
Azure SQL: privatelink.database.windows.net
Cosmos DB: privatelink.documents.azure.com
Storage Blob: privatelink.blob.core.windows.net
Storage File: privatelink.file.core.windows.net
Key Vault: privatelink.vaultcore.azure.net
ACR: privatelink.azurecr.io
Event Hub: privatelink.servicebus.windows.net
Service Bus: privatelink.servicebus.windows.net
Redis: privatelink.redis.cache.windows.net
Same-region peering:
resource hubToSpokePeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-05-01' = {
parent: hubVnet
name: 'hub-to-spoke'
properties: {
remoteVirtualNetwork: { id: spokeVnet.id }
allowVirtualNetworkAccess: true
allowForwardedTraffic: true
allowGatewayTransit: true // Hub shares its gateway
useRemoteGateways: false
}
}
resource spokeToHubPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-05-01' = {
parent: spokeVnet
name: 'spoke-to-hub'
properties: {
remoteVirtualNetwork: { id: hubVnet.id }
allowVirtualNetworkAccess: true
allowForwardedTraffic: true
allowGatewayTransit: false
useRemoteGateways: true // Spoke uses hub's gateway
}
}
resource natGatewayPip 'Microsoft.Network/publicIPAddresses@2023-05-01' = {
name: '${vnetName}-nat-pip'
location: location
sku: { name: 'Standard' }
properties: {
publicIPAllocationMethod: 'Static'
}
zones: ['1', '2', '3']
}
resource natGateway 'Microsoft.Network/natGateways@2023-05-01' = {
name: '${vnetName}-nat'
location: location
sku: { name: 'Standard' }
properties: {
idleTimeoutInMinutes: 10
publicIpAddresses: [
{ id: natGatewayPip.id }
]
}
zones: ['1', '2', '3']
}
resource firewallPip 'Microsoft.Network/publicIPAddresses@2023-05-01' = {
name: '${vnetName}-fw-pip'
location: location
sku: { name: 'Standard' }
properties: { publicIPAllocationMethod: 'Static' }
}
resource firewall 'Microsoft.Network/azureFirewalls@2023-05-01' = {
name: '${vnetName}-firewall'
location: location
properties: {
sku: {
name: 'AZFW_VNet'
tier: 'Premium'
}
ipConfigurations: [
{
name: 'fw-ipconfig'
properties: {
subnet: { id: firewallSubnet.id }
publicIPAddress: { id: firewallPip.id }
}
}
]
firewallPolicy: { id: firewallPolicy.id }
}
}
resource firewallPolicy 'Microsoft.Network/firewallPolicies@2023-05-01' = {
name: '${vnetName}-fw-policy'
location: location
properties: {
sku: { tier: 'Premium' }
threatIntelMode: 'Deny'
intrusionDetection: {
mode: 'Deny'
configuration: {
bypassTrafficSettings: []
}
}
dnsSettings: {
enableProxy: true
}
}
}
resource networkRuleCollection 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-05-01' = {
parent: firewallPolicy
name: 'DefaultNetworkRuleCollectionGroup'
properties: {
priority: 200
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'AllowAzureServices'
priority: 100
action: { type: 'Allow' }
rules: [
{
ruleType: 'NetworkRule'
name: 'AllowDNS'
ipProtocols: ['UDP']
sourceAddresses: ['10.0.0.0/16']
destinationAddresses: ['*']
destinationPorts: ['53']
}
{
ruleType: 'NetworkRule'
name: 'AllowNTP'
ipProtocols: ['UDP']
sourceAddresses: ['10.0.0.0/16']
destinationAddresses: ['*']
destinationPorts: ['123']
}
]
}
]
}
}
Route table to force traffic through Azure Firewall:
resource routeTable 'Microsoft.Network/routeTables@2023-05-01' = {
name: '${vnetName}-app-rt'
location: location
properties: {
disableBgpRoutePropagation: false
routes: [
{
name: 'default-to-firewall'
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: firewall.properties.ipConfigurations[0].properties.privateIPAddress
}
}
]
}
}
Hub VNet (10.0.0.0/16)
├── GatewaySubnet (VPN/ExpressRoute)
├── AzureFirewallSubnet
├── AzureBastionSubnet
└── Shared services (DNS, AD, monitoring)
Spoke 1 - Production (10.1.0.0/16)
├── web-subnet
├── app-subnet
├── data-subnet
└── private-endpoints
Spoke 2 - Development (10.2.0.0/16)
├── dev-subnet
└── test-subnet
resource "azurerm_virtual_network" "main" {
name = var.vnet_name
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
address_space = [var.address_prefix]
}
resource "azurerm_subnet" "web" {
name = "web-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.10.0/24"]
service_endpoints = ["Microsoft.Sql", "Microsoft.Storage", "Microsoft.KeyVault"]
}
resource "azurerm_subnet" "app" {
name = "app-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.11.0/24"]
delegation {
name = "app-service"
service_delegation {
name = "Microsoft.Web/serverFarms"
}
}
}
resource "azurerm_subnet" "data" {
name = "data-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = ["10.0.20.0/24"]
private_endpoint_network_policies = "Disabled"
}
resource "azurerm_network_security_group" "web" {
name = "${var.vnet_name}-web-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
security_rule {
name = "AllowHTTPS"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_address_prefix = "Internet"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "443"
}
security_rule {
name = "DenyAllInbound"
priority = 4096
direction = "Inbound"
access = "Deny"
protocol = "*"
source_address_prefix = "*"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "web" {
subnet_id = azurerm_subnet.web.id
network_security_group_id = azurerm_network_security_group.web.id
}
resource "azurerm_nat_gateway" "main" {
name = "${var.vnet_name}-nat"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
sku_name = "Standard"
idle_timeout_in_minutes = 10
zones = ["1"]
}
resource "azurerm_nat_gateway_public_ip_association" "main" {
nat_gateway_id = azurerm_nat_gateway.main.id
public_ip_address_id = azurerm_public_ip.nat.id
}
resource flowLog 'Microsoft.Network/networkWatchers/flowLogs@2023-05-01' = {
name: 'NetworkWatcher_${location}/${vnetName}-flow-log'
location: location
properties: {
targetResourceId: webNsg.id
storageId: storageAccount.id
enabled: true
retentionPolicy: {
days: 90
enabled: true
}
format: {
type: 'JSON'
version: 2
}
flowAnalyticsConfiguration: {
networkWatcherFlowAnalyticsConfiguration: {
enabled: true
workspaceResourceId: logAnalytics.id
trafficAnalyticsInterval: 10
}
}
}
}