---
title: "Structured Rego"
slug: "structured-rego"
updated: 2026-05-19T09:20:07Z
published: 2026-05-19T09:20:07Z
---

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

# Structured Rego

## About Structured Rego

The Platform uses a structured form of Rego to create Policies.

Rego is a declarative, context-aware language for authoring Policies and supports the Open Policy Agent (OPA).

Policies written in structured Rego can be [imported](/apidocs/import-policy-v2) to the Platform, where they will be visible with the UI and a Policy Map will be available. Any Policy available in the PlainID Administration Point can be [exported](/apidocs/export-policy-v2) to Structured Rego.

### Sample Structured Rego Policy

Following is a sample Policy that is expressed in Rego. In this case, the Policy specifies that Gold member customers and Standard customers (WHO) can view and manage their personal accounts and credit cards (WHAT).

```

# METADATA
# custom:
#   plainid:
# 	policyId: 08ae32e4-fbf3-4cc8-b3b9-3b4061d1c825
# 	name: Manage personal account and Credit cards
# 	description: Customers can view and manage their account’s credit cards only with MFA
# 	accessType: Allow
#   policyUse: DYNAMIC_AUTHORIZATION_SERVICE
#   sourceEnvironmentId: 16233c52-daf2-812c-be53-4b4645ecd3a5
#   applications:
#     - id: 1ce7749a-0ca0-43f4-ad4f-c28c1ccd57a5
#     - id: 1ce7749a-0ca0-43f4-ad4f-c28c1ccd57a6
#     customAttributes:
#         tag: pilotPolicies
#         responsibleGroup: Finance
#         riskLevel: 3
package policy
import rego.v1

 
# METADATA
# custom:
#   plainid:
# 	kind: DynamicGroup
# 	name: Gold member customers
dynamic_group(identity) if {
	identity.template == "User"
	identity["User_Type"] == "External"
	identity["Membership_Type"] == "Gold"
}

# METADATA
# custom:
#   plainid:
# 	kind: DynamicGroup
# 	name: Standard Customers
dynamic_group(identity) if {
	identity.template == "User"
	identity["User_Type"] == "External"
	identity["Membership_Type"] == "Standard"
}

# METADATA
# custom:
# plainid:
# kind: Ruleset
# name: User personal bank accounts
# description: Any account where the owner is the user currently trying to access
ruleset (asset, identity, requestParams, action) if {
asset.template == "Bank Accounts"
identity.template == "User"
asset["account_owner"] == identity["Userid_identity"]
action.id in ["View"]
}
 
 
# METADATA
# custom:
#   plainid:
# 	kind: Ruleset
# 	name: User personal credit cards
# 	description: Any credit card where the owner is the user currently trying to access
ruleset(asset, identity, requestParams, action) if {
	asset.template == "Credit Cards"
	identity.template == "User"
	asset["card_owner"] == identity["Userid_identity"]
    action.id in ["Manage", "View"]
}
```

## Policy In Structured Rego

The structure of the Policy in Rego and each of its Rules are detailed in the following section as they would appear based on the sample Policy shared above.

### Policy METADATA Comment Block

The Policy METADATA Comment Block contains general Details about the Policy.

**Dynamic Authorization Example**

```
# METADATA
# custom:
#   plainid:
# 	policyId: 08ae32e4-fbf3-4cc8-b3b9-3b4061d1c825
# 	name: Manage personal account and Credit cards
# 	description: Customers can view and manage their account’s credit cards only with MFA
# 	accessType: Allow
#   policyUse: DYNAMIC_AUTHORIZATION_SERVICE
#   sourceEnvironmentId: 16233c52-daf2-812c-be53-4b4645ecd3a5
#   applications:
#     - id: 1ce7749a-0ca0-43f4-ad4f-c28c1ccd57a5
#     - id: 1ce7749a-0ca0-43f4-ad4f-c28c1ccd57a6
#   customAttributes:
#         tag: pilotPolicies
#         responsibleGroup: Finance
#         riskLevel: 3
package policy
import rego.v1
```

