Skip to content

Commit c7f07b7

Browse files
committed
Docs for stack role assumptions
Signed-off-by: peterdeme <[email protected]>
1 parent 9a9047a commit c7f07b7

32 files changed

+792
-313
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ For more complex contributions, we recommend that you open an issue to describe
1818
- Start a free trial at https://spacelift.io/free-trial.
1919
<!-- markdownlint-enable MD001 MD034 -->
2020

21+
## Miscellaneous
22+
2123
## License
2224

2325
The Spacelift user documentation is licensed under the [MIT license](./LICENSE).
55.9 KB
Loading

docs/concepts/authorization/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ RBAC operates on three fundamental concepts: actions, actors, and subjects.
2929
- **Users**: Individual team members authenticated through your identity provider.
3030
- **API Keys**: Programmatic access tokens for automation.
3131
- **IdP Groups**: Groups of users as defined by your identity provider.
32+
- **Stacks**: Stacks that can assume roles to make changes inside of Spacelift.
3233

3334
### Subjects
3435

3536
**Subjects** are the resources being acted upon. Examples include:
3637

37-
- **Stacks**: Infrastructure definitions and their runs.
38+
- **Stacks**: Stacks managing infrastructure and their runs.
3839
- **Contexts**: Collections of environment variables and files.
3940
- **Policies**: Rules that govern various Spacelift behaviors.
4041
- **Spaces**: Organizational containers for resources.
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
# Stack role bindings
2+
3+
Stacks can receive role bindings to perform operations with elevated permissions, similar to how users, API keys, and IdP groups receive permissions through [Spacelift's RBAC system](rbac-system.md).
4+
5+
Stack role attachments replace the deprecated [Administrative](../stack/stack-settings.md#administrative) flag, providing a more flexible, auditable, and powerful approach to granting stacks elevated permissions.
6+
7+
## Why use stack role attachments
8+
9+
Stack role attachments offer significant advantages over the deprecated administrative flag:
10+
11+
### Cross-space access
12+
13+
**Administrative flag limitation**: Can only create resources in the stack's own space and subspaces.
14+
15+
**Role attachments advantage**: Can attach roles to sibling spaces, enabling horizontal access across the space tree. In the example below, a stack in `ChildSpace1` can be granted access to `ChildSpace2`:
16+
17+
```mermaid
18+
graph TD
19+
A[root] --> B[ChildSpace1]
20+
A --> C[ChildSpace2]
21+
```
22+
23+
### Fine-grained access control
24+
25+
**Administrative flag limitation**: All-or-nothing approach - grants full Space Admin permission with every available permission.
26+
27+
**Role attachments advantage**: Use custom roles with specific permissions (for example, only `context:create` and `workerpool:create`).
28+
29+
This means a stack can create contexts and worker pools, but cannot manage any other resources, such as policies or webhooks.
30+
31+
### Enhanced audit trail
32+
33+
**Administrative flag**: Basic audit trail with stack actor information.
34+
35+
**Role attachments advantage**: Audit trail webhooks include role information in the `actor_roles` field (array of role slugs).
36+
37+
This provides better visibility into what permissions the stack was using when performing actions. See the [audit trail documentation](../../integrations/audit-trail.md#usage) for details.
38+
39+
### Modern RBAC consistency
40+
41+
Role attachments align stacks with the broader role-based access model already used by [users](./assigning-roles-users.md), [IdP groups](./assigning-roles-groups.md), and [API keys](./assigning-roles-api-keys.md), providing a consistent permission management experience across all actors.
42+
43+
## Assign roles to stacks
44+
45+
### Prerequisites
46+
47+
To attach a role to a stack, you need:
48+
49+
- `StackManage` [permission](./rbac-system.md#actions-the-building-blocks-of-permissions) (or Space admin permission as fallback) to the stack's space
50+
- Space admin permission to the binding space (the space where the role will be effective)
51+
52+
!!! info "Why both permissions are required"
53+
Creating a role binding that grants permissions to a space effectively allows the stack to act in that space. To prevent privilege escalation, you must have admin access to both spaces: the space where the stack resides and the space where the role will be effective.
54+
55+
### Using the Web UI
56+
57+
1. Navigate to the stack's **Settings** page, then choose **Roles** on the left
58+
2. Click **Manage Roles** on the top right
59+
3. In the sidebar, select the desired role and the target space
60+
4. Click **Add**
61+
62+
### Using the Terraform provider
63+
64+
Use the `spacelift_role_attachment` resource:
65+
66+
```hcl
67+
resource "spacelift_space" "devops" {
68+
name = "devops"
69+
description = "A space for devops engineers"
70+
parent_space_id = "root"
71+
}
72+
73+
resource "spacelift_stack" "devops_admin" {
74+
name = "Admin stacks for Devs"
75+
repository = "stacks-for-devs"
76+
space_id = spacelift_space.devops.id
77+
description = "Only has permissions to create another stacks in the dev space"
78+
branch = "main"
79+
}
80+
81+
resource "spacelift_role" "stack_creator" {
82+
name = "Stack creator"
83+
description = "A role solely for managing stacks"
84+
actions = ["STACK_MANAGE"]
85+
}
86+
87+
resource "spacelift_space" "dev" {
88+
name = "dev"
89+
description = "A space for development stacks"
90+
parent_space_id = "root"
91+
}
92+
93+
resource "spacelift_role_attachment" "stack_creator_to_devops_admin_stack" {
94+
stack_id = spacelift_stack.devops_admin.id # (1)
95+
role_id = spacelift_role.stack_creator.id # (2)
96+
space_id = spacelift_space.dev.id # (3)
97+
}
98+
```
99+
100+
1. The stack receiving the role attachment.
101+
2. The [role](./rbac-system.md#roles) to attach to the stack.
102+
3. The target space: this is where the role will be effective.
103+
104+
In the above scenario, the `devops_admin` stack will have the `Stack creator` role effective in the `dev` space, allowing it to create and manage stacks within that space.
105+
106+
For more information, see the [Spacelift Terraform provider documentation](https://search.opentofu.org/provider/spacelift-io/spacelift/latest/docs/resources/role_attachment){: rel="nofollow"}.
107+
108+
## Permission cascading
109+
110+
Role attachments cascade down to child spaces, similar to how the administrative flag worked:
111+
112+
```mermaid
113+
graph TD
114+
role{{Role}}
115+
parentSpace[ParentSpace]
116+
childSpace1[ChildSpace1]
117+
childSpace2[ChildSpace2]
118+
grandchildSpace[GrandchildSpace]
119+
120+
role ~~~ parentSpace
121+
role e1@-. Attached to .-> parentSpace
122+
e1@{animate: true}
123+
parentSpace --> childSpace1
124+
parentSpace --> childSpace2
125+
childSpace2 --> grandchildSpace
126+
```
127+
128+
If a role is attached to `ParentSpace`, the same role will be effective in `ChildSpace1`, `ChildSpace2`, and `GrandchildSpace` as well.
129+
130+
!!! danger "Root space caution"
131+
Since the `root` space is the parent of all spaces, attaching roles to it affects **all spaces** in your account. Use this with extreme caution and only when necessary.
132+
133+
### Root space restriction
134+
135+
You can only assign a role to the `root` space if the stack itself is located in the `root` space. This restriction prevents unintentional access elevation - a stack in a _child-of-root_ space cannot be granted permissions that cascade to all spaces in your account.
136+
If you need a stack in a child space to access resources across multiple spaces, attach roles to specific spaces rather than the root space.
137+
138+
## Administrative flag precedence
139+
140+
The administrative flag takes precedence over role attachments:
141+
142+
- If `administrative = true`, any attached roles will be **completely ineffective**
143+
- You **must** set `administrative = false` for role attachments to take effect
144+
- This is critical for migration - disable the administrative flag after creating role attachments
145+
146+
## Policy integration
147+
148+
Policies can react to stack role attachments through the `stack.roles` field in [policy inputs](../policy/approval-policy.md#data-input-schema). This enables policy-based logic based on what roles a stack has attached.
149+
150+
### Example: Reject Space Admin role usage
151+
152+
```rego
153+
package spacelift
154+
155+
reject_with_note["Don't use the Space Admin role!"] {
156+
role := input.stack.roles[_]
157+
role.id == "space-admin" # (1)
158+
}
159+
```
160+
161+
1. Role slug. Use either "Copy Slug" button in the UI or the [`spacelift_role` data source](https://search.opentofu.org/provider/spacelift-io/spacelift/latest/docs/datasources/role){: rel="nofollow"} to retrieve it.
162+
163+
## Multiple roles
164+
165+
Stacks can have multiple role bindings:
166+
167+
- Different roles in different spaces for varied access levels
168+
- Multiple roles in the same space (permissions are additive)
169+
- Combinations of Space Admin in own space and Reader in other spaces
170+
171+
### Example: Multiple role attachments
172+
173+
```hcl
174+
# Admin access to development space
175+
resource "spacelift_role_attachment" "dev_admin" {
176+
stack_id = spacelift_stack.platform.id
177+
role_id = "space-admin"
178+
space_id = "development-space-id"
179+
}
180+
181+
# Read access to production space
182+
resource "spacelift_role_attachment" "prod_reader" {
183+
stack_id = spacelift_stack.platform.id
184+
role_id = "space-reader"
185+
space_id = "production-space-id"
186+
}
187+
```
188+
189+
## External state access
190+
191+
External state access allows you to read the state of a stack from outside authorized runs and tasks. See the documentation [here](../../vendors/terraform/external-state-access.md) for further details.
192+
193+
In order for your stack to access another stack's OpenTofu/Terraform state, the stack needs to have **Space writer** role to the target stack's space. This can be achieved by attaching the **Space writer** role to the stack for the target stack's space.
194+
195+
Example:
196+
197+
```hcl
198+
resource "spacelift_stack" "consumer" {
199+
# Properties are omitted for brevity
200+
}
201+
202+
data "spacelift_role" "space_writer" {
203+
slug = "space-writer"
204+
}
205+
206+
resource "spacelift_role_attachment" "space_writer" {
207+
stack_id = spacelift_stack.consumer.id
208+
role_id = data.spacelift_role.space_writer.id
209+
space_id = spacelift_stack.provider.space_id
210+
}
211+
212+
resource "spacelift_stack" "provider" {
213+
# Properties are omitted for brevity
214+
terraform_external_state_access = true
215+
}
216+
```
217+
218+
!!! note
219+
The Space admin role also includes Space writer permissions.
220+
221+
## Migration from administrative flag
222+
223+
On June 1st, 2026, Spacelift will automatically disable all administrative flags and attach the Space Admin role to each stack's own space. This automatic migration is **100% backward compatible** and ensures no functionality loss.
224+
225+
However, **manual migration is strongly recommended** to avoid breaking the OpenTofu/Terraform state: since the administrative flag will be disabled during this process, the stacks will experience a drift. The administrative flag will be ineffective (even if set to true, it'll return false), so the only solution to reconcile will be to attach the Space Admin role to the stack's own space.
226+
227+
!!! note
228+
The Space admin role is a built-in system role so you don't need to create it manually, it already exists in your Spacelift account.
229+
230+
### What happens on June 1st, 2026
231+
232+
On June 1st, 2026, Spacelift will automatically:
233+
234+
- Disable all administrative flags
235+
- Attach the Space Admin role to each stack's own space (100% backward compatible)
236+
- Note: if you move the stack to a different space later, the role attachment remains unchanged and will not follow the stack's new space
237+
- Remove the administrative flag from the UI
238+
- Make the flag ineffective in the GraphQL API (even if set to `true`, it will behave as `false`)
239+
240+
### Step-by-step migration guide
241+
242+
#### 1. Identify stacks with administrative flag
243+
244+
List all stacks with `administrative = true` in your account. You can do this through the Spacelift UI (by filtering on the stacks page) or by searching your Terraform code for the `administrative` attribute.
245+
246+
#### 2. For each stack, create role attachment
247+
248+
Attach the Space Admin role to the stack using the stack's own space as the binding space:
249+
250+
**Using the Terraform provider:**
251+
252+
```hcl
253+
data "spacelift_role" "admin_role" {
254+
slug = "space-admin"
255+
}
256+
257+
resource "spacelift_role_attachment" "stack_admin" {
258+
stack_id = spacelift_stack.management.id
259+
space_id = spacelift_stack.management.space_id
260+
role_id = data.spacelift_role.admin_role.id
261+
}
262+
```
263+
264+
**Using the Web UI:**
265+
266+
1. Navigate to the stack's **Settings** page, then choose **Roles**
267+
2. Click **Manage Roles** on the top right
268+
3. In the drawer, select the **Space admin** role and the stack's own Space as the target
269+
4. Click **Add**
270+
271+
Assuming your stack is in the `dev` [Space](../spaces/README.md), the role attachment will grant **Space admin** permissions in the `dev` space:
272+
273+
![](../../assets/screenshots/role_stacks_migration.png)
274+
275+
#### 3. Remove the administrative attribute
276+
277+
Once you've verified the roles have been attached, remove the administrative attibute:
278+
279+
```diff
280+
resource "spacelift_stack" "management" {
281+
name = "Management Stack"
282+
- administrative = true
283+
}
284+
```
285+
286+
!!! important
287+
The administrative flag takes precedence over role attachments. If `administrative = true`, any attached roles will be ignored. You must either set `administrative = false`, or entirely remove the administrative attribute (recommended) for role attachments to take effect.
288+
289+
#### 4. Verify the role attachment
290+
291+
After creating the role attachment, verify that the stack can perform the same operations as before. Trigger a tracked run and ensure it succeeds.
292+
293+
#### 5. Adjust policies if necessary
294+
295+
If any of your policies reference the `stack.administrative` field, update them to use the `stack.roles` field instead. For example:
296+
297+
```rego
298+
# Old policy:
299+
deny["Administrative stacks are not allowed"] {
300+
stack := input.spacelift.stack
301+
stack.administrative == true
302+
}
303+
304+
# Would become:
305+
deny["Administrative stacks are not allowed"] {
306+
role := input.spacelift.stack.roles[_]
307+
role.id == "space-admin" # (1)
308+
}
309+
```
310+
311+
1. Role slug. Use either "Copy Slug" button in the UI or the [`spacelift_role` data source](https://search.opentofu.org/provider/spacelift-io/spacelift/latest/docs/datasources/role){: rel="nofollow"} to retrieve it.
312+
313+
### Rollback procedure
314+
315+
If you need to roll back during migration:
316+
317+
1. Set `administrative = true` again
318+
2. The administrative flag takes precedence, so role attachments will be ignored
319+
3. Test that everything works as before
320+
4. You can leave the role attachments in place and try migration again later
321+
322+
## Edge cases
323+
324+
### Stack moving between spaces
325+
326+
When a stack moves to a different space, existing role bindings remain unchanged. This is intentional and important for Terraform provider stability.
327+
328+
If you want to update role bindings after moving a stack, you need to explicitly modify the role attachments.

0 commit comments

Comments
 (0)