Azure Citadel
  • Blogs

  • Azure Arc
    • Overview
    • Azure Arc-enabled Kubernetes
      • Prereqs
      • Background
      • Deploy Cluster
      • Connect to Arc
      • Enable GitOps
      • Deploy Application
      • Enable Azure AD
      • Enforce Policy
      • Enable Monitoring
      • Enable Azure Defender
      • Enable Data Services
      • Enable Application Delivery
    • Azure Arc-enabled Servers
      • Prereqs
      • Scenario
      • Hack Overview
      • Azure Landing Zone
      • Arc Pilot resource group
      • Azure Monitoring Agent
      • Additional policy assignments
      • Access your on prem VMs
      • Create onboarding scripts
      • Onboarding using scripts
      • Inventory
      • Monitoring
      • SSH
      • Windows Admin Center
      • Governance
      • Custom Script Extension
      • Key Vault Extension
      • Managed Identity
    • Useful Links
  • Azure CLI
    • Install
    • Get started
    • JMESPATH queries
    • Integrate with Bash
  • Azure Landing Zones
    • ALZ Accelerator
      • Prereqs
      • Elevate
      • Bootstrap
      • Demote
      • Components
    • Deploy an Azure Landing Zone
      • Create an initial ALZ config
      • Run through the CI/CD workflow
    • Example Library Configs
      • Azure Landing Zone library
      • Azure Landing Zone library with overrides
  • Azure Lighthouse
    • Minimal Lighthouse definition
    • Using service principals
    • Privileged Identity Management
  • Azure Policy
    • Azure Policy Basics
      • Policy Basics in the Azure Portal
      • Creating Policy via the CLI
      • Deploy If Not Exists
      • Management Groups and Initiatives
    • Creating Custom Policies
      • Customer scenario
      • Policy Aliases
      • Determine the logic
      • Create the custom policy
      • Define, assign and test
  • Marketplace
    • Introduction
      • Terminology
      • Offer Types
    • Partner Center
    • Offer Type
    • Publish a VM Offer HOL
      • Getting Started
      • Create VM Image
      • Test VM Image
      • VM Offer with SIG
      • VM Offer with SAS
      • Publish Offer
      • Other VM Resources
    • Publish a Solution Template HOL
      • Getting Started
      • Create ARM Template
      • Validate ARM Template
      • Create UI Definition
      • Package Assets
      • Publish Offer
    • Publish a Managed App HOL
      • Getting Started
      • Create ARM Template
      • Validate ARM Template
      • Create UI Definition
      • Package Assets
      • Publish Offer
    • Managed Apps with AKS HOL
    • Other Managed App Resources
    • SaaS Offer HOLs
    • SaaS Offer Video Series
      • Video 1 - SaaS Offer Overview
      • Video 2 - Purchasing a SaaS Offer
      • Video 3 - Purchasing a Private SaaS Plan
      • Video 4 - Publishing a SaaS Offer
      • Video 5 - Publishing a Private SaaS Plan
      • Video 6 - SaaS Offer Technical Overview
      • Video 7 - Azure AD Application Registrations
      • Video 8 - Using the SaaS Offer REST Fulfillment API
      • Video 9 - The SaaS Client Library for .NET
      • Video 10 - Building a Simple SaaS Landing Page in .NET
      • Video 11 - Building a Simple SaaS Publisher Portal in .NET
      • Video 12 - SaaS Webhook Overview
      • Video 13 - Implementing a Simple SaaS Webhook in .NET
      • Video 14 - Securing a Simple SaaS Webhook in .NET
      • Video 15 - SaaS Metered Billing Overview
      • Video 16 - The SaaS Metered Billing API with REST
  • Microsoft Fabric
    • Theory
    • Prereqs
    • Fabric Capacity
    • Set up a Remote State
    • Create a repo from a GitHub template
    • Configure an app reg for development
    • Initial Terraform workflow
    • Expanding your config
    • Configure a workload identity
    • GitHub Actions for Microsoft Fabric
    • GitLab pipeline for Microsoft Fabric
  • Packer & Ansible
    • Packer
    • Ansible
    • Dynamic Inventories
    • Playbooks & Roles
    • Custom Roles
    • Shared Image Gallery
  • Partner Admin Link
    • Understanding PAL
    • User IDs & PAL
    • Service principals & PAL
    • CI/CD pipelines & PAL
    • Creating a dedicated PAL service principal
    • Azure Lighthouse & PAL
    • PAL FAQ
  • REST API
    • REST API theory
    • Using az rest
  • Setup
  • Sovereign Landing Zones
    • ALZ Accelerator
      • Prereqs
      • Elevate
      • Bootstrap
      • Demote
      • Components
    • Deploy Sovereign Landing Zone
      • Create an initial SLZ config
      • Run through the CI/CD workflow
      • Sovereign Landing Zone
    • Example Library Configs
      • Sovereign Landing Zone
      • Sovereign Landing Zone library with overrides
  • Terraform
    • Fundamentals
      • Initialise
      • Format
      • Validate
      • Plan
      • Apply
      • Adding resources
      • Locals and outputs
      • Managing state
      • Importing resources
      • Destroy
    • Get set up for Terraform
      • Cloud Shell
      • macOS
      • Windows with PowerShell
      • Windows with Ubuntu in WSL2
    • Using AzAPI
      • Using the REST API
      • azapi_resource
      • Removing azapi_resource
      • azapi_update_resource
      • Data sources and outputs
      • Removing azapi_update_resource
  • Virtual Machines
    • Azure Bastion with native tools & AAD
    • Managed Identities

  • About
  • Archive
  1. Home
  2. Azure Landing Zones
  3. Deploy an Azure Landing Zone
  4. Create an initial ALZ config