**SaaS Management Example**

```
# METADATA
# custom:
#   plainid:
#     policyId: OCP1ALQM1OCAPCE
#     name: Policy1
#     accessType: Deny
#     policyUse: SAAS_APPLICATIONS
#   sourceEnvironmentId: 16233c52-daf2-812c-be53-4b4645ecd3a5
#     applications:
#     - id: POP65A82QQBCJ93
#       attributes: # SaaS Management parameter
#         roleId: '15613'
#         roleName:  roleName1
#         datasetId: datasetId1
#         workspaceId: workspaceId1
#         policyKind: RLS
package policy
import rego.v1
```

| Parameter | Explanation |
| --- | --- |
| `policyId` | A unique identifier for the Policy. The ID is used to refer to this Policy in subsequent API calls when exporting, updating or deleting the Policy. The ID can contain characters (A-Z), numbers, hyphens, underscores, colons and periods. The ID is case-insensitive, and is unique within the Environment. When creating a new Policy, if this value is not defined, it will be automatically generated. The Policy ID can be viewed and updated from the Policy Details tab. |
| `name` | The name of the Policy as it will appear within the Platform. When creating a new Policy, if this value is not defined, the name of the Policy will be the Policy ID. |
| `description` | Optional free text field as it will appear within the Platform. |
| `accessType` | The access decision type. Valid values: **Allow** or **Deny**. Default value if not defined is **Allow**. |
| `package` | Each Policy is represented as a single Rego Module (package). |
| `policyUse` | The services to which the Policy can be connected Valid Values: `DYNAMIC_AUTHORIZATION_SERVICE` or `SAAS_APPLICATIONS` |
| `sourceEnvironmentId` | The Environment ID from which the file was exported. When importing a Policy, the `sourceEnvironmentId` in the Rego file is validated against the `envId` of the API's Environment. - **Local**: If the IDs match, the Policy is imported in Local (Environment-specific) mode. Use Local Attributes when values differ across Environments, ensuring Policies adapt to the current Environment during Rego evaluation. - **Global**: If the IDs differ, the Policy is imported in Global mode (shared across all Environments). Use Global Attributes to maintain consistency so Policies reference the same evaluated values regardless of Environment. For more details, see [Policy Custom Attributes](/docs/policy-custom-attributes). |
| `applications` | The list of Application IDs connected to the Policy. When using **Dynamic Authorization**, you can link *multiple* Applications. When using **SaaS Management**, you can specify a *single* Application. |
| `attributes` | Relevant only to Policies used for SaaS Authorization Management. This includes all the POP details, which are vendor-specific in the form of `attribute id: value` |

### WHO In Structured Rego

The WHO section represents WHO will get access defined in that Policy. In our sample Policy, we have:

- WHO (DynamicGroup): Gold member customers
- WHO (DynamicGroup): Standard  customers

The Rego rule representing the Dynamic Group begins with a METADATA comment section

**Example:**

```
# METADATA
# custom:
#   plainid:
# 	kind: DynamicGroup
# 	name: Gold member customers
```

| Parameter | Explanation |
| --- | --- |
| `kind` | DynamicGroup (Required) |
| `name` | The name of the Dynamic Group as it will appear within the Platform. If this value is not defined, it will be generated automatically. In the example below: **Gold member customers** |

The Rule body contains the expressions that define who has access granted by this Dynamic Group.

**Example:**

```
dynamic_group(identity) if {
	identity.template == "User"
	identity["User_Type"] == "External"
	identity["Membership_Type"] == "Gold"
}
```

