Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iac-tf-azure — an opinionated Azure Terraform delivery platform

A module monorepo and the delivery machinery around it. Most Terraform module collections stop at "here are some modules" — this repository also answers the questions that actually decide whether infrastructure delivery is safe and repeatable:

  • Who deploys? An OIDC-federated service principal you create once with the optional bootstrap/ — no client secrets exist anywhere in the system.
  • Where does state live? In your subscription, in hardened storage (versioning + soft delete), one state file per deployment per environment.
  • What stops a bad deploy? Layered gates: per-module CI (fmt/validate/tflint/trivy), a readiness checklist (access level, applied policies), an Azure Policy pre-flight that blocks Deny conflicts before apply with a human-readable report, and GitHub environment approvals in front of every apply.
  • What is anything called? One machine-readable naming scheme (modules/naming/naming.json, per Microsoft CAF) read by both Terraform and the workflows — the two can never disagree.

How a deployment flows

flowchart TB
    subgraph setup["One-time setup (optional bootstrap/)"]
        B[terraform apply bootstrap/] --> SP["App registration + OIDC<br/>federated to your fork"]
        B --> SA["State storage account<br/>versioning + soft delete"]
        B --> RA["Contributor grant<br/>on your subscription"]
        PF["preflight.sh / .ps1<br/>✅ access level ℹ️ policy notice"]
    end

    subgraph repo["Your fork (trunk-based: main + short-lived branches)"]
        M["modules/*<br/>19 resource modules, tagged"]
        N["naming.json<br/>CAF scheme as data"]
        D["deployments/*<br/>roots + env tfvars"]
    end

    subgraph pipeline["Deploy workflow (dispatch: deployment / environment / action)"]
        G["Quality gates<br/>fmt · validate · trivy"] --> RC["Readiness checklist<br/>app role + policy count"]
        RC --> P["terraform plan<br/>(state in your subscription)"]
        P --> PP{"Policy pre-flight<br/>plan types × Deny assignments"}
        PP -- conflict --> X["❌ Readable conflict table<br/>policy · assignment · fix options"]
        PP -- clean --> A{{"Environment approval<br/>(required reviewers)"}}
        A --> AP["terraform apply"]
    end

    subgraph azure["Your Azure subscription"]
        ST[("tfstate<br/><deployment>.<env>.tfstate")]
        RES["Resources<br/>CAF-named, secure defaults"]
    end

    SP -. "OIDC token per run" .-> pipeline
    D --> G
    N --> M
    M --> P
    AP --> RES
    P <--> ST
    AP <--> ST
Loading

Design principles

  1. Modules own resources, roots own wiring, tfvars own environments. Modules never create resource groups, VNets or subnets — callers pass IDs, which makes every module equally usable greenfield (new resources) and brownfield (data sources against existing estate).
  2. Secure by default, explicit to weaken. Public network access off, deny-by-default network rules, TLS 1.2 floor, RBAC over access policies, managed identities over credentials. A dev convenience (public vault, purgeable on destroy) is a visible line in env/dev.tfvars, never a hidden default.
  3. Releases are tags, not branches. <module>/vMAJOR.MINOR.PATCH on trunk; consumers pin ref= and upgrade deliberately. Branching model: docs/branching.md (trunk-based — no develop, no environment branches).
  4. Policy awareness is part of delivery, not an afterthought. The pre-flight maps a plan's resource types to ARM types and scans every assignment in scope (initiatives expanded, parameterized effects resolved). What it can't prove statically, the apply-time parser catches — either way you get a table naming the policy, the assignment and your options, not a JSON wall.
  5. Everything client-specific is configuration. Three secrets, four repo variables, tfvars. Fork the repo and nothing in the code needs to change.

Quickstart

az login
bootstrap/preflight.sh                # see your checkmarks
cd bootstrap && terraform init && terraform apply \
  -var github_repository="<you>/<fork>" \
  -var state_storage_account_name="<unique>"   # optional, once
# paste the github_setup_commands output, create dev/prod environments
scripts/tf.sh key_vault dev plan      # or Actions -> Deploy module

Full walkthrough: docs/client-onboarding.md.

Consuming modules directly

module "key_vault" {
  source = "git::https://github.com/KarlesP/iac-tf-azure.git//modules/key_vault?ref=key_vault/v1.0.0"

  name                = module.naming.names.key_vault
  location            = "westeurope"
  resource_group_name = azurerm_resource_group.this.name
  tenant_id           = data.azurerm_client_config.current.tenant_id
  tags                = local.tags
}

Module catalog

Module Description
naming CAF-compliant name generator driven by naming.json (no resources)
key_vault Key Vault with RBAC, network ACLs, keys, access policies
storage_account Hardened storage account (deny-by-default networking, AAD auth)
table_storage Tables on an existing storage account
sftp SFTP-enabled storage with optional Log Analytics diagnostics
app_service Windows App Service + plan + Application Insights
app_gateway Application Gateway, fully parametrized listeners/pools/rules
function_app Function App (Windows or Linux) + plan + insights
static_web_app Static Web App + optional custom domain
sql_server Azure SQL Server + database + firewall rules
sql_managed_instance SQL MI into an existing delegated subnet
cosmosdb Cosmos DB (NoSQL) + optional private endpoint
linux_vm / windows_vm Single VMs on an existing subnet (SSH-first on Linux)
vm_extensions CustomScript extensions for existing VMs
container_registry ACR with optional firewall (Premium)
container_app Container Apps environment + app with CPU autoscaling
container_instance Container group, private (delegated subnet) or public
avd AVD host pool + app group + session-host VMSS

Authoring standard (file layout, naming, secure defaults): docs/module-standards.md.

Running things

Path What Gate
Actions → Deploy module plan/apply/destroy a deployment root quality gates → readiness checklist → policy pre-flight → environment approval
Actions → E2E deploy with policy pre-flight full deploy + optional teardown, per-run CAF names same pre-flight; proves the pipeline end to end
Actions → Retiring services report weekly scan for VM sizes Microsoft is retiring, with migration options (Rust, scripts/huginn) —
scripts/tf.sh / tf.ps1 the same roots locally; local state unless TF_STATE_* set terraform's own confirmation

Versioning

Per-module semver tags: <module>/vMAJOR.MINOR.PATCH. A breaking variable/output change bumps MAJOR — consumers are pinned, so trunk can move as fast as it likes.

Contributing

Short-lived branch → PR → squash to main. CI validates only the modules your PR touches. Start with docs/branching.md and docs/module-standards.md.

About

Azure Terraform module library — public edition of edda-terraform-azure

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages