Home / Azure

Azure | Azure Landing Zone (test purposes)

Azure | Azure Landing Zone (test purposes)


Reading Time: 10 minutes

If you want to use Azure with a corporate network, you SHOULD have an Azure Landing Zone. An Azure Landing Zone provides the infrastructure for a safe hybrid network between Azure and your network. Well in a test environment (like home) you do not have a very complex network configuration or the need of for example an Azure Firewall. But to create the hybrid connection between your tenant and your home setup it is possible to do it with a s2s vpn. For this landingzone we use Azure BICEP to deploy it.

How to Setup

Well within Azure you need to have:

Azure Verified Modules

In this codebase, I consistently use Azure Verified Modules (AVM) to deploy Azure resources in a standardized, reliable way.

Azure Verified Modules are Microsoft-supported, community-backed Infrastructure-as-Code modules (for example Bicep modules) that implement Azure resources following established best practices. They are designed to be:

  • Reusable: deploy the same building block (e.g., VNet, Storage Account, Key Vault) across projects without rewriting code.
  • Consistent: naming, tagging, outputs, diagnostics, and patterns are aligned, making environments predictable.
  • Quality-checked: modules follow defined requirements (testing, documentation, versioning, and validation) to reduce deployment errors.
  • Secure by design: common security and governance options (RBAC, private endpoints, diagnostics, encryption) are typically built in or supported.
  • Easier to maintain: versioned modules enable controlled upgrades, and changes are managed centrally rather than per project.

By using AVMs throughout the code, deployments become more repeatable, auditable, and scalable. Instead of custom one-off templates, AVMs provide proven building blocks, so adding new resources or extending environments is faster and less error-prone, while still keeping governance and best practices intact.

Resource Groups

Resource Groups in Azure organize related resources so they can be managed, secured, and monitored together. They simplify deployments, lifecycle management, and access control by grouping items with the same purpose or environment. They also help with cost tracking, applying policies, and keeping environments clean and consistent. Using Resource Groups makes your Azure environment easier to maintain, automate, and govern as it grows.

Parameters
param resourceGroups = [
  {
    name: 'rsg-lz-network-01'
    location: location
    lock: {
      kind: 'CanNotDelete'
      name: 'rsg-lz-network-01-lockdel'
    }
  }
  {
    name: 'rsg-lz-platform-01'
    location: location
    lock: {
      kind: 'CanNotDelete'
      name: 'rsg-lz-platform-01-lockdel'
    }
  }
  {
    name: 'rsg-lz-arc-01'
    location: location
    lock: {
      kind: 'CanNotDelete'
      name: 'rsg-lz-arc-01-lockdel'
    }
  }
  {
    name: 'rsg-lz-privateendpoint-01'
    location: location
    lock: {
      kind: 'CanNotDelete'
      name: 'rsg-lz-privateendpoint-01-lockdel'
    }
  }
  {
    name: 'rsg-lz-privatednszones-01'
    location: location
    lock: {
      kind: 'CanNotDelete'
      name: 'rsg-lz-privatednszones-01-lockdel'
    }
  }
  {
    name: 'rsg-lz-monitoring-01'
    location: location
    lock: {
      kind: 'CanNotDelete'
      name: 'rsg-lz-monitoring-01-lockdel'
    }
  }
]

As shown, the current naming convention does not include any reference to the deployment region. To ensure consistency, clarity, and easier resource identification across multiple Azure regions, the naming standard should be updated to include a region identifier as part of each resource name.

Code
module resourceGroup 'br/public:avm/res/resources/resource-group:0.4.3' = [
  for rg in resourceGroups: {
    name: 'rg-deploy-${rg.Name}'
    params: {
      name: rg.Name
      location: rg.Location
      lock: rg.Lock
    }
  }
]

Some resources must be deployed at a different scope than the subscription-level deployment, often at a specific resource group scope. Therefore, we explicitly pass the required scope to those modules. After deploying the resource groups, we collect resource IDs from multiple groups and use them to correctly scope deployments and reference shared components across the environment.

resource resRsgMonitoring 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
  dependsOn: [resourceGroup]
  name: '${prefix}-${region}-rsg-lz-monitoring-01'
}
resource resRsgNetwork 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
  dependsOn: [resourceGroup]
  name: '${prefix}-${region}-rsg-lz-network-01'
}
resource resRsgDnszones 'Microsoft.Resources/resourceGroups@2025-04-01' existing = {
  dependsOn: [resourceGroup]
  name: '${prefix}-${region}-rsg-lz-privatednszones-01'
}

LogAnalytics Workspace

A Log Analytics Workspace is a central Azure service used to collect, store, and query log and telemetry data from various Azure resources, applications, and on‑premises systems. It serves as the data backend for Azure Monitor, enabling insights, alerting, troubleshooting, and security analysis. Multiple services, such as Azure Monitor, Defender for Cloud, Sentinel, and Diagnostics, send their data to a workspace, making it the core component for observability and operational monitoring in Azure.

Parameter
param logAnalyticsWorkspaces  = [
  {
  name: 'law-lz-01'
  location: location
  }
]

For a Log Analytics Workspace, only two parameters are required to deploy it: the name and the region. The workspace does not need additional configuration values during creation because Azure automatically applies the default settings for retention, SKU, and diagnostics. More advanced configurations, such as data retention policies, solutions, or diagnostic integrations, can be added afterward, but the minimal deployment requires just these two properties.

Code
module modLogAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.15.0' = [
  for law in logAnalyticsWorkspaces: {
    name: 'law-deploy-${law.name}'
    dependsOn: [resourceGroup]
    params: {
      name: law.name
      location: law.location
    }
    scope: resRsgMonitoring
  }
]

Virtual Network

In Azure‑based network architectures, it is considered a best practice to implement a hub‑and‑spoke topology. This involves creating a dedicated Virtual Network (VNet) that functions as the central hub, while all other VNets that host workloads or application resources are deployed as spokes.

The hub VNet typically contains shared services, such as firewalls, VPN gateways, ExpressRoute circuits, or connectivity appliances, that provide secure access to on‑premises environments. By routing all external and hybrid connectivity through the hub, you create a single, centralized entry and exit point to your on‑premises network. This significantly simplifies management, improves security governance, and ensures that traffic flows remain predictable and auditable.

Spoke VNets, on the other hand, contain the actual Azure resources such as virtual machines, databases, application services, and platform components. These spokes connect to the hub using VNet peering, which allows for high‑bandwidth, low‑latency communication without exposing them directly to the on‑premises network. As a result, the environment becomes easier to scale, maintain, and secure, while still enabling controlled inter‑network communication where required.

Parameters
param virtualNetworks  = [
  {
    name: 'vnet-hub-01'
    addressPrefixes: [
      '10.190.0.0/16'
    ]
    subnets: [
      {
        name: 'default'
        addressPrefix: '10.190.1.0/24'
      }
      {
        name: 'GatewaySubnet'
        addressPrefix: '10.190.2.0/24'
      }
    ]
  }
  {
    name: 'vnet-spoke-01'
    addressPrefixes: [
      '10.200.0.0/16'
    ]
    subnets: [
      {
        name: 'default'
        addressPrefix: '10.200.1.0/24'
      }
      {
        name: 'endpoints'
        addressPrefix: '10.200.2.0/24'
      }

    ]
   }
]  

When defining configuration values or deployment settings, I consistently place all parameters inside an array. This approach provides a clear and scalable structure that simplifies future expansion. By organizing parameters in this way, it becomes significantly easier to introduce additional resources or configuration items without disrupting the existing structure.

Using an array‑based parameter model also improves maintainability. It ensures that parameters follow a predictable pattern, reduces duplication, and enables automation tools to iterate over the items in a consistent manner. As a result, adding a new resource, such as another virtual machine, storage account, subnet, or policy assignment, requires only the insertion of an additional element in the array, rather than modifying multiple sections of a template or script.

This method supports cleaner code, reduces the likelihood of misconfigurations, and enhances the flexibility of deployment pipelines, especially in environments where infrastructure evolves frequently.

Code
module modVirtualNetworks 'br/public:avm/res/network/virtual-network:0.7.2' = [
  for vnet in virtualNetworks: {
    name: 'vnet-deploy-${vnet.name}'
    dependsOn: [resourceGroup, modLogAnalyticsWorkspace]
    scope: resRsgNetwork
    params: {
      name: vnet.name
      addressPrefixes: vnet.addressPrefixes
      subnets: [
        for subnet in vnet.subnets: {
          name: subnet.name
          addressPrefix: subnet.addressPrefix
        }
      ]
      tags: vnet.tags
      diagnosticSettings: [
        {
          metricCategories: [
            {
              category: 'AllMetrics'
            }
          ]
          name: 'customSetting'
          workspaceResourceId: modLogAnalyticsWorkspace[0].outputs.resourceId
        }
      ]
    }
  }
]

Public IP Address

A VPN gateway requires an external Public IP Address to act as the entry point for secure connections into the Azure environment. Azure supports several types of Public IP Addresses, each designed for different scenarios. You can choose between Basic and Standard SKUs, as well as static or dynamic allocation methods. Selecting the appropriate Public IP type ensures the correct availability, security, and routing behavior for your VPN setup.