Create an initial ALZ config
Create an initial ALZ config
Deploy an Azure Landing Zone
Create an initial ALZ config
Run through the CI/CD workflow
  • Overview
  • References

Create an initial ALZ config

Clone the repo locally and add an initial Azure Landing Zone config based on the examples in the Terraform Registry.

Table of Contents

  • Overview
  • References

Overview

On this page you will:

  1. Locally clone the repo generated by the Azure Landing Zone accelerator

  2. Add the alz provider

    Use the alz Terraform provider to pull the library definitions from the ALZ Library repo. You will learn plenty more about the alz provider and libraries throughout these labs, but for now we’ll just pull the most recent release of default definitions.

  3. Update the variables.tf and add a main.tf to your Terraform config

    Add a config based on the examples provided for the Azure Verified Modules in the Terraform registry.

  4. Locally test your config will pass the CI tests

    The CI workflow will test that your config meets minimum standards, and will pass the format, will initialise the providers, and passes the validation. You will also test it will create a terraform plan.

    Override the backend with a terraform_override.tf file and test locally.

    ⚠️ You must not attempt to run terraform apply locally at his point. You will use the CI/CD workflow in the next page instead.

Clone the repo

  1. Variables

    github_repo="alz-mgmt"
    github_org="richeney-org"
    

    ⚠️ Set to the correct values for your repo and org names.

  2. Clone the repo

    You may wish to switch to your standard directory for your git repos, if you have one.

    git clone https://github.com/${github_org}/${github_repo}
    
  3. Change directory

    cd $github_repo
    
  4. Open Visual Studio Code

    code .
    

What is the ALZ Library repo?

We will be looking at libraries in greater detail in another series, but for the moment here is a brief introduction. You can read the documentation for the Azure Landing Zone libraries at https://azure.github.io/Azure-Landing-Zones-Library.

Libraries contain definitions for core compliancy guardrails. They define archetypes, collections of Azure Policy policies, initiatives, and assignments, plus custom role definitions. The archetypes. The architecture definitions then define the set of management groups, including their name, cosmetic display names, and the array of archetypes assigned to them.

The Azure Landing Zones Terraform provider can pull library definitions from different sources. These are then referenced by the ALZ Terraform module which specifies the architecture_name as well as any additional modifications, default values, etc. The two are closely related and these labs will help you to understand both.