| Expression | Explanation |
| --- | --- |
| `Identity Template Expression` | (Required) Represents the Identity Template this Dynamic Group is associated with. `identity.template == “&lt;Identity Template ID&gt;"` Template ID can be found in **Identity Workspace Settings** > **Details tab** under **Identity Template ID**. In the example below: **User**. |
| `Rule Expressions` | At least one Rule Expression is required. Represents the set of rules defining WHO can get access. `identity["&lt;Identity Attribute Key&gt;"] &lt;Operator&gt; "&lt;Value&gt;"` The Identity Attribute Key can be found in **Identity Workspace Settings** > **Identity Attributes tab** under the specific Attribute form. The inserted value should be valid for the Attribute type. In the following example: `identity[“User_Type”] == identity[“Membership_Type”]` |

### WHAT In Structured Rego

The WHAT in Rego can contain several Rules to specify WHAT Assets and WHAT Actions are part of the Policy. In our sample Policy, we have:

- WHAT (Ruleset): personal bank accounts
- WHAT (Ruleset): personal credit card
- WHAT (Action): view, manage

#### WHAT For Ruleset

The Rego rule representing the Ruleset begins with a METADATA comment section.

Example:

```
# METADATA
# custom:
#   plainid:
#     kind: Ruleset
#     name: bank accounts of location
}
```

| Parameter | Explanation |
| --- | --- |
| `kind` | Ruleset (Required) |
| `name` | The name of the Ruleset as it will appear in the Platform. If this value is not defined, it will be generated automatically. In the following example: **User personal bank accounts** |

The Rule body contains the expressions that define WHAT access is granted by a Ruleset.

**Example 1**

```
ruleset(asset, identity, requestParams, action) if {
	asset.template == "Bank Accounts"
	identity.template == "User"
	asset["account_location"] == identity["User_Location"]
}
```

**Example 2 (Ruleset and Action Combination in an Import Policy)**

```
ruleset(asset, identity, requestParams, action) if {
	asset.template == "Bank Accounts"
	asset["account_owner"] == requestParams["owner"]
    action.id in ["Manage"]	
}
```

Action Ruleset Combinations

- Ruleset Building Block
  - When importing a Ruleset [Building Block](/v1-api/apidocs/import-building-blocks-1), Rulesets should be created without an Action (as shown in Example 1).
- Importing a Policy
  - A full Ruleset with an Action is required when [importing a Policy](/v1-api/apidocs/import-policy-v2) (see Example 2).
- Asset Template without an Action
  - If an Asset Template lacks an Action, the Ruleset can still be imported, but it will not include an Action.

| Expression | Explanation |
| --- | --- |
| `Asset Template Expression` | (Required) Represents the Asset template this Ruleset is associated with. `asset.template == “&lt;Asset Template ID&gt;"` Template ID Can be found in **Authorization Workspace** > **Assets & Conditions** under the Asset Type details tab. In example 1: `Bank Accounts`. |
| `Rule Expression` | At least one Rule Expression is required. Represents the set of rules defining WHAT assets can be accessed: `asset["&lt;Asset Attribute Key&gt;"] &lt;Operator&gt; "&lt;Value\Identity Attribute Key&gt;"` Asset Attribute Key can be found in **Authorization Workspace settings** > **Asset & Conditions** > **Asset Type** under the specific attribute form **Supported Operators**: Equals, Not equals, Contains, Starts with, Ends with, Greater than, Greater or equals, Less than, Less than or equals The inserted value should be valid for the Attribute type or Dynamic value (Identity Attribute). Identity Attribute Key can be found in **Identity Workspace settings** > **Identity attributes** tab under the specific attribute form. |
| Identity Template Expression | Only required if the Rule expression relates to a Dynamic Value (Identity Attribute). Represents the Identity Template this Ruleset is associated with `identity.template == “&lt;Identity Template ID&gt;"` Template ID can be found in **Identity Workspace Settings** > **Details tab** under Identity Template ID. In the example below: `User`. |
| `Action list expression` | (Optional if importing a Ruleset Building Block) Represents the list of allowed Actions for this Asset Template. `action.id` in ["<action name="">"]<br></action> The Action ID can be found in **Authorization Workspace settings** > **Asset & Conditions** > under the Actions tab. For example: `action.id in ["Manage", "View"]` |

