Deploying your landing zone from a local terminal is a single point of failure. When an engineer’s laptop holds the Terraform state, or when “just a quick change” bypasses review, you no longer have a governed foundation. You have an undocumented configuration that cannot be reliably reconstructed.
Enterprise landing zones require version-controlled pipelines. Every change to your management group hierarchy, hub networking, or identity model must be previewed and gated before reaching production. A CI/CD pipeline is the enforcement mechanism that makes your cloud governance model real.
By the end of this guide, you will:
- Implement a PR-driven “plan-first” workflow.
- Configure OIDC-based, secret-less authentication to Azure.
- Automate security scanning (Checkov) and WAF compliance (PSRule).
- Design sequential deployment layers with environment approval gates.
- Secure state management for Terraform and Bicep.
This is Post 8 in the Azure Platform Engineering series.
Repository Structure and Strategy
Organizing IaC by Layer
A mono-repo simplifies dependency tracking and centralizes branch protection. Organize your platform code by deployment layer:
landing-zone/
├── .github/workflows/
│ ├── validate.yml # Pull request validation
│ └── deploy.yml # Main branch deployment
├── live/ # Environment configurations
│ ├── layer-0-management/ # Hierarchy and Policies
│ ├── layer-1-connectivity/ # Hub Networking
│ └── layer-2-identity/ # Entra ID and PIM
└── modules/ # Reusable AVM wrappers
Keep application-specific code in separate workload repositories. The platform pipeline deploys the foundation; workload pipelines deploy into it.
Authentication: OIDC with Federated Credentials
GitHub Actions connects to Azure using OpenID Connect (OIDC). This eliminates the need for long-lived client secrets. GitHub issues a short-lived token for each run, which Entra ID exchanges for an Azure access token.
Federated Credential (2026):
In 2026, Azure supports Flexible Federation (wildcards). A single credential can cover all environments in a repo using repo:org/name:*.
Note: Wildcard syntax may require the claimsMatchingExpression property in your App Registration.
The Pull Request Workflow
Validation and Scanning
Every PR triggers validate.yml. The pipeline enforces standards, allowing human review to focus on architecture.
Key Steps:
- Terraform fmt: Enforces consistent code style.
- Checkov: Scans for security misconfigurations (e.g., unencrypted storage).
- PSRule for Azure: Validates WAF compliance before deployment.
Plan Previews
The pipeline runs terraform plan or az deployment mg what-if and posts the diff as a collapsible PR comment. Reviewers see the exact resource delta without leaving GitHub.
- name: Post Plan to PR
uses: actions/github-script@v8 # 2026 Stable
with:
script: |
const planOutput = fs.readFileSync('plan.txt', 'utf8');
const body = `## Terraform Plan\n<details><summary>Show Plan</summary>\n\n\`\`\`\n${planOutput}\n\`\`\`\n</details>`;
await github.rest.issues.createComment({ ...context.issue, body });
graph TD
subgraph GitHub_Repo [GitHub Repository]
PR[Pull Request] -- Merge --> Main[Main Branch]
end
subgraph CI_Pipeline [CI/CD Workflow]
direction TB
L0[Layer 0: Management Group Hierarchy]
L1[Layer 1: Connectivity - Hub VNet/Firewall]
L2[Layer 2: Identity - PIM/RBAC]
L3[Layer 3: Governance - Azure Policy]
end
Main --> L0
L0 -- Success --> Gate1{Gate: Hub-Prod Approval}
Gate1 -- Approved --> L1
L1 -- Success --> Gate2{Gate: Identity-Prod Approval}
Gate2 -- Approved --> L2
L2 -- Success --> L3
style L0 fill:#e1f5fe,stroke:#01579b
style L1 fill:#e1f5fe,stroke:#01579b
style L2 fill:#e1f5fe,stroke:#01579b
style Gate1 fill:#fff9c4,stroke:#fbc02d
style Gate2 fill:#fff9c4,stroke:#fbc02d
Notes:
- Layered Deployment prevents dependency cycles and ensures a logical build order.
- Manual Gates (Environment approvals) provide human oversight before changes are applied to production infrastructure.
- The Sequential Flow ensures that the management group hierarchy (Layer 0) is established before attempting to deploy networking or identity resources into those groups.
Deployment Execution
Sequential Layers and Gates
Landing zone layers have hard dependencies. You cannot deploy networking before the management group hierarchy exists. Use environment gates to enforce human approval for production layers.
deploy-layer-1:
needs: deploy-layer-0
environment: hub-prod # Approval gate
steps:
- uses: azure/login@v3 # 2026 Stable
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
# ... OIDC login
State Security
Store Terraform state in an Azure Storage Account with:
- Soft Delete: Minimum 7-day retention.
- Versioning: Recovery from state corruption.
- RBAC-only Access: Disable Shared Key access to force the pipeline’s identity to use RBAC.
Best Practices
- Pin Action Versions: Use
azure/login@v3, not@latest, to prevent silent pipeline breakages. - Concurrency Groups: Use GitHub’s
concurrencyfeature to prevent parallel runs from corrupting the same state file. - Self-hosted Runners: Consider self-hosted runners if your state storage account requires Private Link connectivity for enhanced security.
Sources
Next, move to Post 9: Azure Cost Governance: Tagging, Budgets, and FinOps. We will establish financial guardrails and automated cost reporting for your landing zone.