The provider can pull from more than one source, and those sources can have dependencies on other libraries, which provides great scope for extensibility and customisation. We will explore this from a Microsoft partner perspective, extending the Azure Landing Zone and Sovereign Landing Zone baselines with reusable partner libraries, including country and industry packs for the sovereignty context. In addition, we will explore using archetype overrides to allow individual customers to customise their deployments.

The most common library source is the ALZ Library repo which is actively maintained by the Microsoft Customer Architecture and Engineering team (CAE). The repo contains library definitions for Azure Landing Zones, Sovereign Landing Zones, and Azure Monitoring Baseline Alerts. All are semantically versioned and you can view the releases.

Azure Landing Zone

Provider

Add the alz provider and specify the most recent Azure Landing Zone library release.

  1. Check the releases page for the most recent platform/alz release.

    At the time of writing this is 2025.09.3.

  2. Add to the terraform.tf

    provider "alz" {
      library_overwrite_enabled = true
      library_references = [
        {
          path = "platform/alz"
          ref  = "2025.09.3"
        }
      ]
    }
    

    The alz provider will pull in that version of the library definition from https://github.com/Azure/Azure-Landing-Zones-Library/tree/{tag}/{path} where {tag} is {path}/{ref}.

    For example, https://github.com/Azure/Azure-Landing-Zones-Library/tree/platform/alz/2025.09.3/platform/alz for the core Azure Landing Zones library.

Variables

  1. Add this code block to the variables.tf

    variable "location" {
      type        = string
      default     = "uksouth"
      description = "Location for the resources"
    }
    
    variable "email_security_contact" {
      type        = string
      default     = ""
      description = "Email address for security alerts"
    }
    

Main

