Skip to content

Commit 3619253

Browse files
committed
Docs for stack role assumptions
1 parent f3b0e99 commit 3619253

29 files changed

+450
-97
lines changed
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: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
# Stack role attachments
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 `ActorRoles` 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) 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 (or `SpaceAdmin` permission as fallback) to the stack's space
50+
- `SpaceAdmin` 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_stack" "admin" {
68+
name = "Admin stack"
69+
repository = "spacelift-resources"
70+
description = "Only has permissions to create another stacks in the dev space"
71+
branch = "main"
72+
}
73+
74+
resource "spacelift_role" "stack_creator" {
75+
name = "Stack creator"
76+
description = "A role solely for creating stacks"
77+
actions = ["STACK_MANAGE"]
78+
}
79+
80+
resource "spacelift_space" "dev" {
81+
name = "dev"
82+
parent_space_id = "root"
83+
}
84+
85+
resource "spacelift_role_attachment" "stack_creator_to_admin_stack" {
86+
stack_id = spacelift_stack.admin.id
87+
role_id = spacelift_role.stack_creator.id
88+
space_id = spacelift_space.dev.id
89+
}
90+
```
91+
92+
For more information, see the [Spacelift Terraform provider documentation](https://search.opentofu.org/provider/spacelift-io/spacelift/latest/docs/resources/role_attachment){: rel="nofollow"}.
93+
94+
## Permission cascading
95+
96+
Role attachments cascade down to child spaces, similar to how the administrative flag worked:
97+
98+
```text
99+
parentSpace
100+
├── childSpace1
101+
└── childSpace2
102+
└── grandchildSpace
103+
```
104+
105+
If a role is attached to `parentSpace`, the same role will be effective in `childSpace1`, `childSpace2`, and `grandchildSpace`.
106+
107+
!!! warning "Root space caution"
108+
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.
109+
110+
## Administrative flag precedence
111+
112+
The administrative flag takes precedence over role attachments:
113+
114+
- If `administrative = true`, any attached roles will be **completely ineffective**
115+
- You **must** set `administrative = false` for role attachments to take effect
116+
- This is critical for migration - disable the administrative flag after creating role attachments
117+
118+
## Policy integration
119+
120+
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.
121+
122+
### Example: Reject Space Admin role usage
123+
124+
```rego
125+
package spacelift
126+
127+
reject_with_note["Don't use the Space Admin role!"] {
128+
role := input.stack.roles[_]
129+
role.id == "space-admin" # Role slug. Use the "Copy Slug" button in the UI to get it.
130+
}
131+
```
132+
133+
## Multiple roles
134+
135+
Stacks can have multiple role bindings:
136+
137+
- Different roles in different spaces for varied access levels
138+
- Multiple roles in the same space (permissions are additive)
139+
- Combinations of Space Admin in own space and Reader in other spaces
140+
141+
### Example: Multiple role attachments
142+
143+
```hcl
144+
# Admin access to development space
145+
resource "spacelift_role_attachment" "dev_admin" {
146+
stack_id = spacelift_stack.platform.id
147+
role_id = "space-admin"
148+
space_id = "development-space-id"
149+
}
150+
151+
# Read access to production space
152+
resource "spacelift_role_attachment" "prod_reader" {
153+
stack_id = spacelift_stack.platform.id
154+
role_id = "space-reader"
155+
space_id = "production-space-id"
156+
}
157+
```
158+
159+
## External state access
160+
161+
External state access allows you to read the state of the stack from outside authorized runs and [tasks](../../concepts/run/task.md). See the documentation [here](../../vendors/terraform/external-state-access.md) for further details.
162+
163+
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.
164+
165+
Example:
166+
167+
```hcl
168+
data "spacelift_role" "space_writer" {
169+
slug = "space-writer"
170+
}
171+
172+
resource "spacelift_role_attachment" "space_writer" {
173+
stack_id = spacelift_stack.consumer.id
174+
role_id = data.spacelift_role.space_writer.id
175+
space_id = spacelift_stack.provider.space_id
176+
}
177+
```
178+
179+
!!! note
180+
The **Space admin** role also includes **Space writer** permissions.
181+
182+
## Migration from administrative flag
183+
184+
!!! warning "Automatic Migration on June 1st, 2026"
185+
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.
186+
187+
However, **manual migration is strongly recommended** to take advantage of advanced features like cross-space access and fine-grained permissions that are not available with automatic migration.
188+
189+
### What happens on June 1st, 2026
190+
191+
On June 1st, 2026, Spacelift will automatically:
192+
193+
- Disable all administrative flags
194+
- Attach the Space Admin role to each stack's own space (100% backward compatible)
195+
- 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
196+
- Remove the administrative flag from the UI
197+
- Make the flag ineffective in the GraphQL API (even if set to `true`, it will behave as `false`)
198+
199+
### Step-by-step migration guide
200+
201+
#### 1. Identify stacks with administrative flag
202+
203+
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.
204+
205+
#### 2. For each stack, create role attachment
206+
207+
Attach the Space Admin role to the stack using the stack's own space as the binding space:
208+
209+
**Using the Terraform provider:**
210+
211+
```hcl
212+
data "spacelift_role" "admin_role" {
213+
slug = "space-admin"
214+
}
215+
216+
resource "spacelift_role_attachment" "stack_admin" {
217+
stack_id = spacelift_stack.management.id
218+
space_id = spacelift_stack.management.space_id
219+
role_id = data.spacelift_role.admin_role.id
220+
}
221+
```
222+
223+
**Using the Web UI:**
224+
225+
1. Navigate to the stack's **Settings** page, then choose **Roles**
226+
2. Click **Manage Roles** on the top right
227+
3. In the drawer, select the **Space admin** role and the stack's own Space as the target
228+
4. Click **Add**
229+
230+
Assuming your stack is in the `dev` [Space](../spaces/README.md), the role attachment will grant **Space admin** permissions in the `dev` space:
231+
232+
![](../../assets/screenshots/role_stacks_migration.png)
233+
234+
#### 3. Disable the administrative flag
235+
236+
Once you've verified the roles have been attached, disable the administrative flag:
237+
238+
```hcl
239+
resource "spacelift_stack" "management" {
240+
# [... other stack settings ...]
241+
administrative = false
242+
}
243+
```
244+
245+
!!! important
246+
The administrative flag takes precedence over role attachments. If `administrative = true`, any attached roles will be ignored. You must set `administrative = false` for role attachments to take effect.
247+
248+
#### 4. Verify the role attachment
249+
250+
After creating the role attachment, verify that the stack can perform the same operations as before. Trigger a tracked run and ensure it succeeds.
251+
252+
#### 5. Adjust policies if necessary
253+
254+
If any of your policies reference the `stack.administrative` field, update them to use the `stack.roles` field instead. For example:
255+
256+
```rego
257+
# Old policy:
258+
deny_with_note["Administrative stacks are not allowed"] {
259+
stack := input.stack
260+
stack.administrative == true
261+
}
262+
263+
# Would become:
264+
deny_with_note["Administrative stacks are not allowed"] {
265+
role := input.stack.roles[_]
266+
role.id == "space-admin" # Role slug. Use the "Copy Slug" button in the UI to get it.
267+
}
268+
```
269+
270+
### Rollback procedure
271+
272+
If you need to roll back during migration:
273+
274+
1. Set `administrative = true` again
275+
2. The administrative flag takes precedence, so role attachments will be ignored
276+
3. Test that everything works as before
277+
4. You can leave the role attachments in place and try migration again later
278+
279+
## Edge cases
280+
281+
### Stack moving between spaces
282+
283+
When a stack moves to a different space, existing role bindings remain unchanged. This is intentional and important for Terraform provider stability.
284+
285+
If you want to update role bindings after moving a stack, you need to explicitly modify the role attachments.

docs/concepts/authorization/rbac-system.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The legacy system used three broad roles:
1919
Existing legacy system role assignments have been automatically migrated to equivalent custom RBAC roles:
2020

2121
| Legacy Role | RBAC Equivalent |
22-
|-------------|-----------------|
22+
| ----------- | --------------- |
2323
| Reader | Space Reader |
2424
| Writer | Space Writer |
2525
| Admin | Space Admin |
@@ -104,13 +104,19 @@ Groups of users as defined by your identity provider.
104104
- Base permissions from functional groups.
105105
- Additional project-specific permissions from project groups.
106106

107+
#### Stacks
108+
109+
[Stacks](../stack/README.md) that can assume roles to manage resources programmatically inside Spacelift via the [Spacelift Terraform Provider](https://search.opentofu.org/provider/spacelift-io/spacelift/latest){: rel="nofollow"}.
110+
111+
For more information, see [Assigning Roles to Stacks](./assigning-roles-stacks.md).
112+
107113
### Actions: the building blocks of permissions
108114

109115
Actions are the smallest unit of permission granularity in Spacelift's RBAC system. Each action defines a specific
110116
operation that can be performed:
111117

112118
| Action | Description | Legacy Equivalent |
113-
|------------------|----------------------------|-------------------|
119+
| ---------------- | -------------------------- | ----------------- |
114120
| `run:trigger` | Trigger stack runs | Writer |
115121
| `stack:manage` | Create and modify stacks | Admin |
116122
| `stack:delete` | Delete stacks | Admin |
@@ -126,7 +132,7 @@ operation that can be performed:
126132

127133
Subjects are the resources that actors interact with, for example:
128134

129-
- **Stacks**: Infrastructure definitions, runs, and associated metadata.
135+
- **Stacks**: Stacks managing infrastructure, runs, and associated metadata.
130136
- **Contexts**: Environment variables, mounted files, and configuration collections.
131137
- **Policies**: Rules governing Spacelift behavior (approval, notification, etc.).
132138

@@ -269,3 +275,4 @@ View more detailed instructions for assigning roles to:
269275
- [Individual users](./assigning-roles-users.md)
270276
- [API keys](./assigning-roles-api-keys.md)
271277
- [IdP groups](./assigning-roles-groups.md)
278+
- [Stacks](./assigning-roles-stacks.md)

docs/concepts/blueprint/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
This feature is only available on the Business plan and above. Please check out our [pricing page](https://spacelift.io/pricing){: rel="nofollow"} for more information.
66
{% endif %}
77

8-
There are multiple ways to create [stacks](../stack/README.md) in Spacelift. We recommend using [our Terraform provider](../../vendors/terraform/terraform-provider.md) to programmatically create stacks using an [administrative](../stack/stack-settings.md#administrative) stack.
8+
There are multiple ways to create [stacks](../stack/README.md) in Spacelift. We recommend using [our Terraform provider](../../vendors/terraform/terraform-provider.md) to programmatically create stacks using a stack with appropiate [role attachments](../authorization/assigning-roles-stacks.md).
99

1010
However, some users might not be comfortable using Terraform code to create stacks. This is where Blueprints come in handy.
1111

@@ -17,7 +17,7 @@ You can configure the following resources in a Blueprint:
1717

1818
- All [stack settings](../stack/stack-settings.md) including:
1919
- Name, description, labels, [space](../spaces/README.md).
20-
- **Behavioral settings**: administrative, auto-apply, auto-destroy, hooks, runner image, etc.
20+
- **Behavioral settings**: auto-apply, auto-destroy, hooks, runner image, etc.
2121
- [VCS configuration](../../integrations/source-control/README.md).
2222
- Both default and space-level VCS integrations. In GitHub, custom app installations can only be created by GitHub Enterprise accounts.
2323
- Vendor configuration for your IaC provider.
@@ -169,7 +169,7 @@ inputs:
169169
- Owner/${{ context.user.login }}
170170
- Blueprint/${{ context.blueprint.name }}
171171
- Space/${{ context.blueprint.space }}
172-
administrative: false
172+
administrative: false # Deprecated in favor of role attachments
173173
allow_promotion: false
174174
auto_deploy: false
175175
auto_retry: false

docs/concepts/configuration/context.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ This step enables you to attach the context to projects without navigating to th
8080

8181
In this step, you have the option to configure environment variables and mounted files if you already have them ready during the creation of the context.
8282

83-
| Environment variables | Mounted files |
84-
| - | - |
83+
| Environment variables | Mounted files |
84+
| --------------------------------------------------------------- | --------------------------------------------------------------- |
8585
| ![](../../assets/screenshots/context/creation_form_step_2a.png) | ![](../../assets/screenshots/context/creation_form_step_2b.png) |
8686

8787
#### Step 4 - Add hooks (optional)
@@ -293,11 +293,11 @@ A variation of this use case is collections of [OpenTofu/Terraform input variabl
293293
If the data in the context is produced by one or more Spacelift stacks, contexts can be an attractive alternative to the Terraform [remote state](https://www.terraform.io/docs/providers/terraform/d/remote_state.html){: rel="nofollow"}. In this use case, contexts can serve as outputs for stacks that can be consumed by (attached to) other stacks. So, instead of exposing the entire state, a stack can use Spacelift Terraform provider to define values on a context - either managed by the same stack , or managed externally. Managing a context externally can be particularly useful when multiple stacks contribute to a particular context.
294294

295295
!!! info
296-
In order to use the Terraform provider to define contexts or its configuration elements the stack has to be marked as [administrative](../stack/README.md#administrative).
296+
In order to use the Terraform provider to define contexts or its configuration elements the stack has to have appropiate [role attachments](../authorization/assigning-roles-stacks.md).
297297

298298
As an example of one such use case, let's imagine an organization where shared infrastructure (VPC, DNS, compute cluster etc.) is centrally managed by a DevOps team, which exposes it as a service to be used by individual product development teams. In order to be able to use the shared infrastructure, each team needs to address multiple entities that are generated by the central infra repo. In vanilla Terraform one would likely use remote state provider, but that might expose secrets and settings the DevOps team would rather keep it to themselves. Using a context on the other hand allows the team to decide (and hopefully document) what constitutes their "external API".
299299

300-
The proposed setup for the above use case would involve two administrative stacks - one to manage all the stacks, and the other for the DevOps team. The management stack would programmatically define the DevOps one, and possibly also its context. The DevOps team would receive the context ID as an input variable, and use it to expose outputs as [`spacelift_environment_variable`](https://github.com/spacelift-io/terraform-provider-spacelift#spacelift_environment_variable-resource){: rel="nofollow"} and/or [`spacelift_mounted_file`](https://github.com/spacelift-io/terraform-provider-spacelift#spacelift_mounted_file-resource){: rel="nofollow"} resources. The management stack could then simply attach the context populated by the DevOps stack to other stacks it defines and manages.
300+
The proposed setup for the above use case would involve two [stacks with roles](../authorization/assigning-roles-stacks.md) - one to manage all the stacks, and the other for the DevOps team. The management stack would programmatically define the DevOps one, and possibly also its context. The DevOps team would receive the context ID as an input variable, and use it to expose outputs as [`spacelift_environment_variable`](https://github.com/spacelift-io/terraform-provider-spacelift#spacelift_environment_variable-resource){: rel="nofollow"} and/or [`spacelift_mounted_file`](https://github.com/spacelift-io/terraform-provider-spacelift#spacelift_mounted_file-resource){: rel="nofollow"} resources. The management stack could then simply attach the context populated by the DevOps stack to other stacks it defines and manages.
301301

302302
### Extending Terraform CLI Configuration (Terraform-specific)
303303

0 commit comments

Comments
 (0)