> ## Documentation Index
> Fetch the complete documentation index at: https://docs.envzero.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pin deployments to an approved revision

> Use an organization-level approval policy in env zero to deny deployments that don't use the template's revision or a per-environment approved revision.

An [approval policy](https://docs.envzero.com/guides/policies-governance/approval-policies.md) can deny any deployment that doesn't use an approved revision. Assign it to the organization, so it covers every environment and only users with the **Edit Organization Settings** permission can remove it.

The policy input carries two revisions:

* `deploymentRequest.revision`: the revision the deployment uses. When the deployment doesn't set one, env zero uses the environment's last deployed revision, then the template's revision.
* `template.revision`: the revision set on the template.

Both are compared as entered, a branch, tag, or commit SHA, and are not resolved to commits. Approve a tag or commit SHA. If the approved revision is a branch, every commit pushed to that branch passes the check.

> [!WARNING]
>
> Approval policies run after the plan step. The repository is cloned, and `init` and `plan` run, on the requested revision. A `deny` cancels the deployment before apply.

## Prerequisites

* The **Edit Organization Settings** permission
* A repository for the policy that deployers can't change, separate from the code they deploy, so changes to the approved revisions go through your git review
* An [API key](https://docs.envzero.com/guides/admin-guide/user-role-and-team-management/api-keys.md) ID and secret

## The policy

Every environment must deploy its template's revision, unless `approved_revisions` lists the environment with its own revision. Find an environment's ID in the details card at the top of the environment page.

```rego pin-revision.rego theme={null}
package env0

approved_revisions := {
  "<PROD_ENVIRONMENT_ID>": "v1.4.0"
}

deploy_types := {"deploy", "deployResume"}

checked {
  deploy_types[input.deploymentRequest.type]
}

environment_id := input.variables.env0.ENV0_ENVIRONMENT_ID

approved = approved_revisions[environment_id]

approved = input.template.revision {
  not approved_revisions[environment_id]
}

deny["No approved revision. Set a revision on the template."] {
  checked
  not approved
}

deny[msg] {
  checked
  requested := object.get(input.deploymentRequest, "revision", "")
  requested != approved
  msg := sprintf("Revision %q is not the approved revision %q", [requested, approved])
}

deny["Applying from a pull request comment is not allowed"] {
  checked
  input.deploymentRequest.triggerName == "comment"
}

allow["Revision check passed"] {
  true
}
```

* A deployment is denied when its revision doesn't match, or when the environment isn't in `approved_revisions` and its template has no revision.
* [Applies from pull request comments](https://docs.envzero.com/guides/admin-guide/environments/plan-and-apply-from-pr-comments.md) are denied in every environment. They run the pull request's branch, while `deploymentRequest.revision` still shows the environment's revision.
* Destroys and pull request plans aren't checked against the approved revision.

To approve a new revision, change the template's revision, or the environment's entry in `approved_revisions`, and then redeploy each affected environment with the new revision. Until you do, deployments that don't set a revision, including scheduled deployments, use the environment's last revision and are denied.

Keep `approved_revisions` in the policy file. Don't load it through [`ENV0_POLICY_DATA_PATH`](https://docs.envzero.com/guides/policies-governance/approval-policies#adding-custom-data-to-the-policy-input): that file is read from the repository being deployed, which deployers control.

> [!WARNING]
>
> **This policy decides approval for the whole organization**
>
> Assigned to the organization, a custom approval policy decides approval for every deployment and destroy in it, including environments that aren't in `approved_revisions`. The environment's **Approve plan automatically** setting no longer applies, and the policy input doesn't include it, so no policy can keep it.
>
> * With the `allow` rule, a deployment or destroy that isn't denied continues without manual approval, unless another approval policy returns `pending` or `deny`.
> * Without the `allow` rule, every deployment or destroy that isn't denied waits for approval, because a policy that returns no result defaults to `pending`.

## Allow a team to bypass the check

The policy input doesn't include user roles, so an exception for administrators has to be based on the deployer's teams. Add this rule, and add `not exempt` as the first line of the `checked` rule. Deployments by members of the team skip every check in the policy.

```rego theme={null}
exempt {
  input.deployerUser.teams[_].name == "<EXEMPT_TEAM_NAME>"
}
```

## Assign the policy to the organization

The organization **Policies** page lists only policies from the env zero policy catalog, and the [Terraform provider](https://registry.terraform.io/providers/env0/env0/latest/docs/resources/approval_policy_assignment) assigns approval policies only to projects (`PROJECT`) and templates (`BLUEPRINT`). Assign a custom policy to the organization with the API.

**1. Create the approval policy**

Point it at the repository and folder that hold your `.rego` file. Use the [`env0_approval_policy`](https://registry.terraform.io/providers/env0/env0/latest/docs/resources/approval_policy) Terraform resource and read its `id`, or add the policy to a template as described in [Template approval policies](https://docs.envzero.com/guides/policies-governance/approval-policies.md#template-approval-policies).

To find the ID of a policy you added to a template, list the policies assigned to it:

```bash theme={null}
curl -s -u "<API_KEY_ID>:<API_KEY_SECRET>" \
  "https://api.env0.com/approval-policy/BLUEPRINT/<TEMPLATE_ID>" \
  | jq '.[] | {name: .blueprint.name, id: .blueprint.id}'
```

**2. Assign it to the organization**

Find your Organization ID under **Organization Settings**, then run:

```bash theme={null}
curl -X POST "https://api.env0.com/approval-policy/assignment" \
  -u "<API_KEY_ID>:<API_KEY_SECRET>" \
  -H "Content-Type: application/json" \
  -d '{"scope": "ORGANIZATION", "scopeId": "<ORGANIZATION_ID>", "blueprintId": "<APPROVAL_POLICY_ID>"}'
```

**3. Test it**

Deploy an environment on a revision that isn't approved. The deployment is cancelled, and the **Approval Policies** step of the deployment log shows `Policy result: deny` with the policy's message.

> [!WARNING]
>
> **Required permission**
>
> Assigning or removing an organization-level approval policy requires the **Edit Organization Settings** permission. A project admin can remove approval policies assigned to their project, but not this one. env zero evaluates the policies from every scope together and `deny` wins, so a project or template policy can't override it.

## Next steps

To control who can assign or remove this policy, review which roles hold the [Edit Organization Settings permission](https://docs.envzero.com/guides/admin-guide/user-role-and-team-management/custom-roles.md#organization-permissions).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.