Policies
On this page
Policies define what actions a caller can perform on which resources. This guide explains the JSON structure, the evaluation logic (Allow vs. Deny), wildcard matching, resource formats, and ready-to-use examples.
See also: Permissions Reference
TL;DR (key takeaways)
- Default is deny; you need an explicit Allow to pass, and any matching Deny wins.
- Actions and resources match exactly or by trailing wildcard (e.g.,
compute:*
,exc:compute:instance/*
). - Many list endpoints pass
resource=""
; scope using Action only in those cases. - Managed policies (global) always apply in addition to org policies.
- Changes take up to ~10s to take effect due to caching.
What is a policy
- A policy is a JSON document with
Version
and an array ofStatements
. - Statements decide whether a request is allowed or denied based on
Action
andResource
.
Policy JSON structure
- PolicyDocument
- Version: string (optional)
- Statements: array of StatementEntry (required)
- StatementEntry
- Sid: string (optional label)
- Effect: “Allow” or “Deny” (required)
- Action: string or string[] (required)
- Resource: string or string[] (required; use “*” if not scoping)
- Condition: any (present but not enforced today)
Example Admin policy (full access):
{
"Version": "2024-03-05",
"Statements": [
{ "Sid": "stmt1", "Effect": "Allow", "Action": "*", "Resource": "*" }
]
}
Evaluation model
- Default is deny. Access is only granted if:
- At least one statement matches AND Effect == Allow, AND
- No matching statement has Effect == Deny.
- All policies bound to the caller are merged. Policies apply if:
- policy.OrgID == request’s orgId, OR
- policy.IsManaged == true (global managed policies).
- Matching is case-insensitive.
Matching rules
- Exact or prefix wildcard matching:
compute:instance:list
matches exactly that action.compute:*
matches all compute actions.exc:compute:instance/*
matches any instance with that resource prefix.*
matches everything.
- Wildcards are trailing-only:
prefix*
. - If a handler passes empty resource (
""
), resource matching is skipped; onlyAction
is checked.
Caching and propagation
- Policy sets are cached per caller for ~10 seconds.
- After creating/updating/deleting/binding a policy, allow up to 10s for enforcement to reflect.
Managed vs. Org policies
- Managed policies (
IsManaged=true
) are global and always apply. - Org policies apply only within that org.
Resource naming conventions
- Compute
- Instance lifecycle:
exc:compute:instance/<id>
- Instance connect:
compute:instance:connect/<id>
- Security group:
exc:compute:securitygroup/<id>
- Interface (for SG bindings list):
exc:compute:interface/<id>
- Security group rule:
exc:compute:securitygroup:rule/<id>
- SSH public key:
exc:compute:sshpubkey/<id>
- Many list actions use
resource=""
, so resource scoping doesn’t apply there.
- Instance lifecycle:
- DNS
- Zone:
exc:dns:zone/<zoneName>
- Zone:
- Database
- Cluster:
exc:database:cluster/<id>
- Node:
exc:database:node/<id>
- Cluster:
- IAM, Billing
- Typically
resource="*"
(or""
), not resource-scoped in current handlers.
- Typically
Resource formats cheat sheet
Service | Resource format | Example |
---|---|---|
Compute (instance) | exc:compute:instance/<id> | exc:compute:instance/42 |
Compute (connect) | compute:instance:connect/<id> | compute:instance:connect/42 |
Compute (security group) | exc:compute:securitygroup/<id> | exc:compute:securitygroup/sg-1 |
Compute (sg rule) | exc:compute:securitygroup:rule/<id> | exc:compute:securitygroup:rule/r-10 |
Compute (interface) | exc:compute:interface/<id> | exc:compute:interface/eni-1 |
Compute (ssh key) | exc:compute:sshpubkey/<id> | exc:compute:sshpubkey/k-1 |
DNS (zone) | exc:dns:zone/<zoneName> | exc:dns:zone/example.com |
Database (cluster) | exc:database:cluster/<id> | exc:database:cluster/db-1 |
Database (node) | exc:database:node/<id> | exc:database:node/n-1 |
Current action catalog
- Compute
- Instances:
compute:instance:create
,compute:instance:list
,compute:instance:start
,compute:instance:restart
,compute:instance:stop
,compute:instance:terminate
,compute:instance:connect
- Security groups:
compute:securitygroup:create
,compute:securitygroup:list
,compute:securitygroup:delete
- SG bindings:
compute:securitygroup:binding:create
,compute:securitygroup:binding:list
,compute:securitygroup:binding:delete
- SG rules:
compute:securitygroup:rule:create
,compute:securitygroup:rule:list
,compute:securitygroup:rule:delete
- SSH keys:
compute:sshpubkey:create
,compute:sshpubkey:list
,compute:sshpubkey:delete
- Subnets:
compute:subnet:list
- Volumes:
compute:volume:create
,compute:volume:list
,compute:volume:resize
,compute:volume:delete
- Snapshots:
compute:snapshot:create
,compute:snapshot:list
,compute:snapshot:delete
- Instances:
- DNS
dns:record:create
,dns:record:update
,dns:record:delete
,dns:record:list
dns:zone:create
,dns:zone:delete
,dns:zone:list
- Database
database:cluster:create
,database:cluster:restart
,database:cluster:terminate
,database:cluster:resetpassword
,database:cluster:list
database:node:add
,database:node:restart
,database:node:terminate
- IAM
iam:account:invite
,iam:account:list
iam:serviceaccount:create
,iam:serviceaccount:list
,iam:serviceaccount:update
,iam:serviceaccount:delete
iam:policy:create
,iam:policy:list
,iam:policy:update
,iam:policy:delete
iam:policy:binding:create
,iam:policy:binding:list
,iam:policy:binding:delete
iam:billing:get
,iam:billing:update
iam:org:rename
- Billing
billing:ca
(Cost Explorer)
Note: Some informational endpoints don’t enforce policies (e.g., instance types or images). Those are not listed as actions.
Common policy recipes
Project admin (all within an org):
{
"Version": "2024-03-05",
"Statements": [
{ "Sid": "all-compute", "Effect": "Allow", "Action": "compute:*", "Resource": "*" },
{ "Sid": "all-dns", "Effect": "Allow", "Action": "dns:*", "Resource": "*" },
{ "Sid": "all-db", "Effect": "Allow", "Action": "database:*", "Resource": "*" },
{ "Sid": "iam-basic", "Effect": "Allow", "Action": [
"iam:serviceaccount:create", "iam:serviceaccount:list", "iam:serviceaccount:update", "iam:serviceaccount:delete",
"iam:policy:create", "iam:policy:list", "iam:policy:update", "iam:policy:delete", "iam:policy:binding:create", "iam:policy:binding:list", "iam:policy:binding:delete",
"iam:account:list"
], "Resource": "*" },
{ "Sid": "billing-read", "Effect": "Allow", "Action": ["iam:billing:get", "billing:ca"], "Resource": "*" }
]
}
DNS admin:
{
"Version": "2024-03-05",
"Statements": [
{ "Sid": "zones", "Effect": "Allow", "Action": ["dns:zone:create", "dns:zone:delete", "dns:zone:list"], "Resource": "*" },
{ "Sid": "records", "Effect": "Allow", "Action": ["dns:record:create", "dns:record:update", "dns:record:delete", "dns:record:list"], "Resource": "*" }
]
}
Compute operator (no deletes):
{
"Version": "2024-03-05",
"Statements": [
{ "Sid": "instances", "Effect": "Allow", "Action": [
"compute:instance:create", "compute:instance:list", "compute:instance:start", "compute:instance:restart", "compute:instance:stop", "compute:instance:connect"
], "Resource": "*" },
{ "Sid": "volumes", "Effect": "Allow", "Action": [
"compute:volume:create", "compute:volume:list", "compute:volume:resize"
], "Resource": "*" },
{ "Sid": "securitygroups", "Effect": "Allow", "Action": [
"compute:securitygroup:create", "compute:securitygroup:list",
"compute:securitygroup:binding:create", "compute:securitygroup:binding:list",
"compute:securitygroup:rule:create", "compute:securitygroup:rule:list"
], "Resource": "*" }
]
}
Billing-only:
{
"Version": "2024-03-05",
"Statements": [
{ "Sid": "billing", "Effect": "Allow", "Action": ["iam:billing:get", "billing:ca"], "Resource": "*" }
]
}
Ready-to-use policies
Strict ReadOnly (no connect):
{
"Version": "2024-03-05",
"Statements": [
{
"Sid": "read-only",
"Effect": "Allow",
"Action": [
"compute:instance:list",
"compute:securitygroup:list",
"compute:securitygroup:binding:list",
"compute:securitygroup:rule:list",
"compute:sshpubkey:list",
"compute:subnet:list",
"compute:volume:list",
"compute:snapshot:list",
"dns:zone:list",
"dns:record:list",
"database:cluster:list",
"iam:policy:list",
"iam:policy:binding:list",
"iam:serviceaccount:list",
"iam:account:list",
"iam:billing:get",
"billing:ca"
],
"Resource": "*"
}
]
}
ReadOnly + Connect (allows ephemeral terminal access):
{
"Version": "2024-03-05",
"Statements": [
{
"Sid": "read-and-connect",
"Effect": "Allow",
"Action": [
"compute:instance:list",
"compute:securitygroup:list",
"compute:securitygroup:binding:list",
"compute:securitygroup:rule:list",
"compute:sshpubkey:list",
"compute:subnet:list",
"compute:volume:list",
"compute:snapshot:list",
"compute:instance:connect",
"dns:zone:list",
"dns:record:list",
"database:cluster:list",
"iam:policy:list",
"iam:policy:binding:list",
"iam:serviceaccount:list",
"iam:account:list",
"iam:billing:get",
"billing:ca"
],
"Resource": "*"
}
]
}
Scoped example: allow viewing SG bindings for one security group:
{
"Version": "2024-03-05",
"Statements": [
{
"Sid": "view-one-sg",
"Effect": "Allow",
"Action": ["compute:securitygroup:binding:list", "compute:securitygroup:rule:list"],
"Resource": ["exc:compute:securitygroup/123"]
}
]
}
Deny carve-out example (deny SSH key listing everywhere):
{
"Version": "2024-03-05",
"Statements": [
{
"Sid": "allow-read",
"Effect": "Allow",
"Action": ["*"],
"Resource": ["*"]
},
{
"Sid": "block-ssh-key-list",
"Effect": "Deny",
"Action": ["compute:sshpubkey:list"],
"Resource": ["*"]
}
]
}
Authoring guidelines
- Start least-privileged; add only needed actions.
- Use Resource scoping where possible; otherwise keep
Resource="*"
. - Apply Deny for must-not rules; Deny always wins.
- Avoid legacy or non-enforced actions (e.g.,
iam:policy:bind
,compute:instance:describe
,database:cluster:describe
,compute:subnet:create
,orgs:create
).
Validation rules
- Action/Resource strings are lowercase; accept string or array.
- Wildcards: only trailing
*
supported. - Require at least one statement.
- Effect must be
Allow
orDeny
. - Changes take up to ~10s to take effect (caching).
FAQ
- Why do my changes not apply immediately?
- Policies are cached for ~10s per caller.
- Why am I denied even though an Allow is present?
- A matching Deny exists; Deny overrides Allow.
- How do I scope to a specific resource?
- Use the resource formats above (e.g.,
exc:compute:instance/42
).
- Use the resource formats above (e.g.,