Skip to main content
RunBook Academy

TerraformI · Infrastructure as Code FoundationsFoundations

Infrastructure Ownership and the Configuration Repository

Foundation⏱ ~12 minbash

What you'll learn

  • Define IaC ownership as belonging to the team that maintains the code, not the team that requested the change
  • Describe the boundary between the platform team and the application team
  • Use CODEOWNERS to encode ownership at the file and module level
  • Articulate the operational cost of ambiguous ownership (Friday-night approvals, slow drift response)
  • Apply the change-review rules that follow from clear ownership

Prerequisites

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13

Not yet marked complete on this device.

A PR to enable VPC flow logs has sat unmerged for nine days. Three reviewers are tagged; none has approved. The PR is correct. The change is small. None of the three teams that own adjacent code wants to own the flow log code. The engineer who opened the PR is on holiday. The security team needs the change deployed before the audit. The PR sits.

This is ambiguous ownership in production. The code is in the repository. The repository has reviewers. No reviewer has responsibility. The audit clock runs while the PR ages.

The principle

The team that maintains the IaC code owns it. Not the team that requested the change. Not the team that runs the workload. Not “everyone.” The maintaining team.

Three corollaries.

The owning team approves changes. Reviewers from the owning team must approve PRs before they merge. Other teams may comment; they do not have veto.

The owning team is on the hook for the on-call. If the IaC breaks production, the maintaining team is the responder. The cost of an IaC outage is their cost, and that cost is what gives them authority over the code.

The owning team is named in CODEOWNERS. A reviewer group is not the same as ownership. Ownership is encoded.

Three rules:

Rule 1: The team that maintains the IaC owns it.
Rule 2: The owning team is named in CODEOWNERS at file scope.
Rule 3: The owning team's approval is required for merge.

If these three rules are violated, ownership is ambiguous.

The boundary between platform and application

Two teams typically share the IaC surface. The boundary matters because each team operates at a different abstraction.

  • Platform team. Owns the foundations: networking, identity, organisation-level guardrails, cross-cutting observability. Their IaC is mostly Terraform modules and root modules that compose those modules for the organisation.
  • Application team. Owns the workload: services, databases, queues, queues-and-storage that the application consumes. Their IaC calls the platform team’s modules.

The boundary in the repo:

terraform-modules/           # platform team
  modules/
    vpc/
    rds/
    eks/
  root/
    org-baseline/            # root config shared by every account

services/                    # application team
  payments/
    main.tf                  # composes platform modules
    variables.tf
    outputs.tf
  search/
    main.tf                  # composes platform modules

The platform team changes terraform-modules/. The application team changes services/payments/. A PR that crosses both is a shared change; it must be reviewed by both teams. A PR that touches services/payments/ is owned by the payments team.

CODEOWNERS as the encoding mechanism

The .github/CODEOWNERS file (or platform equivalent) is where ownership becomes enforceable. A CODEOWNERS entry says: changes to these paths require approval from these teams or individuals.

# Platform-owned
/terraform-modules/                  @platform-team
/terraform-modules/modules/vpc/     @platform-team/network
/root/                              @platform-team

# Application-owned
/services/payments/                 @payments-team
/services/search/                   @search-team
/services/auth/                     @auth-team

