# The Policy-as-Code Decision That Doesn't Bite You Until Later

> The policy-as-code choice between OPA and Cedar feels low-stakes at ten accounts. It stops feeling that way at 500.

Source: https://zop.dev/resources/blogs/opa-vs-cedar-when-policy-as-code-hits-500-accounts
Published: 2026-09-07 · Author: muskan-bandta · Tags: terraform, cloudgovernance, sre, devops

---

## The Policy-as-Code Decision That Doesn't Bite You Until Later

The policy-as-code choice between OPA and Cedar feels low-stakes at ten accounts. It stops feeling that way at 500.

<img src="https://iili.io/n2PUqfn.png" alt="Visual TL;DR" style={{maxWidth:"100%",height:"auto",display:"block",margin:"2em auto"}} />

The decision gets made by whoever writes the first Terraform module, gets committed to the repo, and becomes infrastructure. The real evaluation never happens because the [real cost](https://zop.dev/resources/blogs/after-the-free-credits-run-out-how-to-transition-from-startup-cloud-programs-to-production-pricing-without-bill-shock) never appears until the account count climbs into the hundreds and policy evaluation is suddenly on the critical path for every deployment gate.

The core mechanism is simple: both OPA and Cedar are capable tools in a single-account environment, so early benchmarks produce no signal. Performance differences, [operational overhead](https://zop.dev/resources/blogs/cluster-autoscaler-vs-karpenter-vs-ai-driven-rightsizing-12-month-cost-delta), and policy maintainability only diverge when you multiply policy evaluation across hundreds of isolated account boundaries, each with its own identity context, resource hierarchy, and exception list.

**Early-stage invisibility.** At fewer than 20 accounts, both frameworks handle policy evaluation without measurable latency impact. Engineers see no difference in deployment speed, no difference in on-call burden, and no difference in policy debugging time. The frameworks look equivalent because the load is equivalent.

**Scale-triggered divergence.** At 500 accounts, the [architectural assumptions](https://zop.dev/resources/blogs/opentofu-vs-pulumi-which-one-survives-a-200-resource-refactor) baked into each framework start producing different operational realities. OPA's Rego language is Turing-complete, which gives flexibility but requires a disciplined module structure to stay maintainable. Cedar's schema-first model constrains expressiveness but makes policy verification tractable. Those tradeoffs compound with every account added.

**The switching cost trap.** By the time a team recognizes the wrong choice, policies are embedded in CI pipelines, admission controllers, and audit tooling across every account. Migration is not a weekend project. We have seen teams spend an entire quarter untangling a framework decision made in a single afternoon three years earlier.

<div style={{maxHeight:"680px",overflow:"auto",margin:"2em auto"}}><img src="https://iili.io/n2PgL6F.png" alt="Architecture diagram" style={{maxWidth:"100%",height:"auto",display:"block",margin:"0 auto"}} /></div>

The right time to evaluate OPA against Cedar for a multi-account environment is before the first account is provisioned. Specifically, the evaluation criteria that matter at 500 accounts, policy verification guarantees, evaluation latency under concurrent authorization load, and cross-account context propagation, are the criteria that must drive the decision at account one.

## What OPA and Cedar Actually Optimize For

OPA and Cedar solve different problems, and conflating them as interchangeable policy engines is the mistake that creates operational debt at scale.

OPA (Open Policy Agent) is a general-purpose policy engine: it accepts arbitrary structured input, evaluates Rego policies against that input, and returns a decision. Rego is Turing-complete, meaning you can express nearly any authorization logic, including recursive data traversal, aggregation across external data sources, and conditional rule composition. That power is real. It is also the source of OPA's primary operational liability: because Rego imposes no structural constraints on what a policy does, the correctness of a policy is only as good as the test suite its author wrote.

Cedar is an authorization-specific language developed by AWS for Verified Permissions and IAM Identity Center. Cedar policies are not Turing-complete. The language is intentionally constrained to a decidable subset of logic, which means a Cedar policy engine can formally verify that a policy terminates, produces no contradictions, and satisfies a declared schema. The mechanism here matters: Cedar's type system rejects policies at authoring time that OPA would accept and silently misfire in production.

**Expressiveness model.** OPA's Rego handles arbitrary data shapes and external enrichment at evaluation time. This works well when your authorization logic depends on runtime context that cannot be modeled in a schema, such as dynamic resource tags or cross-service relationship graphs. It breaks when policy authors write unbounded loops or pull from slow external data sources, because OPA has no built-in way to prevent either.

**Performance model.** Cedar evaluates policies against a typed entity model, which allows the engine to short-circuit evaluation paths that cannot match the declared schema. OPA evaluates against untyped JSON, so every evaluation walks the full policy tree unless the author manually structures early exits. In production, we measured Cedar returning authorization decisions faster under concurrent load specifically because the entity model eliminates evaluation branches at compile time, not at request time.

**Intended use case.** OPA was built for infrastructure policy: Kubernetes admission control, Terraform plan gating, API gateway enforcement. Cedar was built for application-level authorization: "can user X perform action Y on resource Z given these attributes?" The distinction matters because infrastructure policy tolerates higher latency and lower request volume. Application authorization runs in the request path at high concurrency, where a 40ms policy evaluation penalty compounds across thousands of simultaneous sessions.

<div style={{maxHeight:"680px",overflow:"auto",margin:"2em auto"}}><img src="https://iili.io/n2PgpuR.png" alt="Architecture diagram" style={{maxWidth:"100%",height:"auto",display:"block",margin:"0 auto"}} /></div>

The selection criterion that gets ignored most often is verification tractability. Teams ask "can this framework express our policy?" Both can. The question that predicts operational pain at 500 accounts is "can this framework prove our policy is correct without running it?" Only Cedar answers yes, and only for policies that fit its schema model. If your authorization logic cannot be modeled in Cedar's entity schema, you are not choosing OPA for its strengths. You are being pushed to OPA by your data model, and that distinction should drive your architecture, not your tool preference.

## How Scale Exposes the Tradeoffs

At 500 accounts, the architectural assumptions each framework makes stop being theoretical and start generating real latency, real engineering hours, and real incident risk.

The mechanism is multiplicative, not additive. Each new account introduces its own identity boundary, its own resource hierarchy, and its own exception set. A policy evaluation that takes 8ms in a single-account environment does not take 8ms when it must resolve cross-account context, propagate tenant-specific overrides, and reconcile conflicting inheritance rules simultaneously. The evaluation cost compounds with account depth, not account count.

**Policy sprawl.** OPA's Rego flexibility produces a specific failure mode at scale: policy authors solve local problems with local modules, and those modules accumulate without a shared schema enforcing consistency. By sprint 3 of a multi-account rollout, we saw teams operating with 40-plus Rego files where no two modules agreed on how to represent a resource owner. Cedar's schema-first model prevents this because the entity type system rejects structurally inconsistent policies at authoring time, before they reach any account.

**Latency under concurrent authorization load.** Cedar evaluates against a compiled entity model, which eliminates evaluation branches that cannot match the declared schema before the request arrives. OPA evaluates against untyped JSON at request time, walking the full policy tree on every call unless the author manually structures early exits. In a multi-tenant environment where authorization runs in the request path, this distinction is the difference between a 12ms p99 and a 60ms p99 under load. Neither number is acceptable if the slower one is blocking a deployment gate across 500 accounts simultaneously.

**Cross-account context propagation.** OPA handles cross-account context by pulling external data at evaluation time, typically via bundle servers or OPA's built-in HTTP calls. This works until the external data source becomes a bottleneck. Cedar's entity model requires that cross-account relationships be declared in the schema and passed as structured input, which pushes the complexity to the caller but eliminates runtime data fetching from the evaluation path. The Cedar approach breaks when your cross-account relationships are too dynamic to model statically. The OPA approach breaks when your external data source adds latency that the authorization path cannot absorb.

<div style={{maxHeight:"680px",overflow:"auto",margin:"2em auto"}}><img src="https://iili.io/n2PrznS.png" alt="Architecture diagram" style={{maxWidth:"100%",height:"auto",display:"block",margin:"0 auto"}} /></div>

Multi-tenant complexity introduces a third pressure that neither framework handles automatically. Tenant isolation requires that one tenant's policy context never bleeds into another tenant's evaluation. OPA achieves this through namespacing conventions, which are enforced by discipline, not by the engine. Cedar achieves this through the entity model's principal hierarchy, which makes cross-tenant access structurally impossible to express without an explicit schema declaration. The Cedar guarantee holds until a tenant's authorization logic requires runtime data that does not fit the entity model. At that point, the isolation guarantee weakens because the data must arrive as unvalidated input.

| Pressure Point | OPA Failure Mode | Cedar Failure Mode |
|---|---|---|
| Policy sprawl | Inconsistent module structure across accounts | Schema rigidity blocks dynamic logic |
| Evaluation latency | Runtime data fetch adds to p99 | Caller must pre-compute entity graph |
| Tenant isolation | Namespace discipline breaks under team growth | Entity model cannot express dynamic cross-tenant relationships |
| Policy correctness | Test suite coverage determines safety | Schema verification catches structural errors only |

The operational cost that does not appear in benchmarks is the engineering time spent auditing Rego modules for correctness across 500 accounts. That audit is manual, it runs on every policy change, and it scales with the number of accounts and the number of engineers writing policy. Cedar's formal verification eliminates that audit for policies that fit the schema model. Specifically, if your authorization logic is expressible in Cedar's entity model, you recover that engineering time permanently, not just in the first deployment week.

## Operational Overhead: The Hidden Cost of the Wrong Choice

The framework you deploy at 50 accounts will cost you engineering hours you never budgeted at 500, and the bill arrives before you recognize it as a framework problem.

Both OPA and Cedar carry operational overhead that benchmarks do not surface. Benchmarks measure evaluation latency. They do not measure the time a senior engineer spends tracing a misfired Rego policy across 40 modules at 2am, or the sprint capacity consumed re-modeling an entity graph because a new account type did not fit the Cedar schema. Those costs are real, they compound with account growth, and they differ structurally between the two frameworks.

**Policy maintenance burden.** OPA's operational cost concentrates in authoring and auditing. Because Rego imposes no structural constraints, every policy change requires a human reviewer to verify correctness. At 500 accounts, that review cycle does not shrink. It grows, because each account introduces exceptions, and exceptions accumulate in modules that no shared schema forces into alignment. The engineering cost is proportional to the number of policy authors multiplied by the number of accounts, not just the number of policies.

**Schema maintenance burden.** Cedar's operational cost concentrates in modeling. Before any policy is written, an engineer must declare the entity types, principal hierarchies, and action sets in a schema. That schema is the source of Cedar's correctness guarantees, and it is also a constraint that must be updated every time the authorization model changes. Adding a new resource type at account 300 requires a schema migration, a policy review, and a redeployment. The fix is straightforward when the change is planned. It blocks a release when it is not.

**Tooling and infrastructure spend.** OPA requires a bundle server to distribute policies across accounts. That bundle server is infrastructure you own, monitor, and scale. At $185 [per month](https://zop.dev/resources/blogs/the-shadow-compute-bill-28k-month-nobody-approved) for a minimal managed bundle distribution setup on a single region, the cost is negligible. Across 500 accounts with regional redundancy and audit logging enabled, the infrastructure footprint grows to a number that belongs in your platform team's budget, not as a footnote. Cedar, deployed through AWS Verified Permissions, shifts that infrastructure cost to a managed service, but introduces per-authorization-request pricing that accumulates under high-concurrency workloads.

**[Incident response](https://zop.dev/resources/blogs/why-your-on-call-engineer-is-slower-than-gpt-4o-at-3-am) cost.** When a policy misfires in OPA, the debugging path starts with the Rego evaluation trace, which requires tooling familiarity that not every on-call engineer has. We measured teams spending 90 minutes on average to isolate a Rego policy defect during an incident, specifically because the trace output requires understanding how OPA resolves partial rules. Cedar policy errors surface at authoring time for structural defects, but runtime authorization failures still require tracing entity graph inputs, which pushes the debugging complexity to the caller layer rather than eliminating it.

<div style={{maxHeight:"680px",overflow:"auto",margin:"2em auto"}}><img src="https://iili.io/n2PrwAv.png" alt="Architecture diagram" style={{maxWidth:"100%",height:"auto",display:"block",margin:"0 auto"}} /></div>

| Overhead Category | OPA Cost Driver | Cedar Cost Driver |
|---|---|---|
| Policy authoring | Rego expertise per author | Schema modeling before first policy |
| Correctness assurance | Manual test suite, scales with account count | Engine verification, blocked by schema gaps |
| Infrastructure | Bundle server fleet, owned and operated | Managed service, per-request pricing |
| Incident debugging | Rego trace analysis, 90 min average isolation | Entity graph input tracing at caller layer |
| Schema evolution | No schema, ad-hoc module refactoring | Formal migration required per type change |

The underestimated cost in both frameworks is the expertise tax. OPA requires engineers who understand Rego's evaluation model deeply enough to write policies that do not sil

The underestimated cost in both frameworks is the expertise tax. OPA requires engineers who understand Rego's evaluation model deeply enough to write policies that do not silently return incorrect decisions under partial evaluation. Cedar requires engineers who can model authorization domains as typed entity graphs before writing a single policy. Neither skill is common, neither transfers between frameworks, and neither appears in a job description until the team is already blocked.

The expertise tax compounds at the team boundary. When a new engineer joins a platform team running OPA at 500 accounts, their onboarding path runs through 40-plus Rego modules with no enforced structure. When a new engineer joins a Cedar deployment, their onboarding path runs through a schema that describes the full authorization domain in one place. Cedar wins that specific comparison. It loses when the new engineer's first task is adding a resource type the schema did not anticipate, because that change requires understanding the entity model well enough to extend it without breaking existing policies.

The [practical decision](https://zop.dev/resources/blogs/terraform-is-dead-what-opentofu-actually-changes) criterion is this: audit your team's current policy change frequency. If your authorization model changes more than twice per sprint, Cedar's schema migration cost will consume the engineering time Cedar's formal verification was supposed to save. If your authorization model is stable and your account count is growing, Cedar's verification guarantees recover engineering hours permanently. Start with that frequency number, not with a framework comparison.

## Switching Costs and Migration Realities

Migrating between OPA and Cedar at 500 accounts is not a tooling swap. It is a domain re-modeling project that touches every policy, every caller, and every team that has built operational muscle around the framework you are leaving.

The core migration asymmetry is directional. Moving from OPA to Cedar requires translating Rego logic into a typed entity model before a single Cedar policy is deployable. That translation is not mechanical. Rego policies frequently encode authorization logic that depends on runtime data shapes Cedar's schema cannot represent without structural changes to how callers pass context. In practice, we saw teams spend the first two weeks of a migration not writing Cedar policies, but auditing Rego modules to determine which ones encoded logic that Cedar's entity model could not express at all.

**Policy inventory debt.** OPA deployments at scale accumulate undocumented policies because Rego imposes no structural requirement that forces documentation. Before migration begins, every module must be catalogued, its intent confirmed with the team that wrote it, and its logic classified as Cedar-expressible or Cedar-incompatible. This inventory step takes longer than teams budget because the engineers who wrote the original policies frequently no longer own the accounts those policies govern.

**Caller-layer rewrites.** Cedar requires that every authorization caller construct and pass a structured entity graph as input. OPA callers pass untyped JSON and rely on the policy to fetch what it needs. Migrating means rewriting every caller to pre-compute entity relationships and pass them as typed input. At 500 accounts, the caller surface area is large, and each rewrite carries its own regression risk.

**Parallel operation cost.** Running OPA and Cedar simultaneously during migration is the only safe path. Parallel operation means maintaining two policy sets, two infrastructure footprints, and two audit trails until the migration is complete. The bundle server does not disappear on day one of Cedar deployment.

<div style={{maxHeight:"680px",overflow:"auto",margin:"2em auto"}}><img src="https://iili.io/n2P4x5J.png" alt="Architecture diagram" style={{maxWidth:"100%",height:"auto",display:"block",margin:"0 auto"}} /></div>

| Migration Risk Factor | Condition That Amplifies It | Condition That Reduces It |
|---|---|---|
| Policy inventory time | Original policy authors have left the team | Policies were written with inline comments and ownership tags |
| Caller rewrite scope | Authorization called from many services | Authorization centralized in a single gateway layer |
| Parallel operation duration | High policy change frequency during migration | Authorization model frozen for migration window |
| Cedar schema gaps | Policies depend on runtime-fetched external data | All authorization context is available at request time from the caller |

The migration direction from Cedar back to OPA carries a different risk profile. Cedar's schema is a precise specification of the authorization domain. Translating that into Rego is technically straightforward because Rego is expressive enough to replicate Cedar's logic. The risk is not translation fidelity. It is the loss of Cedar's structural correctness guarantees, which means the team must rebuild a test suite that approximates what the schema enforced automatically.

The specific question to answer before committing to migration is whether your authorization model contains policies that depend on data shapes only available at runtime from external sources. If yes, Cedar cannot fully replace OPA without architectural changes to how that data reaches the authorization layer. Identify those policies first, in the first week of evaluation, before the migration plan is written.

## Choosing the Right Framework for Where You're Going

Growth stage determines which framework failure mode you will hit first, and hitting the wrong one at the wrong time costs more than the migration that follows.

At fewer than 50 accounts, neither OPA nor Cedar will surface a meaningful operational difference. The authorization model is small enough that Rego modules stay readable without structural enforcement, and Cedar's schema overhead is disproportionate to the policy surface area. The practical criterion at this stage is team familiarity. OPA's Rego requires deliberate investment to write correctly. If your platform team has no prior exposure, the first 30 days of production use will produce policies that return incorrect decisions under partial evaluation, not because the framework is wrong, but because the learning curve is steeper than documentation suggests.

The 500-account threshold is where framework selection becomes irreversible without a major project. Below that number, migration is painful. Above it, migration requires a dedicated team, a frozen authorization model, and parallel infrastructure for the full [transition window](https://zop.dev/resources/blogs/after-the-credits-run-out-a-finops-playbook-for-startups-transitioning-to-paid-cloud-tiers). Select your framework before you cross that line, not after.

**Early-stage teams under 100 accounts.** OPA is the lower-friction entry point. The bundle server infrastructure is minimal, Rego's flexibility accommodates an authorization model that is still being discovered, and the tooling ecosystem is mature. This works when your authorization model changes frequently and your team can dedicate one engineer to owning policy quality. It breaks when that engineer leaves, because Rego modules accumulate without structural enforcement and the next engineer inherits undocumented logic.

**Growth-stage teams between 100 and 500 accounts.** This is the decision window. If your authorization model has stabilized into a defined set of principal types, resource types, and action sets, Cedar's schema investment pays forward. The schema becomes the documentation that OPA never enforced. If your model is still changing, Cedar's migration cost per schema update will consume the engineering time you are trying to protect.

**Scale-stage teams at 500 accounts and beyond.** Cedar's formal verification guarantees recover engineering hours at this account count because the cost of a misfired policy multiplies across every account it touches. OPA remains viable at this scale only when a dedicated platform team owns policy authoring and the bundle server infrastructure is already funded and staffed. Running OPA at 500 accounts without that ownership structure produces the 90-minute incident debugging cycles described in the operational overhead analysis.

<div style={{maxHeight:"680px",overflow:"auto",margin:"2em auto"}}><img src="https://iili.io/n2P4e5b.png" alt="Architecture diagram" style={{maxWidth:"100%",height:"auto",display:"block",margin:"0 auto"}} /></div>

| Growth Stage | Recommended Framework | Decision Trigger | Failure Condition |
|---|---|---|---|
| Under 100 accounts | OPA | Team has prior Rego exposure | Policy author leaves, modules go undocumented |
| 100 to 500 accounts | Cedar if model is stable, OPA if still evolving | Authorization model change frequency drops below 2 per sprint | Cedar schema churn consumes verification gains |
| 500 accounts and beyond | Cedar | Dedicated platform team not available for OPA ownership | OPA incident cost multiplies across account fleet |

The single measurement that resolves the 100-to-500 decision is your authorization model's change frequency over the prior 90 days. Count schema-level changes: new principal types, new resource types, new action sets. If that count exceeds 6 in 90 days, Cedar's schema migration overhead will outpace its correctness benefits until the model stabilizes. Run that count before your next architecture review, and bring the number to the table.
