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 zone
    • ALZ Accelerator
      • Prereqs
      • Elevate
      • Bootstrap
      • Demote
      • Components
    • Deploy an Azure landing zone
      • Create an initial ALZ config
      • Add a local override library
      • Test locally
      • Run through the CI/CD workflow
    • Libraries
      • What is a library?
      • Policies, Assignments and Roles
      • Archetypes, Overrides and Architecture
      • Metadata and Policy Default Values
      • Custom libraries
    • 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
    • Service principals with credentials
    • PAL tagging with a service principal
    • CI/CD pipelines & OpenID Connect
    • User and guest IDs
    • Azure Lighthouse & PAL
    • PAL FAQ
  • REST API
    • REST API theory
    • Using az rest
  • Setup
  • Sovereign landing zone
    • ALZ Accelerator
      • Prereqs
      • Elevate
      • Bootstrap
      • Demote
      • Components
    • Deploy Sovereign landing zone
      • Create an initial SLZ config
      • Add a local override library
      • Test locally
      • Run through the CI/CD workflow
    • Libraries
      • What is a library?
      • Policies, Assignments and Roles
      • Archetypes, Overrides and Architecture
      • Metadata and Policy Default Values
      • Custom libraries
    • Reference Library Configs
      • Sovereign landing zone
      • Sovereign landing zone library with overrides
      • SLZ extended with a country pack
  • 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. Sovereign landing zone
  3. Deploy Sovereign landing zone
  4. Create an initial SLZ config
Create an initial SLZ config
Create an initial SLZ config
Deploy Sovereign landing zone
Create an initial SLZ config
Test locally
Add a local override library
Run through the CI/CD workflow

Create an initial SLZ config

Make a new branch, add an initial Sovereign landing zone config based on the examples in the Terraform Registry, and then follow the approval workflow process.

Table of Contents

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 zone 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 zone, Sovereign landing zone, 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 2026.01.0.

  2. Add to the terraform.tf

    provider "alz" {
      library_overwrite_enabled = true
      library_references = [
        {
          path = "platform/alz"
          ref  = "2026.01.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/alz/2026.01.1/platform/alz for the core Azure 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

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.20.2"
    
      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  = "2026.02.0"
        }
      ]
    }
    

    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/2026.02.0/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.20.2"
    
      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

Next

The config in your repo will work as it stands with its direct use of the platform library.

Skip the next page and you will get the standard set of definitions and assignments from the standard alz or slz platform library as you test your repo locally and then deploy via the CI/CD pipelines. Absolutely valid, but not recommended. Why not?

The next page will take you through the steps to configure a local override library. Even if you have no need to use an override on day one this approach gives you the flexibility to do so in the future. It also gives you a local metadata file and architecture file and you will see in the examples how you can update these when you add in additional hosted custom libraries without causing massive trauma to your Terraform state file.

Source: https://www.azurecitadel.com/slz/deploy/initial/
Published: 10 Oct 2025
Printed:
Deploy Sovereign landing zone Create an initial SLZ config Test locally