Plan validation rules run OPA (Open Policy Agent) policies against a deployment plan — the diff between what is currently deployed and what a new version proposes — and report any violations back to you.Documentation Index
Fetch the complete documentation index at: https://docs.ctrlplane.dev/llms.txt
Use this file to discover all available pages before exploring further.
Plan validation is non-blocking. It does not gate or stop a
deployment. It exists to give you a preview: when a new version is planned,
you can see whether the proposed changes pass your policies before you decide
to roll them out. Violations are surfaced on the deployment’s GitHub check and
in the ctrlplane UI, but the deployment is never automatically held back.
Overview
A plan validation rule is a normal policy rule, so it shares the policy’s CELselector that decides which release targets it applies to. The difference
is that instead of approvals or windows, the rule carries a snippet of Rego
that inspects the computed plan.
When does it run?
Validation runs after an agent finishes computing a plan. Only agents that produce a plan are evaluated:- ArgoCD — the rendered manifest diff
- Terraform Cloud — the speculative plan output
Why use plan validation?
- Preview before you ship — confirm a proposed change looks right before promoting it.
- Catch risky diffs — flag deletions, replacements, or scaling changes in a Terraform plan.
- Guard against secrets or forbidden values in rendered manifests.
- Codify review checklists that your team would otherwise eyeball by hand.
Configuration
- Terraform
- API
Properties
Human-readable rule name. Shown in the check output to identify which rule
produced a violation.
Optional human-readable explanation of what the rule does.
Rego v1 source code. Must define a
deny rule set following the
Conftest convention
(deny contains msg if { ... }). Each member of the deny set becomes one
violation message. An empty deny set means the plan passed.Writing the Rego
Policies must be Rego v1 (includeimport rego.v1) and define a deny rule
set. The package name can be anything — ctrlplane detects it automatically.
- The set is empty by default → the plan passes.
- Every string you add to
denybecomes a separate violation line. - Use the OPA standard library (
json.unmarshal,yaml.unmarshal,contains,sprintf, regex, etc.) to parse and inspect the plan.
The input document
Your Rego policy is evaluated against an input document with the following
fields:
| Field | Type | Description |
|---|---|---|
input.current | string | The currently deployed state (raw, agent-specific format) |
input.proposed | string | The proposed state from the new version (raw, agent-specific format) |
input.hasChanges | boolean | Whether the plan contains any changes |
input.agentType | string | The agent that produced the plan (e.g. argo-cd, terraform-cloud) |
input.environment | object | The target environment |
input.resource | object | The target resource |
input.deployment | object | The deployment |
input.proposedVersion | object | The deployment version being planned (the new version) |
input.currentVersion | object | The version currently deployed to this target (may be null) |
input.current and input.proposed are strings, not parsed objects,
because the format is agent-specific (Terraform JSON plan, rendered Kubernetes
YAML, etc.). Parse them inside your policy with json.unmarshal or
yaml.unmarshal.Examples
Flag resource deletions in a Terraform plan
Forbid hard-coded secrets in a rendered manifest
Require that a production change actually has a diff
Flag a major version jump
Viewing results
When a plan completes, ctrlplane records each rule’s result against the plan. Results surface in two places:- GitHub check run — if the version carries GitHub metadata, the deployment check shows the plan diff and a Policy violations section listing each failing rule and its messages. A violation marks the check as failed so it is visible on the pull request, but it does not block the ctrlplane deployment.
- ctrlplane UI — the plan preview for a release target shows the diff alongside any validation results.
Best practices
- ✅ Use a clear
name— it is how violations are labeled in the output. - ✅ Scope rules with the policy
selectorso they only run where they matter (e.g. production only). - ✅ Branch on
input.agentTypewhen a policy is specific to Terraform or ArgoCD output. - ✅ Test Rego in the OPA playground before saving.
- ❌ Don’t rely on plan validation to stop a deployment — it is a preview, not a gate. Use Approval or Environment Progression for gating.
Next Steps
- Policies Overview - Learn about policy structure
- Approval - Require sign-off before deploying
- Verification - Check metrics after deploying