# Shared — both must approve
/services/*/iam.tf                  @platform-team @payments-team
/services/payments/network.tf       @payments-team @platform-team/network

Three properties.

  • File-scope. A path matches a pattern; the pattern may point at a single file, a directory, or a glob.
  • Multi-team. A pattern may list several owners. Each must approve. CI enforces.
  • Last-match wins. Patterns lower in the file override patterns above. The most specific pattern should be last.

The CODEOWNERS file is part of the IaC repo. It is reviewed on changes. A change that adds a new path also adds an owner.

Change-review rules that follow

Clear ownership produces specific review rules.

One-team approval for in-scope changes. A change under /services/payments/ requires @payments-team approval. The platform team may comment but cannot block.

Multi-team approval for boundary changes. A change that modifies terraform-modules/modules/vpc/ requires @platform-team/network approval. A change that consumes a new module requires both the application team and the platform team.

No “drive-by” approvals. A reviewer who is not the owner cannot approve. CODEOWNERS enforces this; the branch protection rule enforces the merge gate.

At least two approvals. A single approver is risky. A typo in an HCL expression is caught by a second reviewer faster than by CI.

Plan posted in the PR. A change that does not include a terraform plan artefact is incomplete. The plan is the change proposal. Without it, the reviewer is guessing.

The five rules together produce a PR culture in which proposals are specific, owners are legible, and the merge gate is mechanical.

The cost of ambiguous ownership

Five concrete failures follow from ambiguous ownership. They are the production signal that ownership needs to be made explicit.

  1. PRs that age. A correct change sits unmerged because no reviewer has authority. The audit clock runs; the engineer files a ticket; the ticket ages.
  2. Friday-night approvals. A reviewer who is “in the loop but not the owner” approves to unblock the engineer. The change is small; the review is fast; the apply breaks something the owner would have caught.
  3. Drift that nobody reconciles. Drift appears in the CI plan. No team owns the response. The drift is investigated eventually; meanwhile, the next plan conflates the drift with intended change.
  4. Module sprawl. Every team writes its own version of a VPC module. None of them has an owner. The platform team does not have authority to standardise.
  5. Runbook gaps. The IaC has an outage. The team that runs the workload pages. The team that maintains the code is asleep. The on-call rotation does not exist.

The cost is not theoretical. Each of these five is an incident class that recurs in unowned IaC repos.

How to validate ownership is in place

Three checks at PR review time.

# READ-ONLY: is a CODEOWNERS file present?
ls .github/CODEOWNERS

# READ-ONLY: is the CODEOWNERS file applied to the relevant path?
gh repo view --json customProperties   # depends on platform
# Or, for any repo:
gh api repos/{owner}/{repo}/contents/.github/CODEOWNERS --jq '.content' \
  | base64 -d | grep -i 'services/payments'
# A match means the path has an owner; absence means ambiguous.

# READ-ONLY: does the repo require CODEOWNERS approval?
gh api repos/{owner}/{repo}/rules/branches/main | jq '.required_pull_request_reviews'
# A non-null value means branch protection enforces the rule.

If any of the three returns “no”, ownership is incomplete. The gap has a known fix:

  • No CODEOWNERS → write one and adopt branch protection.
  • CODEOWNERS has gaps → fill them in a PR.
  • No branch protection → enable it; require the CODEOWNERS review.

Production failure modes

  1. CODEOWNERS pattern is too coarse. A single pattern matches the entire repo. One team owns everything; other teams have no authority. The platform team becomes the review bottleneck.
  2. CODEOWNERS points at individuals, not teams. A reviewer leaves the team. Their individual handle is in CODEOWNERS. New PRs cannot merge until the file is updated. The team forgot to update.
  3. Owning team changes are slow. The owning team’s CI backlog means their PRs take a week. The platform blocks the application team without anyone intending to.
  4. Boundary unclear in the repo. The directory structure does not separate platform from application. The CODEOWNERS is the only structural cue. The cue is not enforced in the tooling beyond the merge gate.
  5. “Drive-by” approvals hidden in CODEOWNERS. A team whose name appears on a pattern they do not maintain has been added to satisfy coverage, not ownership. The team approves without deep review.

Security implications

Ownership has a security dimension that complements CODEOWNERS.

  • Owner accountability. The owning team is the named accountable party for a security misconfiguration in their scope. A misconfigured security group is the platform team’s; a misconfigured IAM role in a service is the service team’s.
  • Separation of duties. A team that writes the configuration cannot be the only team that approves it. Multi-team review at the boundary is the separation-of-duties control.
  • Audit trail. CODEOWNERS plus branch protection gives the auditor a record: who approved, when, and what changed. Without ownership, the audit is a list of commits without a list of approvers.

The lesson: ownership is also a compliance posture. The audit will ask “who is accountable for this change?” A team who answers is auditable; a team who guesses is not.

Performance implications

  • PR latency scales with reviewer availability. A team with three reviewers can usually turn a PR in a day. A team with one reviewer creates a bottleneck. CODEOWNERS should point at teams, not individuals.
  • Module re-use reduces PR latency. If the platform team owns a tested VPC module, the application team does not need to write the VPC code. The PR is shorter. The review is faster.
  • Branch protection adds seconds to every merge. This is the cost of ownership. The seconds are not optional; they are the gate.

What comes next

This lesson closes the I-Foundations part of the course. The next part covers the Terraform architecture: the CLI, the providers, the resource graph, and the state model. Every later lesson builds on the foundations established here.

Verification

Five checks confirm that ownership is encoded, not assumed.

# READ-ONLY: CODEOWNERS exists at the conventional path.
test -f .github/CODEOWNERS && echo "yes" || echo "no"
# Or, for GitLab:
test -f .gitlab/CODEOWNERS && echo "yes" || echo "no"

# READ-ONLY: every IaC path has an owner.
grep -E '^/' .github/CODEOWNERS | sort -u
# A path that has no match above it has no owner.

# READ-ONLY: branch protection enforces CODEOWNERS review.
gh api repos/{owner}/{repo}/rules/branches/main \
  | jq '.required_pull_request_reviews.require_code_owner_reviews'
# A `true` value means CODEOWNERS approval is required to merge.

# READ-ONLY: at least two approvals per PR.
gh api repos/{owner}/{repo}/rules/branches/main \
  | jq '.required_pull_request_reviews.required_approving_review_count'
# A count of 2 or higher means single-reviewer merges are
# impossible.

# READ-ONLY: directory layout reinforces the platform /
# application boundary.
find . -maxdepth 3 -type d | grep -E '(terraform-modules|services)'

If any check returns “no” or “false,” ownership is in practice ambiguous. The fix is structural: a CODEOWNERS change, a branch-protection change, or a directory move. Each fix is a small PR; the absence of these PRs is what produces the aged-PR and Friday-night-approval failures the lesson warns against.

Knowledge check · 7 questions

  1. Q1. Who owns IaC for a service?

  2. Q2. What does the CODEOWNERS file do?

  3. Q3. IaC for a service needs one named owning team, because 'everyone's responsibility' is ambiguous ownership.

  4. Q4. Which of the following are signals that IaC ownership is ambiguous? (Select all that apply.)

  5. Q5. Where does the boundary between the platform team and the application team sit in the IaC repo?

  6. Q6. The platform team writes the VPC modules, but the application team authors and reviews every change to a service. The service team is the bottleneck for every PR. What ownership change addresses this?

  7. Q7. An IaC PR has sat unmerged for two weeks because no reviewer claims ownership. What is the first step?

Passing score: 75%. Answers are checked in this browser.