> 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/access-management/just-in-time-access/access-policies/configure-your-first-access-policy.md).

# Configuring access policies

Configure access policies in P0 Security's Policy Studio to control who can request access to which resources, and how those requests are approved. Includes the Policy Studio walkthrough, the full pol

Access policies give you fine-grained control over your organization's just-in-time access workflow: *who* can request access, *which* resources they can request, and *how* those requests are approved. This page is the complete reference — the Policy Studio walkthrough, the policy format, worked scenarios, and evaluation order.

## Prerequisites

Before you begin, confirm the following:

* **Owner** or **Security Reviewer** role in your P0 organization.
* At least one [resource integration](/integrations/resource-integrations.md) installed (AWS, Google Cloud, Azure, Kubernetes, or another supported resource).
* A [directory integration](/integrations/directory-integrations.md) configured (Google Workspace, Okta, or Microsoft Entra ID) if you plan to use group-based policies.

## Open Policy Studio

1. Sign in to [p0.app](https://p0.app).
2. Navigate to **Policy Studio** in the sidebar, or go to `https://p0.app/o/<your-organization>/policies`.

You see a list of existing policies. If this is your first time, the list may be empty or contain a default policy.

{% hint style="info" %}
Toggle between **Table view** and **YAML view** from the **View options** menu (the ellipsis button in the page header) to see your policies in different formats.
{% endhint %}

## Create a new policy

1. Click **Add New Policy**.

   The policy editor opens in **Guided editor**, which provides a visual form for building your policy. To edit raw YAML instead, select **YAML editor** from the dropdown menu in the header.
2. Enter a descriptive name for your policy in the name field at the top (for example, "Engineering - AWS read-only").

The editor has three sections: **Identity**, **Resource(s)**, and **Actions**. Configure each one in order.

## Define the identity (who can request)

The **Identity** section specifies which users can make requests that match this policy.

1. In the **Identity** section, select one of the following:
   * **Any user**: Matches all users in your organization.
   * **Specific users**: Matches a specific user or directory group.
2. If you selected **Specific users**, choose one of the following in the **Match by** row:
   * **Specific user**: Enter the user's email address.
   * **Directory group**: Select your directory provider and enter the group identifier.

{% hint style="info" %}
**Finding your group identifier:**

* **Google Workspace:** The group email address (for example, `engineering@yourcompany.com`).
* **Okta:** The group ID from the admin console URL. See the [Okta documentation](https://support.okta.com/help/s/article/how-to-find-group-ids-through-the-okta-user-interface?language=en_US) for details.
* **Microsoft Entra ID:** The group's UUID from the Entra admin center.
  {% endhint %}

### Example

To create a policy for your engineering team using Google Workspace:

* Select **User** then **Directory group**.
* Directory: **Google Workspace**
* Group ID: `engineering@yourcompany.com`
* Label: `Engineering`

## Define the resource (what they can request)

The **Resource(s)** section specifies which resources this policy applies to.

1. In the **Resource(s)** section, select one of the following:
   * **Any resource**: Matches all integrated resources.
   * A specific integration (for example, **AWS**, **Google Cloud**, **Kubernetes**).
2. If you selected a specific integration, optionally configure:
   * **Access type**: Restrict to a specific type (for example, `role`, `permission-set`, or `resource` for AWS).
   * **Filters**: Narrow the scope further by matching resource properties.

### Add a resource filter

Filters let you control which specific resources within an integration can be requested.

1. Click **Add New Resource Filter** in the resource section.
2. Select the **filter type** (for example, `policy`, `role`, `resource`).
3. Choose the **effect**:
   * **Include only**: Allow requests for resources that match the pattern.
   * **Exclude only**: Block requests for resources that match the pattern.
   * **Exclude all**: Disable this resource type entirely.
4. Enter the **pattern** (a regular expression) to match against.

{% hint style="info" %}
Patterns are unanchored by default. Use `^` and `$` to anchor patterns. For example, `^true$` matches the exact string "true", while `FullAccess` matches any resource containing "FullAccess" in its name.
{% endhint %}

### Example

To restrict AWS access to non-admin permission sets:

* Integration: **AWS**
* Access type: **permission-set**
* Filter: `permission-set`, Effect: **Exclude only**, Pattern: `FullAccess|Admin`

## Define the actions (how requests are approved)

The **Actions** section specifies how matching requests are handled.

1. Select an approval type:

| Approval type              | Behavior                                                                                                                                                                                        |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **P0 Security Reviewers**  | Routes to your configured Security Reviewers                                                                                                                                                    |
| **Group approval**         | Routes to members of a specific directory group                                                                                                                                                 |
| **Always allowed**         | Automatically grants access (standing access)                                                                                                                                                   |
| **Deny all**               | Denies all matching requests                                                                                                                                                                    |
| **Auto-approve (on-call)** | Automatically approves if the requestor is on-call (requires [PagerDuty](/integrations/approval-integrations/pagerduty.md) or [Incident.io](/integrations/approval-integrations/incidentio.md)) |
| **Escalation service**     | Routes to on-call users for priority approval                                                                                                                                                   |
| **Manager approval**       | Routes to the requestor's manager, read from your directory                                                                                                                                     |

2. Configure approval options as needed:
   * **Require reason with request**: Requestors must give a justification.
   * **Allow self-approvals**: Requestors can approve their own requests.
3. To add multiple approvers, click **Add New Access Rule** to create additional approval rules. When multiple approvers are configured, approval from any one approver is sufficient.

### Example

To route engineering AWS requests to the SRE team with mandatory justification:

* Approval type: **Group**
* Directory: **Google Workspace**
* Group ID: `sre-team@yourcompany.com`
* Label: `SRE Team`
* Enable **Require reason with request**

## Save and enable the policy

1. Review your policy configuration across all three sections.
2. Click **Create Policy**. (When you later edit an existing policy, this button reads **Save Policy**.)
3. Confirm the policy appears in the policies list with the **Enabled** toggle turned on.

{% hint style="warning" %}
Policies take effect immediately after saving. When you save a new policy, it becomes part of your active workflow and applies to all later access requests.
{% endhint %}

## Verify the policy

1. Have a user who matches the identity criteria make a test access request via Slack (`/p0 request`), the [P0 CLI](/p0-cli/p0-commands-and-usage/p0-request.md), or the [web request modal](/access-management/just-in-time-access/requesting-access/web-request-modal.md).
2. Confirm the following:
   * The request is created (not blocked by a missing match).
   * The correct approvers receive the approval notification.
   * Resources outside the policy's filters are not available for request.
3. Check the **Activity** page at `https://p0.app/o/<your-organization>/access-management/activity` to view the request and confirm the policy decision.

{% hint style="info" %}
If the requestor sees *"This resource doesn't exist, or your organization doesn't allow this principal to access this resource"*, no policy matches their request. Review the identity and resource criteria in your policy.
{% endhint %}

## Access policy format

When working with access policies directly, use this reference page for specific definitions of how to format your request. This feature of P0 is powerful and customizable, and is accessible through the [Access policies API](/access-management/just-in-time-access/just-in-time-api/access-policies-api.md) and the [Terraform provider](/p0-api/manage-p0-policies-and-settings-as-code.md).

### Structure of a policy configuration

A policy configuration is a collection of access policies. To change how P0 routes your requests (such as sending engineers' requests to their managers or restricting access to a particular prod resource to a small group), submit a new policy configuration.

You only have one policy configuration active at any given time, but that one configuration can have as many individual access policies as you want. You don't need to separate out different rule types or anything like that.

Here's an example policy configuration that you can copy into your app as a starting point:

```yaml
- requestor:
    type: group
    effect: keep
    groups:
      - id: engineering@yourorg.com
        label: Engineering
        directory: workspace
  resource:
    type: integration
    service: snowflake
  approval:
    - type: group
      effect: keep
      groups:
        - id: dataops@yourorg.com
          label: Data Ops Team
          directory: workspace
      options: {allowOneParty: false}
```

### Structure of a "rule"

The core of policy configurations are the rules, so let's break them down.

### Requestor

The first part of a rule is a "requestor", which matches who makes the request. There are four types of requestors you can choose from:

#### "Any"

```yaml
requestor:
  type: any
```

This matches all requestors. Useful for global rules like fall-back restrictions of sensitive resources.

#### "Group"

```yaml
requestor:
  type: group
  effect: keep|remove
  groups:
    - id: <group identifier>
      label: <human-readable name>
      directory: azure-ad|entra-id|okta|workspace
```

This rule matches any requestor that is a member of a group in your IdP. Currently, this supports Google Workspace, Microsoft Entra ID, and Okta.

All three of `type`, `groups`, and `effect` are required, and no other keys are accepted. Note that the group's own fields sit under `groups`, not directly on the rule.

**groups**: A list of one or more groups. Each entry needs all three of `id`, `label`, and `directory`.

**effect**: `keep` matches requestors who are a member of at least one of the listed groups. `remove` matches requestors who are a member of none of them.

**id**: For Google Workspace, this is the group email address (that is, <engineering@yourco.com>). For Entra, this is the Entra ID group's UUID. For Okta, this is the group ID found in the URL of the group's page in the admin console. See [Okta docs](https://support.okta.com/help/s/article/how-to-find-group-ids-through-the-okta-user-interface?language=en_US).

**label**: This is any friendly human-readable name you like, although P0 suggests using the same name as displayed in your directory.

**directory**: For Google Workspace this is "workspace", for Okta this is "okta", and for Entra ID, this is "entra-id". Use "azure-ad" only if you are on P0's legacy Entra ID integration, which the app labels "Entra ID (Legacy)".

#### "User"

```yaml
requestor:
  type: user
  uid: <user's email address>
```

This rule matches only a specific user, as identified by their email address.

#### "Agentic"

```yaml
requestor:
  type: agentic
  actor: <actor rule>
  subject: <subject rule>
```

This rule matches requests made by AI agents through the [P0 AI Gateway](/readme/agentic-control-plane.md), based on the combination of the agent that is acting and whoever it is acting for. See [Agentic access policies](/access-management/just-in-time-access/access-policies/agentic-access-policies.md) for the actor and subject rule types and examples.

### Resource

The second part of an access policy is the resource this rule should apply to.

#### "Any"

```yaml
resource:
  type: any
```

This matches all resources. This is useful, for instance, when you want to route access for a particular user, regardless of what resource or service they are looking to access.

#### "Integration"

```yaml
resource:
  type: integration
  service: <target service>
  accessType: <target access type>
```

This rule matches a specific type of access request. For instance, maybe you want to create a rule that routes all AWS requests to your DevOps team. Or you want to restrict access to GCP to only a select group.

**service**: For GCloud, "gcloud", for AWS, "aws", for Microsoft Azure "azure", for Snowflake, "snowflake", and for SSH, "ssh".

**accessType** (Optional): The access type within the service that the rule applies to, or "any" meaning the rule matches requests of any access type. Defaults to "any" if omitted.

**filters** (Optional):\
Filters allow you to apply the rule only to the service components that match the filtering condition.

```yaml
resource:
  type: integration
  service: aws|azure|azure-ad|gcloud|k8s|okta|snowflake|ssh
  filters:
    <access-type>:
      effect: keep|remove|removeAll
      key: <property>
      pattern: <regex pattern>
```

Each filter has a `filter-name` that refers to the type of the requested object. For instance, when "service" is "aws", the filter with the name "policy" only allows requesting policies whose ARNs match the `pattern`.

Filters are independent when the access type is omitted or set to "any." For example, if you specify a `filter-name=permission` filter for `service=gcloud` requests, but omit a `filter-name=role` filter, then roles are unaffected by the filter, and all roles remain requestable.

Each filter has three potential properties:

* **effect** - Describes how this filter is applied
  * **keep** - This rule retains objects that match the pattern as requestable
  * **remove** - This rule only retains objects that do not match the pattern
  * **removeAll** - This rule disables the object type entirely.
  * To allow all objects of a type as requestable, omit the filter altogether
* **key** - Describes which property of the object the filter must match; only applies if "effect" is one of "keep" or "remove". For instance, if `service=aws` and `access-type=permission-set` then the key can be "name" or "arn". If "name", the regex pattern matches the name of permission sets. If "arn", the regex pattern matches the entire arn.
* **pattern** - A regex pattern used to match against the specified property; only applies if "effect" is one of "keep" or "remove"

{% hint style="info" %}
Patterns are unanchored. Use line-start (`^`) and line-end (`$`) markers to anchor patterns.

P0 uses the [JavaScript regex dialect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions).
{% endhint %}

*Filter behavior*

The behavior of filters depends on the requested access type.

For example, the "role" filter in the Gcloud integration has a slightly different meaning depending on which access type is used in the request:

* When making a GCP request with "role" as the access type (for example, `p0 request gcloud role`), the filter limits the roles the user can request access to
* When making a GCP request with "resource" as the access type (for example, `p0 request gcloud resource`), the filter limits the roles that the user can request on the resource they are requesting

By setting the rule's access type to either "role" or "resource", you can explicitly specify which of these requests is allowable.

{% hint style="warning" %}
To ensure predictable filtering behavior, P0 recommends setting an `accessType` constraint when using filters.
{% endhint %}

The following table summarizes valid filter filter-name / key combinations. For more detailed information on the filters, see the documentation for [AWS](/access-management/just-in-time-access/access-policies/aws-filtering.md), [Microsoft Azure](/access-management/just-in-time-access/access-policies/microsoft-azure-filtering.md), [Google Cloud](/access-management/just-in-time-access/access-policies/google-cloud-filtering.md), and [SSH](/access-management/just-in-time-access/access-policies/ssh-filtering.md).

<table><thead><tr><th width="155">service</th><th width="147">access-type</th><th width="136">key</th><th>Notes</th></tr></thead><tbody><tr><td>aws</td><td>tag</td><td>&#x3C;tag key></td><td>Only filters policies and permission sets. AWS managed policies can't be tagged.</td></tr><tr><td></td><td>group</td><td>name</td><td>Filters IAM group names</td></tr><tr><td></td><td>permission-set</td><td>arn | name</td><td>Filters Identity Center permission sets</td></tr><tr><td></td><td>policy</td><td>arn</td><td>Filters policies with matching ARNs</td></tr><tr><td></td><td>resource</td><td>arn | name | service</td><td>Filters resource requests based on the resource ARN, the resource name, or the service the resource belongs to.</td></tr><tr><td>azure</td><td>subscription</td><td>id</td><td>Filters requests at a subscription level</td></tr><tr><td></td><td>resource</td><td>id | name</td><td>Filters requests based on what resource is requested</td></tr><tr><td></td><td>role</td><td>id | name</td><td>Filters requests based on the role requested</td></tr><tr><td>azure-ad|okta</td><td>group</td><td>id | label</td><td>Filters directory groups</td></tr><tr><td>gcloud</td><td>permission</td><td>id</td><td>Filters IAM permissions</td></tr><tr><td></td><td>role</td><td>id</td><td>Filters IAM roles based on full ID (for example, the ID for "Owner" is <code>roles/owner</code>)</td></tr><tr><td></td><td>resource</td><td>name | type | full-resource-name</td><td>Filters resource requests based on the resource name, resource type, or the full resource name.</td></tr><tr><td>snowflake</td><td>role</td><td>name</td><td>Filters roles</td></tr><tr><td>k8s</td><td>resource</td><td>kind | name | namespace</td><td>Filters resources</td></tr><tr><td></td><td>role</td><td>name</td><td>Filters roles and cluster roles</td></tr><tr><td></td><td>cluster</td><td>name</td><td>Filters clusters based on the cluster name specified when installed in P0</td></tr><tr><td>ssh</td><td>provider</td><td>id</td><td>Filters by cloud provider (<code>aws</code>, <code>gcloud</code>, <code>azure</code>, or <code>self-hosted</code>)</td></tr><tr><td></td><td>destination</td><td>arn | name | full-resource-name</td><td>Filters by instance identifier</td></tr><tr><td></td><td>group</td><td>name</td><td>Filters by SSH group name</td></tr><tr><td></td><td>parent</td><td>id</td><td>Filters by parent resource ID (AWS account ID, GCP project ID, or Azure subscription ID)</td></tr><tr><td></td><td>region</td><td>id</td><td>Filters by AWS region, GCP zone, or Azure region</td></tr><tr><td></td><td>sudo</td><td><em>(boolean)</em></td><td>Filters by whether sudo privileges are requested</td></tr></tbody></table>

Filters are not currently available for AWS or GCP resource-level grants, nor for Snowflake SQL grants.

*Example that covers each possible access type in the AWS service:*

1. Exclude all permission sets and policies containing "FullAccess"
2. Only allow AWS policies and permission sets where the tag "P0Grantable" is equal to the string "true"
3. Do not allow requesting any AWS groups

<pre><code><strong>- resource:
</strong>    type: integration
    service: aws
    accessType: permission-set
    filters:
      permission-set:
        effect: remove
        key: name
        pattern: FullAccess
      tag:
        effect: keep
        key: P0Grantable
        pattern: ^true$
  requestor:
    type: any
  approval:
    - type: p0

- resource:
    type: integration
    service: aws
    accessType: policy
    filters:
      policy:
        effect: remove
        key: name
        pattern: FullAccess
      tag:
        effect: keep
        key: P0Grantable
        pattern: ^true$
  requestor:
    type: any
  approval:
    - type: p0
    
- resource:
    type: integration
    service: aws
    accessType: group
  requestor:
    type: any
  approval:
    - type: deny
</code></pre>

### Approval

The final part of an access policy is the "approval". Unlike the requestor and resource parts, the approval part is an array of multiple rules, referring to the people/groups/services that can approve (or deny) access requests.

#### "p0"

```json
approval:
  - type: p0
    options: { allowOneParty: true|false, requireReason: true|false, breakGlassApprover: true|false }
```

This approval type is analogous to the default approval behavior. This rule routes approvals to the people designated as "Security Reviewers" on the Settings page of your app. The "options" key is optional. You can use it to:

* allow the requestor to approve their own requests for this flow with the `allowOneParty: true` setting. Defaults to `false`.
* require the requestor to specify a reason when submitting requests with the `requireReason: true` setting. Defaults to `false`.
* designate this approver as a break glass approver for SSH "all" access requests with the `breakGlassApprover: true` setting. Defaults to `false`. See [SSH Filtering](/access-management/just-in-time-access/access-policies/ssh-filtering.md#break-glass-access) for details.

#### "Group"

```yaml
approval:
  - type: group
    effect: keep|remove
    groups:
      - id: <group identifier>
        label: <human readable name>
        directory: azure-ad|entra-id|okta|workspace
    options: { allowOneParty: true|false,  requireReason: true|false, breakGlassApprover: true|false }
```

This rule matches any requestor who is a member of a group in your Identity Provider. Currently, P0 supports Google Workspace, Microsoft Entra ID, and Okta.

The shape matches a [group requestor rule](/access-management/just-in-time-access/access-policies.md#group): `type`, `groups`, and `effect` are all required, and the group's own fields sit under `groups`. Note that `options` stays on the rule, beside `groups`, rather than moving in with the group.

**groups**: A list of one or more groups. Each entry needs all three of `id`, `label`, and `directory`.

**effect**: `keep` routes approval to members of the listed groups. `remove` routes it to everyone who is a member of none of them.

**id**: For Google Workspace, this is the group email address (that is, <engineering@yourco.com>). For Entra ID, this is the Entra ID group's UUID. For Okta, this is the group ID found in the URL of the group's page in the admin console. See [Okta docs](https://support.okta.com/help/s/article/how-to-find-group-ids-through-the-okta-user-interface?language=en_US).

**label**: This is any friendly human-readable name you like, although P0 suggests using the same name as displayed in your directory. This label will be printed in approval notifications.

**directory**: For Google Workspace this is "workspace", for Okta this is "okta", and for Entra ID this is "entra-id". Use "azure-ad" only if you are on P0's legacy Entra ID integration, which the app labels "Entra ID (Legacy)".

**options**: See ["P0" section](/access-management/just-in-time-access/access-policies.md#p0)

#### "Auto"

<pre class="language-yaml"><code class="lang-yaml"><strong>approval:
</strong>  - type: auto
    integration: pagerduty|incidentio
    options: { requireReason: true|false }
</code></pre>

This rule automatically approves matching access requests for one hour.

**integration**: `"pagerduty"` or `"incidentio"`. P0 automatically approves the request if the requestor is on-call. For PagerDuty, P0 checks the escalation policies configured on the integration page. For Incident.io, P0 checks the schedules selected on the [Incident.io integration settings page](/integrations/approval-integrations/incidentio.md#selecting-schedules).

**options**: The "options" key is optional. You can use it to require the requestor to specify a reason when submitting requests with the `requireReason: true` setting. Defaults to `false`.

#### "Escalation"

<pre><code><strong>approval:
</strong>  - type: escalation
    integration: pagerduty|incidentio
    options: { allowOneParty: true|false,  requireReason: true|false, breakGlassApprover: true|false }
    services: [&#x3C;Service or Schedule ID>]
</code></pre>

This rule routes approval to users who are currently on-call for the specified services or schedules. When combined with other approval rules, it enables on-call users to approve requests that have been escalated.

**integration**: `"pagerduty"` or `"incidentio"`.

* For PagerDuty, on-call status is determined by the escalation policies of the configured services. An incident is created against the specified services when the request is escalated.
* For Incident.io, on-call status is determined by the specified schedule IDs. Users who are on-call for any of the listed schedules can approve the request.

**options**: See ["P0" section](/access-management/just-in-time-access/access-policies.md#p0).

**services**: List of PagerDuty service IDs or Incident.io schedule IDs, depending on the configured integration.

#### "Always allowed"

```yaml
approval:
  - type: persistent
```

Access is always granted automatically.

**options**: The "options" key is optional. You can use it to require the requestor to specify a reason when submitting requests with the `requireReason: true` setting. Defaults to `false`.

#### "Deny"

```yaml
approval:
  - type: deny
```

This rule denies all requests that match. This supersedes other matching rules: if at least one "deny" rule matches, the request will be denied.

## Common scenarios

These worked scenarios cover the most common access-policy setups. Adapt the group identifiers, account IDs, and service names to your own organization.

### Route requests by team

Route your engineering team's requests to the SRE team for approval, and your data team's requests to the Data Ops team.

1. In Policy Studio, enter the following access policies:

```yaml
- requestor:
    type: group
    effect: keep
    groups:
      - id: engineering@yourcompany.com
        label: Engineering
        directory: workspace
  resource:
    type: any
  approval:
    - type: group
      effect: keep
      groups:
        - id: sre@yourcompany.com
          label: SRE Team
          directory: workspace
      options: {allowOneParty: false}
- requestor:
    type: group
    effect: keep
    groups:
      - id: data-team@yourcompany.com
        label: Data Team
        directory: workspace
  resource:
    type: integration
    service: snowflake
  approval:
    - type: group
      effect: keep
      groups:
        - id: data-ops@yourcompany.com
          label: Data Ops
          directory: workspace
      options: {allowOneParty: false}
```

2. Replace the group email addresses with your organization's actual directory groups.
3. Click **Submit** to activate the access policies.

**What this does:**

* Any engineer who requests access to any resource gets routed to the SRE team for approval.
* Any data team member who requests Snowflake access gets routed to Data Ops for approval.
* `allowOneParty: false` prevents requestors from approving their own requests.

{% hint style="warning" %}
If a request doesn't match any access policy, P0 rejects it with the message: "This resource doesn't exist, or your organization doesn't allow this principal to access this resource." Add a catch-all policy at the end of your workflow if you want unmatched requests to fall through to default approvers.
{% endhint %}

#### Add a catch-all policy

To ensure unmatched requests still reach your security reviewers, add this policy at the end of your workflow:

```yaml
- requestor:
    type: any
  resource:
    type: any
  approval:
    - type: p0
```

The `type: p0` approval routes requests to the Security Reviewers configured under **P0 Management** → **Access control**.

### Restrict access to production resources

Block requests to production AWS accounts while allowing development and staging access with different approval requirements.

1. In Policy Studio, add the following policies. Replace the AWS account IDs and Okta group IDs with your own values:

```yaml
# Development - one-party approval (engineers can self-approve)
- requestor:
    type: group
    effect: keep
    groups:
      - id: 00g5j4jojlGZMzfhM69
        label: Engineers
        directory: okta
  resource:
    type: integration
    service: aws
    filters:
      policy: {effect: keep, key: arn, pattern: "^arn:aws:iam::111111111111:policy/"}
  approval:
    - type: group
      effect: keep
      groups:
        - id: 00g5j4jojlGZMzfhM69
          label: Engineers
          directory: okta
      options: {allowOneParty: true}

# Staging - peer approval required, reason mandatory
- requestor:
    type: group
    effect: keep
    groups:
      - id: 00g5j4jojlGZMzfhM69
        label: Engineers
        directory: okta
  resource:
    type: integration
    service: aws
    filters:
      policy: {effect: keep, key: arn, pattern: "^arn:aws:iam::222222222222:policy/"}
  approval:
    - type: group
      effect: keep
      groups:
        - id: 00g5j4jojlGZMzfhM69
          label: Engineers
          directory: okta
      options: {allowOneParty: false, requireReason: true}

# Production - deny all access
- requestor:
    type: any
  resource:
    type: integration
    service: aws
    filters:
      policy: {effect: keep, key: arn, pattern: "^arn:aws:iam::333333333333:policy/"}
  approval:
    - type: deny
```

2. Click **Submit**.

**What this does:**

* **Development** (account `111111111111`): Engineers can self-approve their own requests.
* **Staging** (account `222222222222`): Engineers need a peer to approve, and must provide a reason.
* **Production** (account `333333333333`): P0 denies all requests. No one can request access through P0.

{% hint style="info" %}
Deny policies always take precedence. If any matching policy specifies `type: deny`, the request is denied immediately, regardless of other matching policies.
{% endhint %}

#### Exclude dangerous permissions

To further restrict which AWS permissions engineers can request, add filters that remove overly broad policies:

```yaml
resource:
  type: integration
  service: aws
  accessType: policy
  filters:
    policy: {effect: remove, key: arn, pattern: FullAccess}
    tag: {effect: keep, key: P0Grantable, pattern: "^true$"}
```

This removes any policy containing "FullAccess" in its ARN, and only keeps policies tagged with `P0Grantable: true`.

### Auto-approve on-call engineers

Automatically approve requests from engineers who are currently on-call in PagerDuty or Incident.io, while directing all other requests to the SRE team.

1. Confirm that your [PagerDuty](/integrations/approval-integrations/pagerduty.md) or [Incident.io](/integrations/approval-integrations/incidentio.md) integration is installed.
2. In Policy Studio, add the following policies:

{% tabs %}
{% tab title="PagerDuty" %}

```yaml
# On-call engineers get auto-approved
- requestor:
    type: any
  resource:
    type: integration
    service: aws
  approval:
    - type: auto
      integration: pagerduty
      options: {requireReason: true}
    - type: group
      effect: keep
      groups:
        - id: sre@yourcompany.com
          label: SRE Team
          directory: workspace
      options: {allowOneParty: false}
```

{% endtab %}

{% tab title="Incident.io" %}

```yaml
# On-call engineers get auto-approved
- requestor:
    type: any
  resource:
    type: integration
    service: aws
  approval:
    - type: auto
      integration: incidentio
      options: {requireReason: true}
    - type: group
      effect: keep
      groups:
        - id: sre@yourcompany.com
          label: SRE Team
          directory: workspace
      options: {allowOneParty: false}
```

{% endtab %}
{% endtabs %}

3. Click **Submit**.

**What this does:**

* If the requestor is currently on-call (as determined by PagerDuty escalation policies or Incident.io schedules), P0 automatically approves their request for one hour.
* If the requestor is not on-call, P0 routes the request to the SRE team for manual approval.
* `requireReason: true` ensures all requests include a justification in the audit log, even auto-approved ones.

#### Add escalation for urgent requests

You can also configure on-call users as escalation approvers. When a request is pending, the requestor can escalate it to the on-call engineer:

```yaml
approval:
  - type: p0
    options: {requireReason: true, allowOneParty: false}
  - type: escalation
    integration: pagerduty
    options: {requireReason: true, allowOneParty: false}
    services: [PSJXXXG]
```

Replace `PSJXXXG` with your PagerDuty service ID. When the requestor escalates, PagerDuty creates an incident against the specified service, and the on-call engineer receives both a PagerDuty alert and a P0 approval notification.

## Additional examples

Access policies support a wide range of setups. These examples show patterns beyond the common scenarios described earlier.

### Different rules by AWS account

Development AWS account (123456789) allows one-party approvals. Staging AWS account (234567891) allows peer approvals but no one-party approvals. Production AWS account (345678912) requires a manager approver represented by an Okta group. Assumes an AWS Identity Center setup where resource filters allow requesting customer-managed policies in specific AWS accounts, enforced by an ARN match that includes the AWS account ID. Permission sets are implicitly allowed because the `permission-set` access type filter is not defined.

<pre class="language-yaml"><code class="lang-yaml"><strong>- requestor:
</strong>    type: group
    effect: keep
    groups:
      - id: 00g5j4jojlGZMzfhM69
        label: Engineers
        directory: okta
  resource:
    type: integration
    service: aws
    accessType: resource
    filters:
      policy: { effect: keep, key: arn, pattern: ^arn:aws:iam::123456789:policy/ }
      group: { effect: removeAll }
  approval:
    - type: group
      effect: keep
      groups:
        - id: 00g5j4jojlGZMzfhM69
          label: Engineers
          directory: okta
      options: { allowOneParty: true }
- requestor:
    type: group
    effect: keep
    groups:
      - id: 00g5j4jojlGZMzfhM69
        label: Engineers
        directory: okta
  resource:
    type: integration
    service: aws
    accessType: resource
    filters:
      policy: { effect: keep, key: arn, pattern: ^arn:aws:iam::234567891:policy/ }
      group: { effect: removeAll }
  approval:
    - type: group
      effect: keep
      groups:
        - id: 00g5j4jojlGZMzfhM69
          label: Engineers
          directory: okta
      options: { allowOneParty: false, requireReason: true }
- requestor:
    type: group
    effect: keep
    groups:
      - id: 00g5j4jojlGZMzfhM69
        label: Engineers
        directory: okta
  resource:
    type: integration
    service: aws
    accessType: resource
    filters:
      policy: { effect: keep, key: arn, pattern: ^arn:aws:iam::345678912:policy/ }
      group: { effect: removeAll }
  approval:
    - type: group
      effect: keep
      groups:
        - id: 01f5j4jfjlGZMzfhN99
          label: Managers
          directory: okta
      options: { allowOneParty: false, requireReason: true }
</code></pre>

### Approve by manager or service owner

Allow either the requestor's manager or the service owner to approve requests:

```
- requestor:
    type: group
    effect: keep
    groups:
      - id: devs@you.co
        label: Developers
        directory: workspace
  resource:
    type: any
  approval:
    - type: group
      effect: keep
      groups:
        - id: eng-managers@you.co
          label: Eng managers
          directory: workspace
- requestor:
    type: group
    effect: keep
    groups:
      - id: devs@you.co
        label: Developers
        directory: workspace
  resource:
    type: integration
    service: gcloud
  approval:
    - type: group
      effect: keep
      groups:
        - id: gcloud-owners@you.co
          label: GCloud owners
          directory: workspace
```

### Require a reason for a specific resource type

Make reason mandatory for AWS requests

<pre class="language-yaml"><code class="lang-yaml"><strong>- resource:
</strong>    type: any
  approval:
    - options: {requireReason: false, allowOneParty: true}
      type: p0
  requestor:
    type: any
- resource:
    service: aws
    type: integration
  approval:
    - options: {requireReason: true}
      integration: pagerduty
      type: auto
  requestor:
    type: any
</code></pre>

### Exclude cluster-admin and restrict to the default namespace (Kubernetes)

Exclude the `cluster-admin` role and only allow requests in the `default` namespace:

```yaml
- resource:
    type: integration
    service: k8s
    accessType: resource
    filters:
      resource:
        effect: keep
        key: namespace
        pattern: ^default$
      role:
        effect: remove
        key: name
        pattern: ClusterRole/cluster-admin
  approval:
    - type: p0
  requestor:
    type: any
```

### Allow Google Cloud roles but not permissions

Let users request Google Cloud roles while denying direct permission requests:

```
- resource:
    service: gcloud
    type: integration
    accessType: role
  approval:
    - type: p0
  requestor:
    type: any
- resource:
    service: gcloud
    type: integration
    accessType: permission
  approval:
    - type: deny
  requestor:
    type: any
```

### Deny specific roles while allowing others

Deny requests for the Owner role while allowing all other Google Cloud roles:

```
- resource:
    service: gcloud
    type: integration
    accessType: role
    filters:
      role:
        effect: keep
        key: name
        pattern: roles/owner
  approval:
    - type: deny
  requestor:
    type: any
- resource:
    service: gcloud
    type: integration
    accessType: role
  approval:
    - type: p0
  requestor:
    type: any
```

## Evaluation of access policies

1. Match Rules to the Request

   Identify rules that match the request. A rule matches if both the requestor and the resource criteria in the rule align with the request details.
2. Handle Missing Matches

   If no rules match, the access request is not created, and further evaluation stops. The requestor will see the following message:

   *This resource doesn't exist, or your organization doesn't allow this principal to access this resource*
3. Evaluate Access Control of Matching Rules

   For matching rules, review the Access Control field in the following order:

   * Deny all access\
     If *any* matching rule specifies "Deny all access", the request is denied immediately, and further evaluation stops.
   * Always allowed\
     If any rule specifies "Always allowed," the request is automatically approved and provisioned, and further evaluation stops.
   * Third-party auto-approval\
     If a rule specifies a third-party auto-approval, such as a PagerDuty or Incident.io on-call rule, the request is evaluated for automatic approval. If it cannot be auto-approved, proceed to the next step.
   * Manual approval\
     For all other Access Control values, the request requires manual approval by a designated person or group. When multiple rules with different manual approvers match, approval from any one approver is sufficient to provision the request. All approvers are notified.

### Key points to remember

* **Deny all access:** Always takes precedence and stops further evaluation immediately.
* **Always allowed:** Overrides other Access Control values and is prioritized in the evaluation process.
* **Multiple matching rules:** When multiple rules match the requestor and resource, approvers are combined using an **ANY** relationship, meaning approval from any one approver is sufficient.

### Examples

1. **Always allowed rule takes precedence over other allow rules**

Consider a scenario where two rules apply to the same resource, **`node1`** in the **devstack** group:

* An "Always allow" rule grants access to **`node1`**.
* Another "Allow" rule requires approval before granting access.

In this case, the **"Always allow" rule** takes precedence. As a result:

* The user is immediately granted access to **`node1`** without requiring additional approval.

2. **Deny Rules are evaluated first, before ANY allow rules**

Using the same example as before, if there is an additional rule that **denies access** to any instance containing **`node`** in its name, the system evaluates the "Deny" rule first.

In this case:

* The "Deny" rule matches because **`node1`** contains the string **`node`** in its name.
* The user's request is immediately denied, and is not evaluated further.

This highlights the priority of "Deny" rules: the system enforces them before considering any other rules.

## Troubleshooting

| Symptom                                                | Cause                                                                                    | Solution                                                                                                                                                                                  |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Request rejected with "This resource doesn't exist..." | No access policy matches the request.                                                    | Add a catch-all policy or verify that your requestor and resource filters match the request.                                                                                              |
| Wrong approver notified                                | A broader policy matches before the specific one.                                        | Reorder policies so more specific policies come first. Deny policies always take precedence regardless of order.                                                                          |
| Auto-approval not working                              | The requestor is not on-call, or the PagerDuty/Incident.io integration is not installed. | Verify the requestor's on-call status in your incident management tool, and confirm the integration is active in **Integrations**.                                                        |
| Filter not matching                                    | Regex pattern mismatch.                                                                  | Patterns are unanchored by default. Use `^` and `$` to anchor. P0 uses the [JavaScript regex dialect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions). |

## Next steps

* Fine-tune which resources are requestable with [AWS](/access-management/just-in-time-access/access-policies/aws-filtering.md), [Google Cloud](/access-management/just-in-time-access/access-policies/google-cloud-filtering.md), [Microsoft Azure](/access-management/just-in-time-access/access-policies/microsoft-azure-filtering.md), or [SSH](/access-management/just-in-time-access/access-policies/ssh-filtering.md) filtering.
* Route or auto-approve requests made by AI agents with [agentic access policies](/access-management/just-in-time-access/access-policies/agentic-access-policies.md).
* Manage policies programmatically with the [Access Policies API](/access-management/just-in-time-access/just-in-time-api/access-policies-api.md) or the [Terraform provider](/p0-api/manage-p0-policies-and-settings-as-code.md).
* New to access policies? Start with the [Configure your first access policy](/getting-started/configure-your-first-access-policy.md) tutorial.
