{"id":51331163,"url":"https://github.com/gerardrecinto/azure-infrastructure","last_synced_at":"2026-07-01T23:03:52.780Z","repository":{"id":359305540,"uuid":"1245444866","full_name":"gerardrecinto/azure-infrastructure","owner":"gerardrecinto","description":"Enterprise Azure infrastructure: hub-spoke networking, private AKS, APIM + Front Door Premium, Service Bus, Key Vault RBAC — all via Terraform and OIDC GitHub Actions","archived":false,"fork":false,"pushed_at":"2026-05-21T08:36:19.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T15:56:59.241Z","etag":null,"topics":["azure","azure-api-management","azure-front-door","azure-kubernetes-service","devops","github-actions","hub-spoke","infrastructure-as-code","kubernetes","terraform"],"latest_commit_sha":null,"homepage":null,"language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gerardrecinto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T08:20:37.000Z","updated_at":"2026-05-21T08:36:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gerardrecinto/azure-infrastructure","commit_stats":null,"previous_names":["gerardrecinto/azure-infrastructure"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/gerardrecinto/azure-infrastructure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerardrecinto%2Fazure-infrastructure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerardrecinto%2Fazure-infrastructure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerardrecinto%2Fazure-infrastructure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerardrecinto%2Fazure-infrastructure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gerardrecinto","download_url":"https://codeload.github.com/gerardrecinto/azure-infrastructure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gerardrecinto%2Fazure-infrastructure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35025987,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","azure-api-management","azure-front-door","azure-kubernetes-service","devops","github-actions","hub-spoke","infrastructure-as-code","kubernetes","terraform"],"created_at":"2026-07-01T23:03:52.134Z","updated_at":"2026-07-01T23:03:52.766Z","avatar_url":"https://github.com/gerardrecinto.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# azure-infrastructure\n\n![Azure Infrastructure logo](docs/assets/logo.svg)\n\n![Terraform](https://img.shields.io/badge/Terraform-1.7%2B-7B42BC?logo=terraform\u0026logoColor=white)\n![Azure](https://img.shields.io/badge/Azure-AKS%20%7C%20APIM%20%7C%20Front%20Door-0078D4?logo=microsoftazure\u0026logoColor=white)\n![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-OIDC-2088FF?logo=githubactions\u0026logoColor=white)\n![License: MIT](https://img.shields.io/badge/License-MIT-22c55e)\n\n![Demo](docs/assets/demo.gif)\n\nEnterprise Azure infrastructure using Terraform. Hub-spoke topology, private AKS, APIM behind Front Door Premium, Service Bus Premium, and centralized Key Vault: all provisioned via OIDC-authenticated GitHub Actions with no stored service principal secrets.\n\nCommercial angle and consulting hooks: [docs/go-to-market.md](docs/go-to-market.md).\n\n\u003e All company-specific values (subscription IDs, tenant IDs, resource names, CIDR blocks, hostnames) are replaced with `PLACEHOLDER_*` values.\n\n---\n\n## Architecture\n\n```\n               +--------------------------------------------------+\n               |           Azure Front Door Premium               |\n               |      WAF: OWASP 3.2 + BotManager rules           |\n               +----------------------+---------------------------+\n                                      | Private Link\n               +----------------------+---------------------------+\n               |       APIM (Internal VNet mode)                  |\n               |   JWT validation  |  Key Vault named values      |\n               +--------+-----------+----------------------------+\n                        |                       |\n           +------------+--------+   +----------+----------+\n           |     AKS Cluster     |   |   Service Bus        |\n           |  (private API svr)  |   |   Premium + PE       |\n           +---------------------+   +---------------------+\n                    |\n        +-----------+-----------+\n        |       Hub VNet        |\n        |  Azure Firewall Prem  |\n        |  Bastion (Standard)   |\n        +-----------+-----------+\n               |    |    |\n        +------+  +-+-+  +------+\n        | AKS  |  |API|  | Data |\n        | Spoke|  |Spk|  | Spk  |\n        +------+  +---+  +------+\n```\n\n---\n\n## Design decisions\n\n**Hub-spoke over full mesh** — At n spokes, full mesh requires n*(n-1)/2 peering connections and each spoke manages routes to every other spoke. Hub-spoke centralizes east-west traffic through Azure Firewall Premium (IDPS + application layer inspection). Adding a new spoke is one peering pair, not n-1.\n\n**APIM Internal VNet + Front Door Premium** — APIM has no routable public IP. Front Door Premium reaches the APIM internal VIP via Private Link, so the gateway is only reachable after passing the WAF layer. The common alternative (APIM on a public IP behind IP allowlisting) fails when IPs rotate.\n\n**Private AKS cluster** — The API server endpoint is a private endpoint inside the hub VNet. Operators connect via Bastion → jump VM → kubectl. Even a misconfigured NSG cannot expose the API server to the internet.\n\n**Azure CNI Overlay over classic Azure CNI** — Classic Azure CNI allocates pod IPs from the VNet subnet, exhausting a /22 at roughly 200 nodes. CNI Overlay keeps pod IPs in a separate overlay CIDR (/16) that does not consume VNet address space, while nodes retain VNet IPs for subnet-scoped network policies.\n\n**Workload Identity over pod-managed identity (NMI)** — MSI used an in-cluster DaemonSet (NMI) that intercepted all IMDS traffic on port 2579. An SSRF vulnerability in any pod could reach the IMDS endpoint and escalate to the node-level managed identity. Workload Identity issues federated OIDC tokens scoped to specific Kubernetes ServiceAccounts — a compromised pod can only access the identity explicitly assigned to its ServiceAccount.\n\n**Key Vault RBAC over access policies** — Access policies predate Azure RBAC and do not integrate with Conditional Access, PIM, or Azure Policy audit. `enable_rbac_authorization = true` means Key Vault access goes through the same role pipeline as every other Azure resource: auditable, PIM-eligible, and enforceable via Policy deny effects.\n\n---\n\n## Repository layout\n\n```\nlanding-zone/\n  management-groups/   Tenant root -\u003e Org -\u003e Platform / Workloads / Sandbox hierarchy\n  policies/            Deny KV public access, deny AKS privileged, allowed locations\n\nplatform/\n  hub-network/         Hub VNet + Firewall Premium + Bastion + spoke VNets + private DNS zones\n  monitoring/          Log Analytics workspace + Application Insights + action groups\n\nworkloads/\n  aks-platform/        Private AKS: CNI Overlay, Workload Identity, Defender, OMS, KVSP\n  api-platform/        APIM Internal VNet + Front Door Premium + Key Vault RBAC\n  messaging/           Service Bus Premium + private endpoint + DLQ alerts\n\npipelines/\n  github-actions/\n    terraform-plan.yml    OIDC auth, matrix plan per workspace, posts diff to PR\n    terraform-apply.yml   OIDC auth, serialized apply, production environment gate\n```\n\n---\n\n## Deployment order\n\n```\n1. landing-zone/management-groups    (once per tenant)\n2. landing-zone/policies             (once per tenant)\n3. platform/hub-network              (connectivity subscription)\n4. platform/monitoring               (management subscription)\n5. workloads/aks-platform            (workloads subscription)\n6. workloads/api-platform            (workloads subscription)\n7. workloads/messaging               (workloads subscription)\n```\n\nWorkloads depend on hub-network output (VNet IDs, subnet IDs) and monitoring output (Log Analytics workspace ID) via `terraform_remote_state`.\n\n---\n\n## GitHub Actions OIDC — no stored secrets\n\nThe service principal authenticates via federated identity credentials. No `AZURE_CLIENT_SECRET` is stored.\n\n```bash\n# Create app registration and service principal\naz ad app create --display-name \"PLACEHOLDER_SP_NAME\"\naz ad sp create --id \u003capp-id\u003e\n\n# Assign Contributor at subscription scope (tighten per workspace in prod)\naz role assignment create \\\n  --role Contributor \\\n  --assignee \u003csp-object-id\u003e \\\n  --scope /subscriptions/PLACEHOLDER_SUBSCRIPTION_ID\n\n# Federated credential for main branch pushes\naz ad app federated-credential create \\\n  --id \u003capp-id\u003e \\\n  --parameters '{\n    \"name\": \"github-main\",\n    \"issuer\": \"https://token.actions.githubusercontent.com\",\n    \"subject\": \"repo:PLACEHOLDER_GH_ORG/azure-infrastructure:ref:refs/heads/main\",\n    \"audiences\": [\"api://AzureADTokenExchange\"]\n  }'\n\n# Federated credential for PR plans\naz ad app federated-credential create \\\n  --id \u003capp-id\u003e \\\n  --parameters '{\n    \"name\": \"github-pr\",\n    \"issuer\": \"https://token.actions.githubusercontent.com\",\n    \"subject\": \"repo:PLACEHOLDER_GH_ORG/azure-infrastructure:pull_request\",\n    \"audiences\": [\"api://AzureADTokenExchange\"]\n  }'\n```\n\nSet `AZURE_CLIENT_ID` and `AZURE_TENANT_ID` as repository secrets. The pipeline uses `azure/login@v2` with `use-oidc: true`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgerardrecinto%2Fazure-infrastructure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgerardrecinto%2Fazure-infrastructure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgerardrecinto%2Fazure-infrastructure/lists"}