Parameters
param publicIpAddresses = [
  {
    Name: 'pip-vgw-hub-01'
    Location: location
    Sku: 'Standard'
  }
]

The type of Public IP Address you want to deploy is determined by the SKU. Azure uses the SKU to define the capabilities and behavior of the Public IP, such as whether it is Basic or Standard, and what level of availability, security, and features it supports. By selecting the correct SKU, you ensure that the Public IP matches the requirements of your workload, for example, VPN Gateways generally require a Standard SKU Public IP for production‑grade reliability and security.

Code
module modpublicIpAddresses 'br/public:avm/res/network/public-ip-address:0.5.1' = [for pip in publicIpAddresses: {
  name: 'public-ip-deploy-${pip.name}'
  scope: resRsgNetwork
  params: {
    name: pip.Name
    location: pip.Location
    publicIPAllocationMethod: 'Static'
    skuName: pip.Sku
  }
}]  

Virtual Network Gateway

The Virtual Network Gateway serves as the central connectivity component of the landing zone. It acts as the primary secure entry and exit point between Azure and external networks, such as on‑premises datacenters or other cloud environments. Through VPN or ExpressRoute connections, the gateway enables encrypted, reliable communication and ensures that all hybrid network traffic flows through a controlled, monitored, and highly available path.
Because it provides the foundational hybrid connectivity layer, the Virtual Network Gateway is often considered the heart of the landing zone, enabling workloads inside Azure to communicate seamlessly and securely with systems outside the cloud.

Parameter
param virtualNetworkGateways  = [
  {
    name: 'vnet-gw-01'
    virtualNetworkName: virtualNetworks[0].name
    publicIpAddressName: 'vpnGatewayPublicIP'
    gatewayType: 'Vpn'
    vpnType: 'RouteBased'
    sku: 'VpnGw1AZ'
    clusterSettings: {}
  }
]

As you can see, this parameter file contains several references to other parameters. These references allow values to be dynamically reused throughout the deployment, ensuring consistency and reducing duplication. By linking parameters together, the configuration becomes easier to maintain and update, especially when multiple resources depend on shared values such as names, locations, or resource IDs.

Code
module modVirtualNetworkGateways 'br/public:avm/res/network/virtual-network-gateway:0.10.1' = [
  for vnetGw in virtualNetworkGateways: {
    name: 'vnet-gw-deploy-${vnetGw.name}'
    dependsOn: [modVirtualNetworks,modpublicIpAddresses]
    scope: resRsgNetwork
    params: {
      name: vnetGw.name
      gatewayType: vnetGw.gatewayType
      vpnType: vnetGw.vpnType
      skuName: vnetGw.sku
      existingPrimaryPublicIPResourceId: modpublicIpAddresses[0].outputs.resourceId
      virtualNetworkResourceId: modVirtualNetworks[0].outputs.resourceId
      clusterSettings: {
        clusterMode: 'activePassiveNoBgp'
      }
    }
  }
]

In this design, I have selected the simplest configuration, using a single Public IP Address for the Virtual Network Gateway. This setup is sufficient for standard connectivity scenarios. However, Azure supports multiple, more advanced gateway configurations that can include additional Public IPs or higher availability options. These alternatives allow for greater redundancy, enhanced throughput, or more complex hybrid networking requirements, depending on the needs of the environment.

Local Network Gateway

A Local Network Gateway represents your on‑premises network in Azure. It defines the public IP address of your local VPN device and the internal address spaces used in your datacenter. Azure uses this information to correctly route traffic between your on‑premises environment and the Virtual Network Gateway. Together, they form the basis of a secure, bidirectional hybrid VPN connection between Azure and your local network.

Parameter
param localNetworkGateways  = [
  {
    name: 'lng-home-01'
    ipAddress: '8.8.8.8' // your external home ip
    addressPrefixes: [
      '192.168.1.1/24' // ip range on the other side of the vpn
    ]
  }
]

You need to adjust the addressPrefixes and ipAddress values to match your own on‑premises network configuration. These settings define the internal IP ranges and the public IP of your local VPN device, which Azure uses to route traffic correctly. Providing accurate values ensures that the VPN connection functions properly and avoids routing conflicts between your on‑premises environment and the Azure virtual network.

Code
module modLocalNetworkGateways 'br/public:avm/res/network/local-network-gateway:0.4.0' = [
  for lng in localNetworkGateways: {
    name: 'lng-deploy-${lng.name}'
    dependsOn: [modVirtualNetworkGateways]
    scope: resRsgNetwork
    params: {
      name: lng.name
      localGatewayPublicIpAddress: lng.ipAddress
      localNetworkAddressSpace: {
        addressPrefixes: lng.addressPrefixes
      }
    }
  }
]