We will cover how the AVM modules work with the library as we progress through the labs. For the moment we will just get an example up and running,

  1. Create a main.tf

    data "azapi_client_config" "current" {}
    
    locals {
      management_resource_group_name          = "rg-management-${var.location}"
      management_resource_group_id            = "/subscriptions/${var.subscription_ids["management"]}/resourcegroups/${local.management_resource_group_name}"
      automation_account_name                 = "aa-management-${var.location}"
      log_analytics_workspace_name            = "law-management-${var.location}"
      ama_user_assigned_managed_identity_name = "uami-management-ama-${var.location}"
      dcr_change_tracking_name                = "dcr-change-tracking"
      dcr_defender_sql_name                   = "dcr-defender-sql"
      dcr_vm_insights_name                    = "dcr-vm-insights"
    
      ama_user_assigned_managed_identity_id       = "${local.management_resource_group_id}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/${local.ama_user_assigned_managed_identity_name}"
      ama_change_tracking_data_collection_rule_id = "${local.management_resource_group_id}/providers/Microsoft.Insights/dataCollectionRules/${local.dcr_change_tracking_name}"
      ama_mdfc_sql_data_collection_rule_id        = "${local.management_resource_group_id}/providers/Microsoft.Insights/dataCollectionRules/${local.dcr_defender_sql_name}"
      ama_vm_insights_data_collection_rule_id     = "${local.management_resource_group_id}/providers/Microsoft.Insights/dataCollectionRules/${local.dcr_vm_insights_name}"
      log_analytics_workspace_id                  = "${local.management_resource_group_id}/providers/Microsoft.OperationalInsights/workspaces/${local.log_analytics_workspace_name}"
    
    }
    
    module "management_resources" {
      # <https://registry.terraform.io/modules/Azure/avm-ptn-alz-management/azurerm/latest>
      source  = "Azure/avm-ptn-alz-management/azurerm"
      version = "0.9.0"
    
      location                     = var.location
      resource_group_name          = local.management_resource_group_name
      automation_account_name      = local.automation_account_name
      log_analytics_workspace_name = local.log_analytics_workspace_name
    
      data_collection_rules = {
        "change_tracking" = {
          "name" = local.dcr_change_tracking_name
        }
        "defender_sql" = {
          "name" = local.dcr_defender_sql_name
        }
        "vm_insights" = {
          "name" = local.dcr_vm_insights_name
        }
      }
    
      user_assigned_managed_identities = {
        ama = {
          name = local.ama_user_assigned_managed_identity_name
        }
      }
    }
    
    module "management_groups" {
      # <https://registry.terraform.io/modules/Azure/avm-ptn-alz/azurerm/latest>
      source  = "Azure/avm-ptn-alz/azurerm"
      version = "0.14.0"
    
      architecture_name  = "alz"
      location           = var.location
      parent_resource_id = data.azapi_client_config.current.tenant_id # Tenant root group
      #   retries            = local.default_retries
      #   timeouts           = local.default_timeouts
    
      dependencies = {
        policy_assignments = [
          module.management_resources.data_collection_rule_ids,
          module.management_resources.resource_id,
          module.management_resources.user_assigned_identity_ids,
        ]
      }
    
      policy_assignments_to_modify = {
        "alz" = {
          "policy_assignments" = {
            "Deploy-MDFC-Config-H224" = {
              "parameters" = {
                "ascExportResourceGroupLocation"              = jsonencode({ value = var.location })
                "ascExportResourceGroupName"                  = jsonencode({ value = "rg-asc-export-${var.location}" })
                "emailSecurityContact"                        = jsonencode({ value = var.email_security_contact })
                "enableAscForAppServices"                     = jsonencode({ value = "Disabled" })
                "enableAscForArm"                             = jsonencode({ value = "Disabled" })
                "enableAscForContainers"                      = jsonencode({ value = "Disabled" })
                "enableAscForCosmosDbs"                       = jsonencode({ value = "Disabled" })
                "enableAscForCspm"                            = jsonencode({ value = "Disabled" })
                "enableAscForKeyVault"                        = jsonencode({ value = "Disabled" })
                "enableAscForOssDb"                           = jsonencode({ value = "Disabled" })
                "enableAscForServers"                         = jsonencode({ value = "Disabled" })
                "enableAscForServersVulnerabilityAssessments" = jsonencode({ value = "Disabled" })
                "enableAscForSql"                             = jsonencode({ value = "Disabled" })
                "enableAscForSqlOnVm"                         = jsonencode({ value = "Disabled" })
                "enableAscForStorage"                         = jsonencode({ value = "Disabled" })
              }
            }
          }
        }
        "connectivity" = {
          "policy_assignments" = {
            "Enable-DDoS-VNET" = {
              "enforcement_mode" = "DoNotEnforce"
            }
          }
        }
        "corp" = {
          "policy_assignments" = {
            "Deploy-Private-DNS-Zones" = {
              "enforcement_mode" = "DoNotEnforce"
            }
          }
        }
        "landingzones" = {
          "policy_assignments" = {
            "Enable-DDoS-VNET" = {
              "enforcement_mode" = "DoNotEnforce"
            }
          }
        }
      }
    
      policy_default_values = {
        "ama_user_assigned_managed_identity_name"     = jsonencode({ value = local.ama_user_assigned_managed_identity_name })
        "ama_user_assigned_managed_identity_id"       = jsonencode({ value = local.ama_user_assigned_managed_identity_id })
        "ama_change_tracking_data_collection_rule_id" = jsonencode({ value = local.ama_change_tracking_data_collection_rule_id })
        "ama_mdfc_sql_data_collection_rule_id"        = jsonencode({ value = local.ama_mdfc_sql_data_collection_rule_id })
        "ama_vm_insights_data_collection_rule_id"     = jsonencode({ value = local.ama_vm_insights_data_collection_rule_id })
        "log_analytics_workspace_id"                  = jsonencode({ value = local.log_analytics_workspace_id })
      }
    
      subscription_placement = {
        "connectivity" = {
          "management_group_name" = "connectivity"
          "subscription_id"       = var.subscription_ids["connectivity"]
        }
        "identity" = {
          "management_group_name" = "identity"
          "subscription_id"       = var.subscription_ids["identity"]
        }
        "management" = {
          "management_group_name" = "management"
          "subscription_id"       = var.subscription_ids["management"]
        }
        # "security" = {
        #   "management_group_name" = "security"
        #   "subscription_id"       = var.subscription_ids["security"]
        # }
      }
    }
    

    ⚠️ Note the subscription placement object. Comment (CTR+K,CTRL+C) and uncomment (CTRL+K,CTRL+U) as needed depending on how many subscriptions you are specifying.

