> For the complete documentation index, see [llms.txt](https://docs.p0.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.p0.dev/p0-api/manage-p0-policies-and-settings-as-code.md).

# Manage P0 policies and settings as code

Use the P0 Terraform provider to manage access policies, RBAC role assignments, and just-in-time access settings as code, with worked examples and migration guidance.

This guide shows you how to manage P0's access policies, role assignments, and just-in-time (JIT) access settings with the [P0 Terraform provider](https://registry.terraform.io/providers/p0-security/p0/latest/docs). You manage the same policies and settings you would configure in the P0 dashboard. These include who can request access to what, who holds each P0 role, and the durations requestors can choose. You manage them as version-controlled, reviewable configuration.

If you haven't configured the provider yet, start with [Manage integrations as code with the P0 Terraform provider](/getting-started/get-started-with-the-p0-terraform-provider.md), which covers authentication and installing your first integration. This guide picks up from a configured provider and focuses on policies and settings.

## Prerequisites

Before you begin, confirm the following:

* **P0 account with the Owner role** — You need the **Owner** role to manage policies, roles, and settings.
* **The P0 Terraform provider, version 0.52.0 or later** — Agentic access policies (the `agent` and `user` attributes) require `0.52.0`. The `p0_access_policy` resource itself is available from `0.51.0`, and the role-binding and JIT-settings resources from `0.50.0`.
* **A configured provider** — You have declared and authenticated the `p0` provider, as described in [Manage integrations as code with the P0 Terraform provider](/getting-started/get-started-with-the-p0-terraform-provider.md).

Declare the provider with a version that includes the policy resources:

```hcl
terraform {
  required_providers {
    p0 = {
      source  = "p0-security/p0"
      version = "~> 0.52"
    }
  }
}

provider "p0" {
  org = "my-org"
}
```

The provider reads your token from the `P0_API_TOKEN` environment variable. See [Manage integrations as code with the P0 Terraform provider](/getting-started/get-started-with-the-p0-terraform-provider.md) to authenticate, or [Authenticating with the P0 API](/p0-api/authenticating-with-the-p0-api.md) to get a token. A pipeline that applies policies or settings needs an identity with the **Owner** role.

{% hint style="info" %}
The P0 API has no read endpoint for role assignments or JIT settings, so Terraform cannot detect drift on those resources after they are applied. Manage each setting in one place—Terraform or the dashboard—rather than both.
{% endhint %}

## Manage access policies

An access policy ([Configuring access policies](/policy-studio/policy-studio/configure-your-first-access-policy.md)) controls who can request access to what, and what approvals the request needs. The `p0_access_policy` resource models one policy with three parts:

* `requestor` — who the policy matches (a directory group, a specific user, anyone, or an agent).
* `resource` — the integration and, optionally, the resource filters the policy applies to.
* `approval` — how requests are approved, as an ordered list of approval steps.

The resources a policy references must already exist in P0. For example, the Okta group must come from an installed Okta directory integration ([Okta](/integrations/directory-integrations/okta.md)), the `aws` integration from an installed AWS account, and PagerDuty from an in-app connection.

The following policy lets members of an Okta group request AWS access, auto-approved through PagerDuty when they give a reason:

```hcl
resource "p0_access_policy" "aws_developers_oncall" {
  name = "okta-aws-developers-oncall"

  requestor = {
    type   = "group"
    effect = "keep"
    groups = [{
      directory = "okta"
      id        = "00abcdefghijklmno697"
      label     = "AWS Developers"
    }]
  }

  resource = {
    type    = "integration"
    service = "aws"
    filters = {
      "tag" = {
        effect  = "keep"
        key     = "p0_grantable"
        pattern = "1|true"
      }
    }
  }

  approval = [{
    type        = "auto"
    integration = "pagerduty"
    options = {
      require_reason = true
    }
  }]
}
```

### Match agent requestors

For [Agentic access policies](/policy-studio/policy-studio/agentic-access-policies.md), set `requestor.type` to `agentic` and describe the agent under `agent`. The `user` block describes the human behind the agent; use `type = "none"` to match headless agent sessions.

This policy denies access to a specific MCP client agent running without a human present:

```hcl
resource "p0_access_policy" "mcp_agent_headless" {
  name = "mcp-agent-headless"

  requestor = {
    type = "agentic"
    agent = {
      type      = "agent-client"
      client_id = "my-agent-client"
    }
    user = {
      type = "none"
    }
  }

  resource = {
    type    = "integration"
    service = "aws"
  }

  approval = [{
    type = "deny"
  }]
}
```

The `agent.type` value selects how the policy matches an agent: `any` matches any agent, `agent-client` matches agents connecting through a specific gateway client, `agent-owner` matches an agent owned by a specific user, `owner-group` matches an agent owned by a member of a directory group, and `provider` matches an agent federated by an identity provider.

## Manage P0 role assignments

The P0 RBAC roles ([Role-based access control](/p0-management/role-based-access-control.md)) map to eight role-binding resources—a user and a group variant for each of the four assignable roles:

| Role              | User resource               | Group resource               |
| ----------------- | --------------------------- | ---------------------------- |
| Owner             | `p0_owner_user`             | `p0_owner_group`             |
| Security Reviewer | `p0_security_reviewer_user` | `p0_security_reviewer_group` |
| Assessment User   | `p0_assessment_user`        | `p0_assessment_group`        |
| Assessment Viewer | `p0_assessment_viewer_user` | `p0_assessment_viewer_group` |

Each user resource takes an `email`, and each group resource takes a `group`:

```hcl
resource "p0_owner_user" "admin" {
  email = "alice@example.com"
}

resource "p0_security_reviewer_group" "reviewers" {
  group = "eng-security"
}
```

## Manage just-in-time access settings

Two singleton resources manage the organization's JIT access settings. Declare each at most once.

`p0_access_durations` sets the organization's duration policy. Each field takes a `time` (a whole number) and a `unit` (`s`, `m`, `h`, `d`, or `w`):

```hcl
resource "p0_access_durations" "org" {
  # Maximum time between a request and its approval.
  approvable = {
    time = 14
    unit = "d"
  }

  # Maximum duration access may be granted for.
  max_access = {
    time = 180
    unit = "d"
  }

  # Maximum duration of standing (persistent) access before re-approval.
  standing_access = {
    time = 24
    unit = "h"
  }
}
```

`p0_expiry_options` sets the request-duration presets requestors can select:

```hcl
resource "p0_expiry_options" "org" {
  options = [
    { time = 5, unit = "m" },
    { time = 1, unit = "h" },
    { time = 24, unit = "h" },
    { time = 168, unit = "h" },
  ]
}
```

## Migrate from p0\_routing\_rule

Access policies were previously called routing rules, and the `p0_routing_rule` resource is now deprecated. It shares the same schema and behavior as `p0_access_policy`, but it will be removed in a future release. Migrate any existing `p0_routing_rule` resources to `p0_access_policy`.

To migrate without destroying and recreating the policy, rename the resource type and add a `moved` block, which requires Terraform 1.8 or later:

```hcl
resource "p0_access_policy" "aws_developers_oncall" {
  name = "okta-aws-developers-oncall"
  # ...unchanged policy configuration...
}

moved {
  from = p0_routing_rule.aws_developers_oncall
  to   = p0_access_policy.aws_developers_oncall
}
```

Run `terraform plan` and confirm the plan reports a move rather than a create or destroy, then apply. After the move succeeds, you can remove the `moved` block.

## Apply and verify

Apply your configuration:

```bash
terraform apply
```

Review the plan and confirm. To verify a policy applied, open **Access Policies** in the P0 dashboard and confirm the policy appears, or check the resource state:

```bash
terraform state show p0_access_policy.aws_developers_oncall
```

Confirm role assignments in **Role-Based Access Control** and JIT settings in the JIT access settings, both in the P0 dashboard.

## Next steps

* **Understand access policies** — See [Configuring access policies](/policy-studio/policy-studio/configure-your-first-access-policy.md) for the concepts behind requestors, resources, and approvals.
* **Read the resource reference** — The full argument reference for each resource is in the [Terraform Registry](https://registry.terraform.io/providers/p0-security/p0/latest/docs), including [`p0_access_policy`](https://registry.terraform.io/providers/p0-security/p0/latest/docs/resources/access_policy), [`p0_access_durations`](https://registry.terraform.io/providers/p0-security/p0/latest/docs/resources/access_durations), and [`p0_expiry_options`](https://registry.terraform.io/providers/p0-security/p0/latest/docs/resources/expiry_options).
* **Install integrations as code** — See [Manage integrations as code with the P0 Terraform provider](/getting-started/get-started-with-the-p0-terraform-provider.md) to install integrations with the provider.