VPN Connection

With all prerequisite components deployed and configured, the final step is to establish the actual VPN connection to the Local Network Gateway. This connection links the Azure Virtual Network Gateway to your on‑premises environment, enabling encrypted, bidirectional communication. Once created, Azure can route traffic securely between both networks based on the address spaces and settings defined earlier.

Parameter
param vpnConnections  = [
  {
    name: 'vnet-gw-01-to-${localNetworkGateways[0].name}-conn'
    vpnGatewayName: 'vnet-gw-01'
    localNetworkGatewayName: 'lng-home-01'
    sharedKey: 'DitiseenSharedKey01!'
  }
  // {
  //   name: 'connection2'
  //   localNetworkGatewayName: localNetworkGateways[1].name
  //   vpnGatewayName: 'vpnGateway'
  //   sharedKey: ''
  // }
]

Once again, the configuration is defined within an array. This approach ensures that additional connections can be added easily in the future without modifying the overall structure. By simply appending a new entry to the array, you can introduce new VPN connections in a clean, scalable, and maintainable way, keeping the deployment flexible as the environment grows.

Code
module modConnection 'br/public:avm/res/network/connection:0.1.6' = [
  for (connection, i) in vpnConnections: {
    scope: resRsgNetwork
    name: 'connection-deploy-${connection.name}'
    dependsOn: [modVirtualNetworkGateways, modLocalNetworkGateways]
    params: {
      name: connection.name
      virtualNetworkGateway1: {
        id: modVirtualNetworkGateways[0].outputs.resourceId
      }
      localNetworkGateway2ResourceId: modLocalNetworkGateways[0].outputs.resourceId
      connectionType: 'IPsec'
      vpnSharedKey: connection.sharedKey
    }
  }
]

Private DNS Zones

Private DNS Zones provide internal, private name resolution for resources inside an Azure virtual network. They allow Azure services and virtual machines to resolve hostnames to private IP addresses without exposing DNS records to the public internet. By linking Private DNS Zones to one or more virtual networks, resources can automatically register and resolve names, simplifying connectivity in hybrid and hub‑and‑spoke environments. They are essential for services like Private Endpoints, ensuring secure and consistent internal DNS resolution.

Parameter
param dnsZones = [
  {
    name: 'privatelink.vaultcore.azure.net'
  }
  {
    name: 'privatelink.file.core.windows.net'
  }
  {
    name: 'privatelink.blob.core.windows.net'
  }
  {
    name: 'privatelink.queue.core.windows.net'
  }
  {
    name: 'privatelink.we.backup.windowsazure.com'
  }
  {
    name: 'privatelink.wvd.microsoft.com'
  }
]

In this case I have chosen some private dns zones. In the array it is easy to add another zone.

Code
module modDnsZones 'br/public:avm/res/network/private-dns-zone:0.5.0' = [
  for dnsZone in dnsZones: {
    name: 'dnszone-deploy-${dnsZone.name}'
    scope: resRsgDnszones
    params: {
      name: dnsZone.name
    }
  }
]

Conclusion

Now that the landing zone is fully deployed and all core networking components are in place, we can proceed with configuring the on‑premises internet connection for the IPsec tunnel to Azure. With the Virtual Network Gateway, Local Network Gateway, and Public IP Address already prepared, your firewall or VPN appliance can be configured to establish a secure, encrypted IPsec connection into the Azure environment.


Designer (23)

Stay close to the action—follow GetToThe.Cloud across social!
Deep dives and hands‑on how‑tos on Azure Local, hybrid cloud, automation, PowerShell/Bicep, AVD + FSLogix, image pipelines, monitoring, networking, and resilient design when the internet/Azure is down.

🔗 Our channels
▶️ YouTube: https://www.youtube.com/channel/UCa33PgGdXt-Dr4w3Ub9hrdQ
💼 LinkedIn Group: https://www.linkedin.com/groups/9181126/
✖️ X (Twitter): https://x.com/Gettothecloud
🎵 TikTok: https://www.tiktok.com/@gettothecloud
🐙 GitHub: https://github.com/GetToThe-Cloud/Website
💬 Slack: DM us for an invite
📲 WhatsApp: DM for the community link

We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners. View more
Cookies settings
Accept
Privacy & Cookie policy
Privacy & Cookies policy
Cookie name Active

Who we are

Our website address is: https://www.gettothe.cloud

Comments

When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.

Media

If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.

Cookies

If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.

Embedded content from other websites

Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.

Who we share your data with

If you request a password reset, your IP address will be included in the reset email.

How long we retain your data

If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.

What rights you have over your data

If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.

Where we send your data

Visitor comments may be checked through an automated spam detection service.
Save settings
Cookies settings