Sovereign Landing Zone

Provider

Add the alz provider and specify the most recent Sovereign Landing Zone library release.

  1. Check the releases page for the most recent platform/slz release.

    At the time of writing this is 2025.10.1.

  2. Add to the terraform.tf

    provider "alz" {
      library_overwrite_enabled = true
      library_references = [
        {
          path = "platform/slz"
          ref  = "2025.10.1"
        }
      ]
    }
    

    The alz provider will pull in that version of the library definition from https://github.com/Azure/Azure-Landing-Zones-Library/tree/{tag}/{path} where {tag} is {path}/{ref}.

    For example, https://github.com/Azure/Azure-Landing-Zones-Library/tree/platform/slz/2025.10.1/platform/slz for the Sovereign Landing Zone library.

Variables

  1. Add this code block to the variables.tf

    variable "location" {
      type        = string
      default     = "uksouth"
      description = "Location for the resources"
    }
    
    variable "email_security_contact" {
      type        = string
      default     = ""
      description = "Email address for security alerts"
    }
    

Main

The Sovereign Landing Zone example differs from the Azure Landing Zone example in only a few ways, all related to the management_groups module:

  1. the architecture_name is now set to slz rather than alz
  2. the policy_assignments_to_modify object also refers to the slz architecture name
  3. the policy_default_values now includes "allowed_locations" = jsonencode({ value = [${var.location}]}) as this array of region shortcodes is expected by the additional archetypes

We won’t cover those changes in any real detail on this page. For the moment let’s get it up and running.

