Atlas now models the options a role membership is granted with, not just who is a member of what. A new membership block on role and user blocks declares options such as WITH ADMIN OPTION, with inspection, diffing, and planning support on PostgreSQL, MySQL, ClickHouse, Redshift, and Oracle.
A grant like GRANT reader TO vault WITH ADMIN OPTION used to be inspected as a plain membership: the option was invisible to schema inspect, absent from migrate diff, and dropped on apply. Roles that differed only by their grant options were considered identical, so Atlas never planned the change.
The membership Block
Options are declared with a repeatable membership block, available on both role and user blocks:
role "reader" {}role "writer" {} user "vault" { member_of = [role.reader] membership { role = role.writer admin = true }}
member_of keeps working as before and remains the form for a membership with no options. Atlas emits a membership block only where an option is actually set, so inspecting an existing database produces the same compact output it always did. The two forms mix freely on the same block, and Atlas plans them as the exact statements the engine accepts:
-- Grant role "reader" to "vault"GRANT "reader" TO "vault";-- Grant role "writer" to "vault"GRANT "writer" TO "vault" WITH ADMIN OPTION;
What Each Engine Supports
| Engine | Options | Notes |
|---|---|---|
| PostgreSQL | admin, inherit, set | inherit and set require PostgreSQL 16 |
| MySQL | admin | MySQL 8.0 and above |
| ClickHouse | admin | ON CLUSTER is carried through all membership statements |
| Redshift | admin | On roles granted to users only |
| Oracle | admin | Role-to-role memberships |
An option the server cannot hold fails at plan time instead of being silently dropped: inherit or set below PostgreSQL 16, and admin on a Redshift group or role-to-role grant. SQL Server, Snowflake, Spanner, and Databricks expose no options on memberships, so member_of remains the only form there.
Diffing and Modifying Options
Diffing respects server defaults. admin defaults to false, set to true, and inherit to the member role's own inherit attribute, so an option that matches the default never produces a change. When one does change, PostgreSQL 16 and above modifies it in place and states only what changed:
-- Modify the membership of "vault" in "reader"GRANT "reader" TO "vault" WITH INHERIT TRUE;-- Modify the membership of "vault" in "writer"GRANT "writer" TO "vault" WITH ADMIN FALSE, SET FALSE;
Re-granting a membership does not clear an option it already holds, so each engine gets the sequence it requires. MySQL has no statement for revoking the option alone, so dropping it revokes the membership and grants it back:
-- Modify the membership of "vault" in "reader"GRANT `reader` TO `vault` WITH ADMIN OPTION;-- Modify the membership of "vault" in "writer"REVOKE `writer` FROM `vault`;-- Modify the membership of "vault" in "writer"GRANT `writer` TO `vault`;
Oracle takes the same revoke and re-grant path, while ClickHouse, and PostgreSQL below 16, drop the option in place with REVOKE ADMIN OPTION FOR.
Getting Started
Role and user management is part of Atlas Pro:
$ atlas loginSee the HCL reference for every attribute, and the security guides for full walkthroughs per engine.