Direct Assignment of Assets

To directly assign an Asset instead of a Ruleset, ensure that the:

- Asset Template source is PlainID
- Ruleset has a single rule
- Rule is based on the Asset ID Attribute
- == operator is used in the Rule

```
# METADATA
# custom:
#   plainid:
#     kind: Ruleset
#     name: HR
ruleset(asset, identity, requestParams, action) if {
	asset.template == "ApplicationSegment"
	asset["path"] == "HR"
}
```

### WHEN in Structured Rego

Conditions are external factors that you may want to use to create more flexible and precise Policies. With Conditions, you can allow or restrict Member actions based on:

- IP
- Identity Attributes
- Request Attributes

The Rego rule representing the Conditions always begins with a METADATA comment section. PlainID supports two types of Conditions in Structured Rego:

- Predefined Conditions: Built-in conditions supported across the Platform ([IP](/v1-api/apidocs/structured-rego#when-for-ip-conditions), [Identity](/v1-api/apidocs/structured-rego#when-for-identity-attributes), [Request](/v1-api/apidocs/structured-rego#when-for-request-attributes)).
- [Generic Conditions](/v1-api/apidocs/structured-rego#when-for-generic-conditions-application-templates): Conditions defined using Condition Templates associated with an Application. These templates determine the supported attributes and values that can be used in the condition.

#### WHEN for IP Conditions

IP Conditions allow or restrict users based on a preconfigured IP range.

Example:

```
# METADATA
# custom:
#   plainid:
#     kind: Condition IP
#     name: Local IP
```

| Parameter | Explanation |
| --- | --- |
| kind | Condition IP (Required) |
| name | The name of the IP Condition as it appears in the Platform. If this value is not defined, it is generated automatically. Example: `Local IP` |

The body contains the expressions which define WHEN access is granted or denied by this Condition.

**Example 1:**

```
condition_ip(env) if {
	net.cidr_contains("10.0.0.0/8", env.sourceIp)
}
```

**Example 2:**

```
condition_ip(env) if {
	not net.cidr_contains("10.0.0.0/8", env.sourceIp)
}
```

| Expression | Explanation |
| --- | --- |
| Condition Expression | **At least one Condition Expression is required.** This expression represents the IP Range indicating WHEN assets can be accessed or restricted. Referencing the code block examples provided above: **Example 1:** Allows access for an IP range `net.cidr_contains("10.0.0.0/8", env.sourceIp)` **Example 2:** Restricts access for an IP range `not` `net.cidr_contains("10.0.0.0/8",env.sourceIp)` *Note: Only valid CIDR addresses are supported.* |

#### WHEN for Identity Attributes

Identity Attribute Conditions allow or restricts access based on an attribute associated to the Identity - like all users of a **specific department**; all users within a **specific location** (country, city, region, etc.), all users with a** specific title**.

**Example:**

```
# METADATA
# custom:
#   plainid:
#     kind: Condition Identity
#     name: User Site is USA
```

| Parameter | Explanation |
| --- | --- |
| kind | Condition Identity (Required) |
| name | The name of the Identity Condition as it appears in the Platform. If this value is not defined, it is generated automatically. Example: `User Site is USA` |

The body contains the expressions that define WHEN access is granted by this Condition. **Example 1:**

```
    condition_identity(identity) if {
	identity.template == "User"
	identity["Site"] == "USA"
}
```

| Expression | Explanation |
| --- | --- |
| Condition Expression | At least one Condition Expression is required. This expression represents the set of rules defining WHEN assets can be accessed: `identity["Site"] == "USA"` The value should be valid for the specified Attribute type. |
| Identity Template Expression | The Identity Condition consists of Identity Attributes, so the associated Identity Template ID is required. `identity.template == “&lt;Identity Template ID&gt;"` The Template ID can be found in the Identity Workspace Settings > Details tab under Identity Template ID. Example as referenced in the **Example 1** code block: `User` |

#### WHEN for Request Attributes

Request Attribute Conditions allow or restrict access based on the value of a specific request parameter.

**Example:**

```
# METADATA
# custom:
#   plainid:
#     kind: Condition Request
#     name: MFA Authentication
}
```

| Parameter | Explanation |
| --- | --- |
| kind | Condition Request (Required) |
| name | The name of the Request Condition as it appears in the Platform. If this value is not defined, it is generated automatically. Example: `MFA Authentication` |

The body contains the expressions which define WHEN access is granted by this Condition. **Example 1:**

```
condition_request(requestParams, identity) if {
	requestParams["Auth_Method"] == "MFA"
}
```

**Example 2:**

```
condition_request(requestParams, identity) if {
	identity.template == "User"
	requestParams["Auth_Method"] == identity["userMethod"]
}
```

| Expression | Explanation |
| --- | --- |
| Condition Expression | At least one Condition Expression is required. It represents the set of rules defining WHEN assets can be accessed: `requestParams["Auth_Method"] == "MFA"` The value should be valid for the specified Attribute type or Dynamic value (Identity Attribute). The Identity Attribute Key can be found in the Identity Workspace settings > Identity attributes tab under the specific attribute form. Compare **Example 2** to the following Identity Attribute value: `requestParams["Auth_Method"] == identity["userMethod"]` |
| Identity Template Expression | This expression is only required if the Rule expression relates to a Dynamic Value (Identity Attribute). It represents the Identity Template the Condition is associated with. ` identity.template == “&lt;Identity Template ID&gt;"` The Template ID can be found in the Identity Workspace Settings > Details tab under Identity Template ID. Example as shown in **Example 2** code block: `User` |

---

#### WHEN for Generic Conditions

Generic Conditions are used to support SaaS Applications (Orchestration) based on a structured Template that defines the available attributes, supported operators, and valid values for a Condition.

In Structured Rego, Generic Conditions are defined using the `Condition` **kind** and a `subKind` identifying the associated template. In the Platform, the `subKind` is labelled as ** Condition Type**.

Generic Conditions use the following rule structure:

| Expression | Explanation |
| --- | --- |
| `condition(requestParams)` | Defines the rule used to evaluate the condition using request parameters defined by the template. **Note that this is a fixed expression**. |
| Condition Expressions | One or more expressions comparing a request attribute to a value defined in the template schema. |

**Format**

```
condition(requestParams) if {
    <Condition Expressions>
}
```

---

### Condition Expression

Each Condition Expression follows the format:

`requestParams["&lt;Condition Key&gt;"] &lt;Operator&gt; "&lt;Value&gt;"`

| Element | Explanation |
| --- | --- |
| **Condition Key** | Must match an attribute defined in the associated **Condition Template**. |
| **Operator** | Check the supported Operations in the SaaS Vendor |
| **Value** | Must match the type and constraints defined for the attribute. |

*Note: `requestParams` is a fixed value.*

---

### Metadata Parameters

| Parameter | Explanation |
| --- | --- |
| `kind` | Must be `Condition`. |
| `subKind` | The name of the **Condition Template** associated with the Application. |
| `name` | The name of the Condition as it appears in the Platform. If not defined, it is generated automatically. |

---

### Example: Zscaler Posture Condition

The following example defines a condition using the **Zscaler Client Connector Posture Profiles** template.

```
# METADATA
# custom:
#   plainid:
#     kind: Condition
#     subKind: Client Connector Posture Profiles
#     name: Test

condition(requestParams) if {
	requestParams["posture 1"] == "Verified"
}
```

In this example:

- `Client Connector Posture Profiles` identifies the **Zscaler condition template**.
- `posture 1` is an attribute defined in that template.
- `Verified` is a valid value allowed by the template schema.

### Structured Rego Guidelines

Each Policy is represented as a single Rego Module (package) and is constructed using the following syntax rules:

- Each Policy file begins with a `# METADATA` comment block. This block contains the metadata for the Policy (Policy Name, Policy ID, Description and Access Type (Allow/Deny), etc.)
- Each Rule within Rego begins with an expression that specifies which Identity Template or Asset Template is being used (see Policy Structure in Rego for details).
- All Rego Rules are preceded by a `# METADATA` comment block, which contains the metadata about the building block, including its type (Dynamic Group, Ruleset, etc., and optionally other information such as name, to find the Asset Template ID.
- The expressions must conform to a strict format, wherein a value from the source is compared to a value (or a set of values) appearing directly in the expression, using an operator such as `==, &lt;, &gt;` etc.
- Multiple statements may exist within a Rule. When there is more than one statement within a Rule, the statements are evaluated with an `AND` relationship between them.
- Multiple Rego Rules may exist for one building block. When there is more than one Rego Rule for a Building Block, the Rules are evaluated with an `OR` relationship between them
- Each such expression is a comparison between an Attribute and a fixed value, or between two Attributes.
- Attributes, which are defined as part of the Template (either an Identity Template in the case of Dynamic Groups and an Asset Template in the case of Rulesets), can source their values either from the Authorization Request itself or from an external data source. Depending on the type of the Building Block, different operators are allowed in an expression. For details, please see the PlainID Policy Authoring documentation.
- Annotated code is validated to contain only expressions that use supported operators, and only access attributes that exist in the relevant Identity or Asset Templates.
- Generic Conditions use kind: Condition together with subKind to identify the Application-specific Condition Template.

### Structured Rego Ordering Best Practices

To import a Policy into PlainID, these blocks and their parameters must be included in the Policy file:

- Policy metadata
- Dynamic Groups and Rulesets
  - Actions (optional)
- Conditions (optional)
  - Time-based Conditions are not supported in Structured Rego.

Note: All Structured Rego files include these constant parameters:

- `METADATA`
- `custom`
- `plainid`

When a Policy is exported from PlainID as a Rego file, the Policy Structured Rego is organized in the following manner:

- Policy metadata Block:
  - `policyId`
  - `name`
  - `description` (optional)
  - `accessType`
  - `policyUse`
  - `sourceEnvironmentId`
  - `applications`
    - `id`
- Dynamic Group Block (sorted alphabetically by name):
  - `kind`
  - `name`
  - `dynamic_group(identity)`
    - `identity.template`
    - `identity[{“IdentityAttName”}]`
- Ruleset Block (sorted alphabetically by Asset Type Name and their associated Rulesets):
  - `kind`
  - `name`
  - `ruleset(asset, identity, requestParams, actions)`
    - `asset.template`
    - `identity.template (optional)`
    - `asset[{“AssetAttName”}]`
    - `action.id["ActionID"]`
      - Optional if creating a Ruleset building block. Required if importing a Ruleset using [Import Policy](/v1-api/apidocs/import-policy-v2).
- Condition Block (sorted alphabetically by type and their associated Condition):
  - **Type: Condition IP**
    - `kind`
    - `name`
    - `condition_ip(env)`
    - `net.cidr_contains ({“IPRange”}, env.sourceIp)`
  - **Type: Condition Request**
    - `kind`
    - `name`
    - `condition_request(requestParams, identity)`
    - `identity.template (optional)`
    - `requestParams[{“RequestAtt”}]`
  - **Type: Condition Identity**
    - `kind`
    - `name`
    - `condition_identity(identity)`
    - `identity.template`
    - `identity[{“IdentityAttName”}]`

It is recommended to keep Rego files in the order returned by the Export API (or the code output of the Import API) to ensure that changes in the Policy via the console do not result in major differences in code. If the Rego files are managed in a source control system (such as Git), keeping the export order will result in minimal diffs between versions.

Policy Evaluation Logic

Refer to [Working with Policy APIs](/v1-api/apidocs/working-with-import-and-validate-policy) to learn more about how Policy API logic functions.

Important

When a Rego file is imported by PlainID, any code that isn't annotated with a PlainID-formatted #METADATA block is skipped.
