The Junior Climb hardcover mockup
Secure SDLC for IaC

Secure SDLC for IaC

A README said “all changes through Terraform.” A platform engineer still had Owner and a habit. The insecure storage account arrived from a click, not from a PR. Your SDLC never saw it.

Secure delivery for a landing zone means the pipeline is the only privileged path for infrastructure. Code scanners on the app repo do not cover the hub firewall PR that never happened.

This is article six of Series 3. Deployment Model split platform and workload identities. Threat modeling put the pipeline on the asset list. This article is the Tuesday path. Break-glass covers emergencies. Emergencies that never return to git are permanent drift.

Identity of the pipeline

OIDC federation to Azure. No service principal passwords in variable groups for routine work. Separate identities for platform vs workload. Least privilege: the workload identity cannot edit hub DNS, firewall policy, or management group policy assignments.

Concrete checks:

  • Federated credential subject matches the repo and environment
  • Platform identity sits at the management group; workload identity is Contributor (or less) in the spoke subscription
  • No second “legacy” client secret still valid on the same app registration
  • Humans who can approve prod applies use PIM for any Azure portal investigation role; they do not share the pipeline’s credential

If a developer can download a secret and apply from a laptop, you have two control planes. Git is optional theater.

Branch protection that matches blast radius

Platform repo rules should be stricter than a typical app repo. The blast radius is the estate.

Minimum bar:

  • Required reviewers via CODEOWNERS for hub, policy, and identity paths
  • No admin bypass except a documented break-glass procedure with logging
  • Status checks that must be green before merge
  • Signed commits if your organization already mandates them for production paths
  • Protected environments for prod apply with required reviewers

Status checks that matter for IaC:

  1. terraform fmt -check and terraform validate
  2. terraform plan against the correct remote state (no “plan skipped” green checks)
  3. Policy-as-code scanners (Checkov, tfsec, OPA/Conftest, or Microsoft’s templates you actually maintain)
  4. Secret scanning on the diff
  5. A human review by someone who understands whether the plan touches the hub

A green pipeline that never ran plan against real state is how Friday clicks return with CI cosmetics.

Plan is a gate. Apply is a privilege.

Every PR shows a plan. Reviewers read the plan, not only the HCL.

Operating model that holds up:

  • Nonprod may auto-apply on merge to the main branch for workload spokes, if the identity and module are solid
  • Prod workload apply requires environment approval
  • Platform hub / policy / MG apply always requires approval from a platform owner who is not the author
  • Never auto-apply platform hub changes late Friday without a human who will stay online

Apply is not the change calendar. The ticket, the PR, and the approval are the calendar. Auditors ask for the PR link. Article seven expects you to have it.

Policy-as-code is not Azure Policy

Azure Policy enforces in ARM after someone tries to create a resource. Policy-as-code in CI catches obvious mistakes before merge: public storage, open NSG rules, missing encryption flags, * in security rules, hardcoded secrets.

Use both:

  • CI scanners fail the PR early
  • Azure Policy Deny catches portal clicks and modules that bypassed a scanner rule
  • Defender recommendations catch residual drift after the fact

Do not expect Checkov to replace Policy at the management group. Do not expect Deny to teach a developer why their PR was wrong as fast as a CI annotation.

Secrets in PRs and state

Scanners for high-entropy strings on every PR. Pre-commit hooks for the engineers who will run them. Remote state in a locked storage account with platform-controlled access. State may contain sensitive values; treat the storage account and its CMK as control-plane assets from article one.

If a secret lands in git history:

  1. Rotate the secret immediately
  2. Purge or rewrite history only with a documented procedure and org approval
  3. Treat “we deleted the line” as incomplete

Key Vault references and OIDC exist so app secrets do not need to live in pipelines. Secrets at scale already covered the vault patterns. SDLC is how you stop inventing new places to paste strings.

Module supply chain

Pin module versions. Read changelogs. Upstream Azure Verified Modules / CAF / ALZ bumps go to nonprod first. source = "...ref=main" on external modules is not an SDLC. It is a standing surprise.

Internal module rules:

  • Versioned tags for anything prod consumes
  • CODEOWNERS on module roots
  • Changelog entry for behavior that affects security defaults (public access flags, RBAC roles, firewall rules)
  • Canary subscription or management group before wide rollout

A module default that reintroduces public blob access will create Defender noise across every new spoke. That is an SDLC bug, not a “teams need training” problem.

Break-glass that returns to git

Production is down. The pipeline is blocked. Break-glass may allow a portal change or a local apply with a PIM-elevated identity.

