> ## 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](/guides/policies-governance/approval-policies) 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.
</Warning>

## 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](/guides/admin-guide/user-role-and-team-management/api-keys) 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](/guides/admin-guide/environments/plan-and-apply-from-pr-comments) 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`](/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`.
</Warning>

## 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.

<Steps>
  <Step title="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](/guides/policies-governance/approval-policies#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}'
    ```
  </Step>

  <Step title="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>"}'
    ```
  </Step>

  <Step title="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.
  </Step>
</Steps>

<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.
</Warning>

## Next steps

* [Manage approval policies](/guides/policies-governance/approval-policies) - Full policy input reference and result types.
* [Plan and apply from PR comments](/guides/admin-guide/environments/plan-and-apply-from-pr-comments) - The PR comment applies this policy denies.
* [Role-based access control (RBAC)](/guides/admin-guide/user-role-and-team-management/rbac) - Control who has Edit Organization Settings.


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