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

# Manage OpalScripts with Terraform

> Create, version, and attach OpalScripts as code with Opal's Terraform provider.

You can manage OpalScripts with [Opal's Terraform provider](/docs/use-terraform-with-opal), the same way you manage resources and groups. Keep script source in your repository, review changes in pull requests, and attach request review scripts to approval stages in the same configuration.

## What you can manage

| Object | Use it to |
| - | - |
| `opal_script` resource | Create, update, rename, and delete an OpalScript of any type |
| `opal_script` data source | Read an existing script by ID |
| `opal_script_from_name` data source | Look up an existing script by name |
| `opal_script_ids` on `reviewer_stages` | Attach request review scripts as reviewers on an `opal_resource`, `opal_group`, or `opal_configuration_template` |

You can create all three script types with Terraform. Only request review scripts can be attached with Terraform. Delegation condition scripts and Paladin context scripts are still attached in the Opal UI. See [What isn't supported yet](#what-isnt-supported-yet).

## Requirements

* An Opal provider version that includes the `opal_script` resource. See the [provider changelog](https://registry.terraform.io/providers/opalsecurity/opal/latest/docs).
* An API token that belongs to an admin. Creating, updating, and deleting scripts is admin-only.

## Create a script

```hcl main.tf theme={null}
resource "opal_script" "mfa_check" {
  name        = "MFA check"
  script_type = "REQUEST_REVIEW"
  owner_id    = "7c86c85d-0651-43e2-a748-d69d658418e8"
  script      = file("${path.module}/scripts/mfa_check.star")
}
```

| Argument | Required | Description |
| - | - | - |
| `name` | Yes | The script's name. Names don't have to be unique, but `opal_script_from_name` needs them to be. |
| `script_type` | Yes | One of `REQUEST_REVIEW`, `REQUEST_DELEGATION_CONDITION`, or `CONTEXT_PROVIDER`. Changing it replaces the script. |
| `script` | Yes | The OpalScript source. |
| `owner_id` | For `REQUEST_REVIEW` | The owner of the service user Opal provisions to run the script. Ignored for other types. Changing it replaces the script. |

The resource also exports `id`, `version`, and `updated_at`.

<Tip>
  Keep each script in its own `.star` file and load it with `file()`. You get syntax highlighting in your editor and readable diffs in code review, and you can paste the file straight into the [editor](/docs/opalscript-editor) to test it.
</Tip>

The other two types are created the same way, without `owner_id`:

```hcl theme={null}
resource "opal_script" "same_team_delegates" {
  name        = "Same-team delegates only"
  script_type = "REQUEST_DELEGATION_CONDITION"
  script      = file("${path.module}/scripts/same_team_delegates.star")
}

resource "opal_script" "hr_context" {
  name        = "HR system context"
  script_type = "CONTEXT_PROVIDER"
  script      = file("${path.module}/scripts/hr_context.star")
}
```

## Attach a request review script to an approval stage

Reference the script's `id` in `opal_script_ids` on a reviewer stage. This replaces the old workaround of looking up the script's service user and listing it under `service_user_ids`.

```hcl theme={null}
resource "opal_resource" "prod_db" {
  # ...

  request_configurations = [
    {
      priority = 0
      reviewer_stages = [
        {
          operator        = "AND"
          opal_script_ids = [opal_script.mfa_check.id]
          owner_ids       = [opal_owner.db_admins.id]
        }
      ]
    }
  ]
}
```

`opal_script_ids` works the same way on `opal_group` and `opal_configuration_template`. It's a set, so order doesn't matter. Only `REQUEST_REVIEW` scripts can be reviewers; Opal rejects any other type.

<Warning>
  If your configuration already lists a script's service user under `service_user_ids`, move it to `opal_script_ids` when you upgrade the provider. Opal reports script reviewers under `opal_script_ids`, so leaving the service user ID under `service_user_ids` shows a diff on every plan.
</Warning>

## Update a script

Changing `script` saves a new version, just like selecting **Save** in the editor. `version` and `updated_at` update on the next refresh; they never cause drift on their own. Earlier versions stay in the editor's [version history](/docs/opalscript-editor#versions-and-diffs).

Changing `name` renames the script and the service user behind it. Attachments follow the script, not its name.

A run uses the version that was current when it started. Runs that are paused with `actions.pause` or `actions.poll` finish on their original version.

<Warning>
  Changing `script_type` or `owner_id` destroys the script and creates a new one with a new ID. Opal won't delete a script that's still attached to something, so the plan fails at the destroy step. Detach the script first, or create the new script under a new resource name and switch your reviewer stages to it before removing the old one.
</Warning>

## Use an existing script

To reference a script that someone made in the editor without managing it in Terraform, use a data source:

```hcl theme={null}
data "opal_script_from_name" "mfa_check" {
  opal_script_name = "MFA check"
}

# Or, by ID:
data "opal_script" "mfa_check" {
  id = "32acc112-21ff-4669-91c2-21e27683eaa1"
}
```

Both return `id`, `name`, `script` (the latest version's source), `script_type`, `version`, and `updated_at`. If more than one script has the name you look up, `opal_script_from_name` fails instead of picking one. Rename the scripts or look the script up by ID.

## Import a script

To bring a script you made in the editor under Terraform management, import it by ID:

```hcl theme={null}
import {
  to = opal_script.mfa_check
  id = "32acc112-21ff-4669-91c2-21e27683eaa1"
}
```

Or, on Terraform earlier than 1.5:

```shell theme={null}
terraform import opal_script.mfa_check "32acc112-21ff-4669-91c2-21e27683eaa1"
```

Find the ID in the editor URL (the `scriptId` query parameter), or with the `opal_script_from_name` data source.

<Note>
  After you manage a script with Terraform, make changes in code, not in the editor. Saving in the editor creates a new version that the next `terraform apply` overwrites with the source in your repository. You can still use the editor to [test runs](/docs/opalscript-editor) and review run history.
</Note>

## Delete a script

Removing an `opal_script` resource deletes the script and, for request review scripts, the service user Opal provisioned for it. Opal won't delete a script that's still in use:

* A request review script still attached to a resource or group. Remove it from `opal_script_ids` in the same change, and Terraform detaches it before it deletes the script.
* A delegation condition script used by any delegation, including delegations that have ended. Delete those delegations first.

## What isn't supported yet

* **Attaching delegation condition scripts.** `opal_delegation` doesn't have a condition script field. Select the script under **Condition script** when you create the delegation in Opal. See [Delegation conditions](/docs/opalscript-delegation-conditions).
* **Attaching Paladin context scripts.** Attach the script to a Paladin agent in Opal. See [Paladin context](/docs/opalscript-context-provider).
* **Secrets.** Scripts read secrets with the `secrets` module, but you create and manage secrets in Opal, not with Terraform. Don't put secret values in script source.


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