There is a later deep dive on libraries and how the AVM modules work with those library definitions and we’ll circle back to these changes at that poit.

  1. Create a main.tf

    data "azapi_client_config" "current" {}
    
    locals {
      management_resource_group_name          = "rg-management-${var.location}"
      management_resource_group_id            = "/subscriptions/${var.subscription_ids["management"]}/resourcegroups/${local.management_resource_group_name}"
      automation_account_name                 = "aa-management-${var.location}"
      log_analytics_workspace_name            = "law-management-${var.location}"
      ama_user_assigned_managed_identity_name = "uami-management-ama-${var.location}"
      dcr_change_tracking_name                = "dcr-change-tracking"
      dcr_defender_sql_name                   = "dcr-defender-sql"
      dcr_vm_insights_name                    = "dcr-vm-insights"
    
      ama_user_assigned_managed_identity_id       = "${local.management_resource_group_id}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/${local.ama_user_assigned_managed_identity_name}"
      ama_change_tracking_data_collection_rule_id = "${local.management_resource_group_id}/providers/Microsoft.Insights/dataCollectionRules/${local.dcr_change_tracking_name}"
      ama_mdfc_sql_data_collection_rule_id        = "${local.management_resource_group_id}/providers/Microsoft.Insights/dataCollectionRules/${local.dcr_defender_sql_name}"
      ama_vm_insights_data_collection_rule_id     = "${local.management_resource_group_id}/providers/Microsoft.Insights/dataCollectionRules/${local.dcr_vm_insights_name}"
      log_analytics_workspace_id                  = "${local.management_resource_group_id}/providers/Microsoft.OperationalInsights/workspaces/${local.log_analytics_workspace_name}"
    }
    
    module "management_resources" {
      # <https://registry.terraform.io/modules/Azure/avm-ptn-alz-management/azurerm/latest>
      source  = "Azure/avm-ptn-alz-management/azurerm"
      version = "0.9.0"
    
      location                     = var.location
      resource_group_name          = local.management_resource_group_name
      automation_account_name      = local.automation_account_name
      log_analytics_workspace_name = local.log_analytics_workspace_name
    
      data_collection_rules = {
        "change_tracking" = {
          "name" = local.dcr_change_tracking_name
        }
        "defender_sql" = {
          "name" = local.dcr_defender_sql_name
        }
        "vm_insights" = {
          "name" = local.dcr_vm_insights_name
        }
      }
    
      user_assigned_managed_identities = {
        ama = {
          name = local.ama_user_assigned_managed_identity_name
        }
      }
    }
    
    module "management_groups" {
      # <https://registry.terraform.io/modules/Azure/avm-ptn-alz/azurerm/latest>
      source  = "Azure/avm-ptn-alz/azurerm"
      version = "0.14.0"
    
      architecture_name  = "slz"
      location           = var.location
      parent_resource_id = data.azapi_client_config.current.tenant_id # Tenant root group
    
      #   retries            = local.default_retries
      #   timeouts           = local.default_timeouts
    
      dependencies = {
        policy_assignments = [
          module.management_resources.data_collection_rule_ids,
          module.management_resources.resource_id,
          module.management_resources.user_assigned_identity_ids,
        ]
      }
    
      policy_assignments_to_modify = {
        "slz" = {
          "policy_assignments" = {
            "Deploy-MDFC-Config-H224" = {
              "parameters" = {
                "ascExportResourceGroupLocation"              = jsonencode({ value = var.location })
                "ascExportResourceGroupName"                  = jsonencode({ value = "rg-asc-export-${var.location}" })
                "emailSecurityContact"                        = jsonencode({ value = var.email_security_contact })
                "enableAscForAppServices"                     = jsonencode({ value = "Disabled" })
                "enableAscForArm"                             = jsonencode({ value = "Disabled" })
                "enableAscForContainers"                      = jsonencode({ value = "Disabled" })
                "enableAscForCosmosDbs"                       = jsonencode({ value = "Disabled" })
                "enableAscForCspm"                            = jsonencode({ value = "Disabled" })
                "enableAscForKeyVault"                        = jsonencode({ value = "Disabled" })
                "enableAscForOssDb"                           = jsonencode({ value = "Disabled" })
                "enableAscForServers"                         = jsonencode({ value = "Disabled" })
                "enableAscForServersVulnerabilityAssessments" = jsonencode({ value = "Disabled" })
                "enableAscForSql"                             = jsonencode({ value = "Disabled" })
                "enableAscForSqlOnVm"                         = jsonencode({ value = "Disabled" })
                "enableAscForStorage"                         = jsonencode({ value = "Disabled" })
              }
            }
          }
        }
        "connectivity" = {
          "policy_assignments" = {
            "Enable-DDoS-VNET" = {
              "enforcement_mode" = "DoNotEnforce"
            }
          }
        }
        "corp" = {
          "policy_assignments" = {
            "Deploy-Private-DNS-Zones" = {
              "enforcement_mode" = "DoNotEnforce"
            }
          }
        }
        "landingzones" = {
          "policy_assignments" = {
            "Enable-DDoS-VNET" = {
              "enforcement_mode" = "DoNotEnforce"
            }
          }
        }
      }
    
      policy_default_values = {
        "allowed_locations"                           = jsonencode({ value = [var.location] })
        "ama_user_assigned_managed_identity_name"     = jsonencode({ value = local.ama_user_assigned_managed_identity_name })
        "ama_user_assigned_managed_identity_id"       = jsonencode({ value = local.ama_user_assigned_managed_identity_id })
        "ama_change_tracking_data_collection_rule_id" = jsonencode({ value = local.ama_change_tracking_data_collection_rule_id })
        "ama_mdfc_sql_data_collection_rule_id"        = jsonencode({ value = local.ama_mdfc_sql_data_collection_rule_id })
        "ama_vm_insights_data_collection_rule_id"     = jsonencode({ value = local.ama_vm_insights_data_collection_rule_id })
        "log_analytics_workspace_id"                  = jsonencode({ value = local.log_analytics_workspace_id })
      }
    
      subscription_placement = {
        "connectivity" = {
          "management_group_name" = "connectivity"
          "subscription_id"       = var.subscription_ids["connectivity"]
        }
        "identity" = {
          "management_group_name" = "identity"
          "subscription_id"       = var.subscription_ids["identity"]
        }
        "management" = {
          "management_group_name" = "management"
          "subscription_id"       = var.subscription_ids["management"]
        }
        # "security" = {
        #   "management_group_name" = "security"
        #   "subscription_id"       = var.subscription_ids["security"]
        # }
      }
    }
    

    ⚠️ Note the subscription placement object. Comment (CTR+K,CTRL+C) and uncomment (CTRL+K,CTRL+U) as needed depending on how many subscriptions you are specifying

