Back to changelog
New
•2 minute read

Grants on Objects Declared Outside HCL

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:

schema.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():

schema.hcl
role "reader" {}
permission {
to = role.reader
for = ref("schema.analytics")
privileges = [USAGE]
}
permission {
to = role.reader
for = ref("table.analytics.events")
privileges = [SELECT]
}

The project config composes both files into one desired state:

atlas.hcl
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.url
mode {
roles = true
permissions = 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" table
CREATE 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 only
for = ref("schema.sales")
# Table, view, and materialized view: qualified with the schema
for = 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

Databaseschematableviewmaterialized
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.

featurepermissionscomposite schemaatlas pro