Clock starts:

  1. Incident ticket opened with commander named
  2. Change performed with a scribe noting resource IDs and timestamps
  3. Evidence preserved (platform IR covers the security-shaped version)
  4. Follow-up PR opened inside the documented window (same day or next business day for platform)
  5. State refreshed so the next plan does not revert the fix or ignore it forever

An emergency that never becomes a PR is permanent drift. Permanent drift becomes the next outage when someone applies old state.

What to ban in definition of done

Ship a short definition of done for platform changes. Paste it in the PR template.

  • [ ] No standing human Owner on the management group required for this change
  • [ ] Plan reviewed; no unexpected destroys in hub or shared DNS
  • [ ] Policy and IAM diffs called out explicitly in the PR body
  • [ ] Secrets not added to git or pipeline variables when Key Vault or federation works
  • [ ] Ticket links the PR and the apply run ID
  • [ ] If break-glass was used, follow-up PR linked before the ticket closes

Auditors ask for that PR link. Security asks whether Owner is still standing. On-call asks whether Friday’s hotfix is in state. One checklist answers all three when people use it.

OIDC and environment protection as code

Store federated credential configuration in Terraform or documented platform modules. Review changes to subjects the same way you review role assignments. A new subject that matches repo:org/:ref:refs/heads/ is a standing surprise.

GitHub Environments or Azure DevOps environments with required reviewers for prod apply are part of the control. Screenshot settings once for the audit pack. Prefer exporting configuration as code so the screenshot matches reality next quarter.

Never commit the Azure client secret “just for local plan.” Use az login with PIM for humans, or a short-lived federated path. Local secrets become pipeline secrets. Pipeline secrets become paste incidents.

Separating platform and workload pipelines in practice

Two repos or two strongly separated paths. Two identities. Two sets of environments.

Workload teams get a published spoke module and examples. They do not fork the hub. They do not receive a PAT that can push to the platform repo. If the only way to add a private DNS zone is to give an app team rights on the platform repo, your product backlog is missing a catalog item. Series 2’s contract said the same thing in workload language. SDLC enforces it with branch permissions.

Metrics that show the SDLC is real

Track monthly:

  • Percentage of prod platform changes with a PR link on the ticket
  • Count of portal-only changes detected by ARG drift queries or terraform plan surprises
  • Age of break-glass follow-up PRs still open
  • Scanner findings reopened because module defaults regressed

If portal-only changes are common, README slogans are not working. Fix identity and Deny policies before you buy another DevSecOps tool.

How this feeds audit and IR

Article seven wants sample PRs with plan and approval. Article eight wants to freeze applies when the pipeline identity might be compromised. Both assume you know what “normal apply” looks like: OIDC subject, environment approval, git SHA.

If normal is a laptop with Owner, freeze does nothing useful. You are already frozen in the wrong posture.

Drift detection as a scheduled job

Even with good PR habits, portal clicks happen. Schedule a plan against platform state on a read-only identity, or run ARG drift queries for resources that should only exist via module (missing tags, unexpected public IPs, NSGs edited outside Terraform).

When drift appears:

  1. Open a ticket
  2. Decide: absorb into Terraform, or revert in Azure
  3. Never leave drift as “known” without an owner and date

Terraform refresh-only or plan-only pipelines are evidence that the SDLC is watching. They are also how you catch the helpful engineer who opened RDP “for a vendor.”

CODEOWNERS that match real expertise

Put hub firewall, private DNS, policy initiatives, and platform identity paths under owners who actually review those plans. A rubber-stamp reviewer on every file trains the team to ignore CODEOWNERS.

Split ownership:

  • /modules/hub/ and firewall policy: network platform owners
  • /modules/policy/ : governance owners
  • /modules/identity/ or federated credential templates: identity owners
  • Spoke examples: can be broader

If the only owner is one person, you have a holiday risk. Add a backup. Document who covers when.

Nonprod is not a free-for-all

Sandbox management groups can allow more portal use. They still need budgets, and they should not share the hub identity or the platform vault. SDLC looseness in nonprod that uses the prod OIDC app registration is how nonprod becomes a path to prod.

Separate app registrations or federated subjects per environment. Separate state. Separate approvals for prod. The Deployment Model article drew that line. Secure SDLC is where you enforce it in the repo settings, not only in a diagram.

Next: Audit Season Runbook. The questions they ask first, and the artifacts that should already be warm.

Randy Bordeaux

Azure Trainer | Veteran | Engineer

Helping IT professionals grow through cloud, leadership, and shared knowledge.


Discover more from Randy Bordeaux

Subscribe to get the latest posts sent to your email.