Test Locally

We’ll now run a few tests locally before we commit the changes and create a pull request. We’ll override the backend so that we can run terraform commands at the CLI. , and then run a few commands to ensure we’ll pass the continuous integration (CI) workflow’s inbuilt checks.

Override the backend

The terraform block in the terraform.tf file includes an empty backend.

  backend "azurerm" {}

The workflow’s will use the BACKEND_AZURE_* environment variables within the terraform init steps so that the managed identities write the remote state file to the correct storage account and container.

Before we progress we want to make sure that your config will pass the CI checks, so we will locally override the backend so that you can run the tests at the CLI.

  1. Create a terraform_override.tf

    terraform {
      backend "local" {
        path = "terraform.tfstate"
      }
    }
    

    ℹ️ The file should be greyed out in Visual Studio Code’s explorer pane as this filename is included in the .gitignore. It will not be included in any git commits.

  2. Set the ARM_SUBSCRIPTION_ID env var

    The config is also expecting a subscription ID for the main azurerm provider block. Export the environment variable, setting it to the management subscription GUID.

    export ARM_SUBSCRIPTION_ID="{managementSubscriptionId}"
    

Check your config

  1. Check the format

    The CI checks will ensure that your committed code aligns with gofmt standards.

    terraform fmt
    

    This command will automatically correct issues where possible, listing the files it has modified. If there is no command output then all of the files are already compliant.

  2. Initialise

    terraform init
    

    Confirm that terraform has been successfully initialized and has downloaded the providers as expected.

  3. Validate

    Confirm that the config is syntactically sound.

    terraform validate
    

    Correct any errors until terraform validate succeeds.

    Success! The configuration is valid.
    

Plan (optional)

If your ID has access to view the resources in both Azure and GitHub then you can test to see if the plan will succeed.

If your ID does not have that access the the plan should correctly fail. If so then skip this step and move onto the next page. When you commit your changes and create your pull request then the terraform plan will be tested in the CI workflow.

Note that running terraform plan locally just checks the code and is not using the remote state in the storage account as we overrode the backend. This may be more noticeable when adding in additional changes in later labs, but the displayed planned changes may be disregarded as it is really just a deeper validation at this stage.

The terraform plan outputs in the CI/CD pipelines will provide the accurate and definitive diff and should always be properly reviewed.

⚠️ DO NOT RUN terraform apply! You will do this via the CI/CD pipelines on the next page.

  1. Plan

    terraform plan
    

    The output will show that terraform plans to create over 600 resources.

Next

If the tests all look good then your config should pass the CI tests. You’re done on this page.

On the next page we will run through the standard branch based workflow to commit your changes into a new branch and submit a pull request to trigger the CI/CD pipelines.

References

  • https://aka.ms/alz
  • https://aka.ms/alz/accelerator/docs
  • https://github.com/Azure/alz-terraform-accelerator
Source: https://icy-island-077f0c303-135.westeurope.4.azurestaticapps.net/alz/deploy/initial/
Published: 10 Oct 2025
Printed:
Deploy an Azure Landing Zone Create an initial ALZ config Run through the CI/CD workflow