Language basics
OpalScript uses Starlark syntax, which is similar to Python. This section gives you a quick overview of Starlark syntax and its key differences from Python.Syntax overview
# Variables
count = 10
name = "alice"
is_active = True
# Conditionals
if count > 5:
print("high")
elif count > 0:
print("low")
else:
print("zero")
# Loops
for item in [1, 2, 3]:
print(item)
for i in range(10):
print(i)
# Functions
def calculate_score(base, multiplier):
return base * multiplier
# String operations
text = "Hello World"
lower_text = text.lower() # "hello world"
contains_hello = "hello" in lower_text # True
# Lists
items = ["a", "b", "c"]
items.append("d")
first = items[0]
# Dictionaries
config = {"key": "value", "count": 42}
value = config["key"]
config["new_key"] = "new_value"
Key differences from Python
OpalScript (Starlark) has some intentional limitations compared to Python:| Feature | OpalScript | Python |
|---|---|---|
| Import statements | Not supported (modules pre-loaded) | Supported |
| Classes | Not supported | Supported |
| Exceptions | Not supported (try/except) | Supported |
| While loops | Not supported (use for with range()) | Supported |
| Global variables | Limited mutation | Full support |
| Standard library | Minimal | Extensive |
Quick reference
Utility functions (all script types)
| Module | Function | Description |
|---|---|---|
| access | check_access(principal, entity, [level]) | Check whether a principal has access to an entity |
| notifications | notify_user(user_id, title, body) | Notify a specific user via email and Slack (if configured) |
| notifications | notify_admins(title, body) | Notify all Opal admins |
| notifications | notify_owner(owner_id, title, body) | Notify an owner via Slack channel (if configured) or all owner members |
| requests | by_user(user_id, [status], [cursor], [per_page]) | Get requests a user submitted or was the target of (paginated) |
| requests | by_resource(resource_id, [user_id], [status], [cursor], [per_page]) | Get historical requests for a resource (paginated) |
| requests | by_group(group_id, [user_id], [status], [cursor], [per_page]) | Get historical requests for a group (paginated) |
| risk | resource_sensitivity(resource_id) | Get the risk sensitivity level of a resource |
| risk | group_sensitivity(group_id) | Get the risk sensitivity level of a group |
| time | now() | Get the current Unix timestamp |
| time | from_unix(timestamp) | Convert a Unix timestamp to an RFC3339 string |
| time | is_before(timestamp1, timestamp2) | Check if the first timestamp is before the second |
| time | is_after(timestamp1, timestamp2) | Check if the first timestamp is after the second |
| time | seconds_since(timestamp1, timestamp2) | Get the number of seconds between two timestamps |
| time | minutes(n) / hours(n) / days(n) | Convert time intervals to seconds |
| entity | get_user(user_id) | Look up a user by ID |
| entity | get_group(group_id) | Look up a group by ID |
| entity | get_resource(resource_id) | Look up a resource by ID |
| tickets | providers.<PROVIDER> | Access an installed ticket provider (e.g. tickets.providers.JIRA) |
| tickets | list_projects(provider) | List projects on a provider; returns a dict keyed by project key |
| tickets | create_ticket(project, title, description) | Create a ticket on the provider; returns a ticket object |
| tickets | get_ticket(provider, remote_ticket_id) | Fetch an existing ticket by its remote identifier |
| tickets | comment_ticket(provider, remote_ticket_id, comment) | Add a comment to an existing ticket |
| tickets | close_ticket(provider, remote_ticket_id, [comment]) | Close a ticket, with an optional closing comment |
| http | request(url, [method], [headers], [params], [json], [body], [timeout]) | Send an outbound HTTP request and return a Response |
| http | get / post / put / patch / delete | Per-verb convenience wrappers around request |
| secrets | get(name) | Return an opaque Secret to use as an http URL or header value |
http and secrets are currently available in Request Review scripts only. See Reach external services.
Request Review functions
| Module | Function | Description |
|---|---|---|
| context | get_request() | Get the request being reviewed |
| actions | approve([comment], [duration_minutes]) | Approve the request, optionally overriding the access duration |
| actions | deny(comment) | Deny the request (comment required) |
| actions | comment(comment, [duration_minutes]) | Add a comment, optionally overriding the access duration |
| actions | pause(minutes) | Pause script execution for 1–1440 minutes, then resume from the same point; actions before the pause are skipped on replay |
| actions | poll(pollFunction, minutes, max_iterations) | Call pollFunction every minutes minutes (1–1440) up to max_iterations times (1–50); returns True if it succeeded |
Request attributes
| Attribute | Type | Always Present |
|---|---|---|
id | String | Yes |
created_at | int | Yes |
updated_at | int | Yes |
requester_id | String | Yes |
target_user_id | String | No |
target_group_id | String | No |
status | String | Yes |
reason | String | Yes |
requested_duration_minutes | int | No |
requested_resources | List | Yes (may be empty) |
requested_groups | List | Yes (may be empty) |
custom_fields | Dict | Yes (may be empty) |