permission.for accepts ref() to grant on schemas, tables, and views that another part of a composite schema declares, such as a SQL file.
A permission block can now target an object that has no block in its HCL document. Write the target as ref("<kind>.<schema>.<name>") instead of a block reference. This lets a project keep its tables in SQL and its roles and grants in HCL, combined with a composite_schema.
Example
The tables are defined in SQL:
CREATE SCHEMA analytics;CREATE TABLE analytics.events (id int PRIMARY KEY);
The grants are defined in HCL and address the SQL objects with ref():
role "reader" {}permission {to = role.readerfor = ref("schema.analytics")privileges = [USAGE]}permission {to = role.readerfor = ref("table.analytics.events")privileges = [SELECT]}
The project config composes both files into one desired state:
data "composite_schema" "app" {schema {url = "file://schema.sql"}schema {url = "file://schema.hcl"}}env "local" {url = "postgres://postgres:pass@localhost:5432/app?sslmode=disable"dev = "docker://postgres/17/dev"schema {src = data.composite_schema.app.urlmode {roles = truepermissions = true}}}
Running atlas migrate diff --env local or atlas schema apply --env local plans the grants together with the objects they target:
-- Create role "reader"CREATE ROLE "reader";-- Add new schema named "analytics"CREATE SCHEMA "analytics";-- Grant on schema "analytics" to "reader"GRANT USAGE ON SCHEMA "analytics" TO "reader";-- Create "events" tableCREATE TABLE "analytics"."events" ("id" integer NOT NULL,PRIMARY KEY ("id"));-- Grant on table "events" to "reader"GRANT SELECT ON TABLE "analytics"."events" TO "reader";
Syntax
The first segment of the reference string is the object type. A schema reference takes only the name. Every other type must include the schema before its own name:
# Schema: the name onlyfor = ref("schema.sales")# Table, view, and materialized view: qualified with the schemafor = ref("table.sales.orders")for = ref("view.sales.daily")for = ref("materialized.sales.rollup")
An unqualified table or view reference, such as ref("table.orders"), fails with:
permission.for reference to undeclared table "orders" must be qualified as ref("table.<schema>.<name>")
Supported forms
| Database | schema | table | view | materialized |
|---|---|---|---|---|
| PostgreSQL | ✓ | ✓ | ✓ | ✓ |
| MySQL | ✓ | ✓ | ✓ | - |
| SQL Server | ✓ | ✓ | ✓ | - |
| Oracle | - | ✓ | ✓ | - |
| Databricks | ✓ | ✓ | ✓ | ✓ |
| Spanner | - | ✓ | ✓ | - |
| Snowflake | ✓ | ✓ | ✓ | - |
| Redshift | ✓ | ✓ | ✓ | ✓ |
| ClickHouse | ✓ | ✓ | ✓ | ✓ |
Support for more object kinds, such as functions and procedures, is in progress.
Behavior
- A ref() never creates, alters, or drops the object it names. Atlas compares only the grants on it and plans them as GRANT and REVOKE statements.
- Use ref() targets inside a composite schema. Both parts are replayed on the dev database, so the grants land on real objects and re-applying reports no changes.
- On Snowflake, to also accepts ref() for a role managed elsewhere, such as ref("role.app").
Roles and permissions require Atlas Pro. Run atlas login to enable them.