Protected Branches & Review Requirements for Infra-Touching PRs
A pull request that changes an IAM policy or a CloudFormation template can alter what a running application is allowed to do in AWS, not just how it behaves. GitHub's branch protection features let you gate merges into a shared branch with rules that apply to everyone, including required reviews, required status checks, and a block on force-pushing history away. This page covers how to configure those rules and route the strictest ones specifically at infra-touching changes.
A protected branch in GitHub (Settings -> Branches -> Branch protection rules) is a real feature that enforces conditions before a PR can merge into it. The core options:
Require a pull request before merging - no one, including admins if you choose, can push directly to the branch.
Require approvals - a minimum number of reviewers must approve, and you can require re-approval if new commits are pushed after an approval.
Require status checks to pass - CI jobs (tests, linting, a policy scanner) must succeed before the merge button is enabled.
Require conversation resolution - open review comments must be marked resolved.
Block force pushes and branch deletion - git push --force and deleting the branch are both rejected on a protected branch.
# Configure via the GitHub CLI (or the same settings in the web UI)gh api -X PUT repos/your-org/your-repo/branches/main/protection \ -f required_status_checks[strict]=true \ -f required_status_checks[contexts][]=ci/tests \ -f required_status_checks[contexts][]=ci/policy-lint \ -f enforce_admins=true \ -f required_pull_request_reviews[required_approving_review_count]=1 \ -f required_pull_request_reviews[require_code_owner_reviews]=true \ -f restrictions=null \ -F allow_force_pushes=false \ -F allow_deletions=false
require_code_owner_reviews is the setting that connects branch protection to a CODEOWNERS file - if a matching entry exists for the changed paths, that reviewer's approval becomes mandatory, not just requested.
Most repos don't need every PR held to the same bar. A CODEOWNERS file lets you require a specific reviewer only for the paths that matter most:
# .github/CODEOWNERS# Default: no mandatory owner*# Infra and IAM changes require the platform team/infra/ @your-org/platform-team/iam/ @your-org/platform-team*.tf @your-org/platform-teamcloudformation/** @your-org/platform-team
With require_code_owner_reviews enabled on the protected branch, a PR touching any of these paths cannot merge without an approval from @your-org/platform-team, regardless of who else has already approved. PRs that touch only application code are unaffected and merge under the general review requirement.
A required status check can add an automated layer ahead of human review - for example, a job that runs a least-privilege policy linter against any changed IAM policy document and fails the check if it detects an overly broad Action or Resource wildcard. This catches obvious problems before a reviewer even opens the diff.
Force-pushing rewrites a branch's history, which is sometimes legitimate (squashing commits before merge) but dangerous on a shared branch: it can silently drop a commit another reviewer already approved, or erase the audit trail of exactly which change was reviewed and when. For infra-touching work, that audit trail matters beyond convenience - if an incident review later needs to establish exactly what IAM policy was live at a given time, a rewritten history undermines that. Blocking force-push and deletion on the protected branch removes this risk entirely, at no cost to normal development.
Is branch protection a real GitHub feature or something you build?
It's a native GitHub feature, configured per-branch under repository settings or via the GitHub API/CLI. No third-party tooling is required for the core rules covered here.
Does CODEOWNERS work without branch protection enabled?
CODEOWNERS entries only become a hard requirement when the protected branch rule require_code_owner_reviews is turned on. Without it, GitHub still auto-requests the matching reviewer, but their approval isn't mandatory to merge.
Should admins be exempt from these rules?
Generally no for infra-touching branches. Setting enforce_admins=true means even repository admins go through the same review and status-check requirements, which closes the most common bypass path.
What's a good required status check for IAM-heavy repos?
A policy-linting job that scans changed IAM policy documents for overly permissive Action or Resource wildcards, run as CI and wired in as a required status check on the protected branch.
Why block force-push specifically for infra-touching branches?
Because a rewritten history can erase the record of exactly which policy or infra change was reviewed and approved, which matters if an incident review later needs to reconstruct what was live at a given time.
Can these rules slow down routine, non-infra PRs?
Not if CODEOWNERS paths are scoped narrowly to infra directories and file types. Application-only PRs skip the extra reviewer requirement entirely and merge under the general rule.