Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1d11ca6
initial commit
JorTurFer Jan 6, 2026
8c10c0d
request id token for GH
JorTurFer Jan 6, 2026
26f6e80
.
JorTurFer Jan 6, 2026
def1119
.
JorTurFer Jan 6, 2026
9573017
.
JorTurFer Jan 6, 2026
ddbed49
.
JorTurFer Jan 6, 2026
b9069f1
Add audience parameter to GitHub request query
JorTurFer Jan 7, 2026
643b975
Update provider.go
JorTurFer Jan 7, 2026
03bfee1
Fix return type in URL query parsing error
JorTurFer Jan 7, 2026
4f95b62
support passing the oidc token directly
JorTurFer Jan 7, 2026
fd1b10e
.
JorTurFer Jan 7, 2026
69d904a
.
JorTurFer Jan 7, 2026
625c0bb
Update tmp.yaml
JorTurFer Jan 7, 2026
18378f3
.
JorTurFer Jan 7, 2026
02aff3b
.
JorTurFer Jan 7, 2026
457fd2e
use SDK function
JorTurFer Jan 7, 2026
29617cf
use SDK function
JorTurFer Jan 7, 2026
ddedace
use SDK function
JorTurFer Jan 7, 2026
ab9898f
use SDK function
JorTurFer Jan 7, 2026
4d5e383
lint the code
JorTurFer Jan 7, 2026
692e953
update docs
JorTurFer Jan 7, 2026
b825333
bump sdk
JorTurFer Jan 12, 2026
1c5a0d0
bump sdk
JorTurFer Jan 20, 2026
53e8786
use core sdk
JorTurFer Jan 21, 2026
841e37c
Update docs/index.md
JorTurFer Feb 3, 2026
dadceb9
apply feedback
JorTurFer Feb 3, 2026
d7163f5
fix wrong rebase
JorTurFer Feb 3, 2026
ca57f1c
fix wrong rebase
JorTurFer Feb 3, 2026
a758393
updat4 docs
JorTurFer Feb 3, 2026
4e90c4a
Add guide
JorTurFer Feb 10, 2026
1868320
Update docs
JorTurFer Feb 10, 2026
0e7f40f
update validation
JorTurFer Feb 10, 2026
aa8c0a4
Merge branch 'main' into prepare-wif
JorTurFer Feb 10, 2026
78bc381
fix linting
JorTurFer Feb 10, 2026
38c49a4
remove testing flow
JorTurFer Feb 10, 2026
bdfc572
fix linting
JorTurFer Feb 10, 2026
6f26de1
remove testing flow
JorTurFer Feb 10, 2026
83bd761
feedback
JorTurFer Feb 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions docs/guides/workload_identity_federation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
page_title: "Workload Identity Federation with GitHub Actions"
---

# Workload Identity Federation with GitHub Actions

Workload Identity Federation (WIF) allows you to authenticate the STACKIT Terraform provider without using long-lived Service Account keys.
This is particularly useful in CI/CD environments like **GitHub Actions**, **GitLab CI**, or **Azure DevOps**, where you can use short-lived
OIDC tokens. This guide focuses on using WIF with GitHub Actions, but the principles may apply to other CI/CD platforms that support OIDC.

## Prerequisites

Before using Workload Identity Federation flow, you need to:
1. [Create](https://docs.stackit.cloud/platform/access-and-identity/service-accounts/how-tos/manage-service-accounts/) a **Service Account** on STACKIT.

## Setup Workload Identity Federation

WIF can be configured to trust any public OIDC provider following the [docs page](https://docs.stackit.cloud/platform/access-and-identity/service-accounts/how-tos/manage-service-account-federations/#create-a-federated-identity-provider)
but for the purpose of this guide we will focus on GitHub Actions as OIDC provider. GitHub Actions supports OIDC authentication using
the public issuer "https://token.actions.githubusercontent.com" (for GH Enterprise you should check your issuer URL) and setting repository and action information
as part of the OIDC token claims. [More info here](https://docs.github.com/es/actions/concepts/security/openid-connect).

Using this provider [repository](https://github.com/stackitcloud/terraform-provider-stackit) (stackitcloud/terraform-provider-stackit) as example and assuming that we want to
execute terraform on the main branch, we will configure the service account "Federated identity Provider" with the following configuration:
- **Provider Name**: GitHub # This is just an example, you can choose any name you want
- **Issuer URL**: https://token.actions.githubusercontent.com # This is the public issuer for GitHub Actions OIDC tokens
- **Assertions**:
- **sub**->equals->repo:stackitcloud/terraform-provider-stackit:ref:refs/heads/main # This is the repository and branch where the action will run
- **aud**->equals->sts.accounts.stackit.cloud # Mandatory value

> Note: You can use more fine-grained assertions just adding them. More info about OIDC token claims in [GitHub](https://docs.github.com/en/actions/reference/security/oidc)

## Provider Configuration

To use WIF, you must set an `use_oidc` flag to `true` as well as provide an OIDC token for the exchange. While you can provide the token directly in the configuration
through `service_account_federated_token`, this is not recommended for GitHub Actions as the provider will automatically fetch the token from the GitHub OIDC.

In addition to this, you need to set the `service_account_email` to specify which service account you want to use. This is mandatory as the provider needs to know which service account to exchange the token for.

```hcl
provider "stackit" {
service_account_email = "[email protected]"
use_oidc = true
... # Other provider configuration
}
```

### Using Environment Variables (Recommended)

In most CI/CD scenarios, the cleanest way is to set the `STACKIT_SERVICE_ACCOUNT_EMAIL` environment variable as well as `STACKIT_USE_OIDC="1"` to enable the WIF flow. This way you don't need to
change your provider configuration and the provider will automatically fetch the OIDC token and exchange it for a short-lived access token.

## Example GitHub Actions Workflow

> Note: To request OIDC tokens, you need to [grant this permission to the GitHub Actions workflow](https://docs.github.com/en/actions/reference/security/oidc#required-permission).

```yaml
name: Workload Identity Federation with STACKIT

on:
push:
branches:
- '**'

jobs:
demo-job:
name: Workload Identity Federation with STACKIT
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: false

- name: Create Test Configuration
run: |
cat <<EOF > main.tf
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
}
}
}

provider "stackit" {
default_region = "eu01"
}

resource "stackit_service_account" "sa" {
project_id = "e1925fbf-5272-497a-8298-1586760670de"
name = "terraform-example-ci"
}
EOF

- name: Terraform Init
run: |
terraform init
env:
STACKIT_USE_OIDC: "1"
STACKIT_SERVICE_ACCOUNT_EMAIL: "[email protected]"

- name: Terraform Plan
run: |
terraform plan -out=tfplan
env:
STACKIT_USE_OIDC: "1"
STACKIT_SERVICE_ACCOUNT_EMAIL: "[email protected]"

- name: Terraform Apply
run: terraform apply -auto-approve tfplan
env:
STACKIT_USE_OIDC: "1"
STACKIT_SERVICE_ACCOUNT_EMAIL: "[email protected]"
```
59 changes: 37 additions & 22 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@ provider "stackit" {

# Authentication

# Token flow (scheduled for deprecation and will be removed on December 17, 2025)
# Workload Identity Federation flow
provider "stackit" {
default_region = "eu01"
service_account_token = var.service_account_token
default_region = "eu01"
service_account_email = var.service_account_email
service_account_federated_token = var.service_account_federated_token
use_oidc = true
}

# Workload Identity Federation flow (using path)
provider "stackit" {
default_region = "eu01"
service_account_email = var.service_account_email
service_account_federated_token_path = var.service_account_federated_token_path
use_oidc = true
}

# Key flow
Expand All @@ -36,13 +46,13 @@ provider "stackit" {

To authenticate, you will need a [service account](https://docs.stackit.cloud/platform/access-and-identity/service-accounts/). Create it in the [STACKIT Portal](https://portal.stackit.cloud/) and assign the necessary permissions to it, e.g. `project.owner`. There are multiple ways to authenticate:

- Key flow (recommended)
- Token flow (is scheduled for deprecation and will be removed on December 17, 2025)
- Workload Identity Federation
- Key flow

When setting up authentication, the provider will always try to use the key flow first and search for credentials in several locations, following a specific order:
When setting up authentication, the provider will always try to use the workload identity federation flow first and search for credentials in several locations, following a specific order:

1. Explicit configuration, e.g. by setting the field `service_account_key_path` in the provider block (see example below)
2. Environment variable, e.g. by setting `STACKIT_SERVICE_ACCOUNT_KEY_PATH`
1. Explicit configuration, e.g. by setting the field `use_oidc` in the provider block (see example below)
2. Environment variable, e.g. by setting `STACKIT_USE_OIDC`
3. Credentials file

The provider will check the credentials file located in the path defined by the `STACKIT_CREDENTIALS_PATH` env var, if specified,
Expand All @@ -51,12 +61,23 @@ When setting up authentication, the provider will always try to use the key flow

```json
{
"STACKIT_SERVICE_ACCOUNT_TOKEN": "foo_token",
"STACKIT_SERVICE_ACCOUNT_KEY_PATH": "path/to/sa_key.json",
"STACKIT_PRIVATE_KEY_PATH": "path/to/private_key.pem"
}
```

### Workload Identity Federation

The following instructions assume that you have created a service account and assigned the necessary permissions to it, e.g. `project.owner`.

When using Workload Identity Federation (WIF), you don't need a static service account secret or key. Instead, the provider exchanges a short-lived OIDC token (from GitHub Actions, GitLab CI, etc.) for a STACKIT access token. This is the most secure way to authenticate in CI/CD environments as it eliminates the need for long-lived secrets.

WIF can be configured to trust any public OIDC provider following the [official documentation](https://docs.stackit.cloud/platform/access-and-identity/service-accounts/how-tos/manage-service-account-federations/#create-a-federated-identity-provider).

To use WIF, set the `use_oidc` flag to `true` and provide an OIDC token for the exchange. While you can provide the token directly via `service_account_federated_token`, this is **not recommended for GitHub Actions**, as the provider will automatically fetch the token from the environment. For a complete setup, see our [Workload Identity Federation guide](./guides/workload_identity_federation.md).

In addition to this, you must set the `service_account_email` to specify which service account to impersonate.

### Key flow

The following instructions assume that you have created a service account and assigned the necessary permissions to it, e.g. `project.owner`.
Expand All @@ -67,7 +88,7 @@ When creating the service account key, a new pair can be created automatically,

**Optionally**, you can provide your own private key when creating the service account key, which will then require you to also provide it explicitly to the [STACKIT Terraform Provider](https://github.com/stackitcloud/terraform-provider-stackit), additionally to the service account key. Check the STACKIT Docs for an [example of how to create your own key-pair](https://docs.stackit.cloud/platform/access-and-identity/service-accounts/how-tos/manage-service-account-keys/).

To configure the key flow, follow this steps:
To configure the key flow, follow these steps:

1. Create a service account key:

Expand Down Expand Up @@ -109,17 +130,6 @@ To configure the key flow, follow this steps:
> - setting the environment variable: `STACKIT_PRIVATE_KEY_PATH`
> - setting `STACKIT_PRIVATE_KEY_PATH` in the credentials file (see above)


### Token flow

> Is scheduled for deprecation and will be removed on December 17, 2025.

Using this flow is less secure since the token is long-lived. You can provide the token in several ways:

1. Setting the field `service_account_token` in the provider
2. Setting the environment variable `STACKIT_SERVICE_ACCOUNT_TOKEN`
3. Setting it in the credentials file (see above)

# Backend configuration

To keep track of your terraform state, you can configure an [S3 backend](https://developer.hashicorp.com/terraform/language/settings/backends/s3) using [STACKIT Object Storage](https://docs.stackit.cloud/products/storage/object-storage).
Expand Down Expand Up @@ -172,6 +182,8 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
- `mongodbflex_custom_endpoint` (String) Custom endpoint for the MongoDB Flex service
- `objectstorage_custom_endpoint` (String) Custom endpoint for the Object Storage service
- `observability_custom_endpoint` (String) Custom endpoint for the Observability service
- `oidc_request_token` (String) The bearer token for the request to the OIDC provider. For use when authenticating as a Service Account using OpenID Connect.
- `oidc_request_url` (String) The URL for the OIDC provider from which to request an ID token. For use when authenticating as a Service Account using OpenID Connect.
- `opensearch_custom_endpoint` (String) Custom endpoint for the OpenSearch service
- `postgresflex_custom_endpoint` (String) Custom endpoint for the PostgresFlex service
- `private_key` (String) Private RSA key used for authentication, relevant for the key flow. It takes precedence over the private key that is included in the service account key.
Expand All @@ -185,7 +197,9 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
- `server_backup_custom_endpoint` (String) Custom endpoint for the Server Backup service
- `server_update_custom_endpoint` (String) Custom endpoint for the Server Update service
- `service_account_custom_endpoint` (String) Custom endpoint for the Service Account service
- `service_account_email` (String, Deprecated) Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource.
- `service_account_email` (String) Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource. This value is required using OpenID Connect authentication.
- `service_account_federated_token` (String) The OIDC ID token for use when authenticating as a Service Account using OpenID Connect.
- `service_account_federated_token_path` (String) Path for workload identity assertion. It can also be set using the environment variable STACKIT_FEDERATED_TOKEN_FILE.
- `service_account_key` (String) Service account key used for authentication. If set, the key flow will be used to authenticate all operations.
- `service_account_key_path` (String) Path for the service account key used for authentication. If set, the key flow will be used to authenticate all operations.
- `service_account_token` (String, Deprecated) Token used for authentication. If set, the token flow will be used to authenticate all operations.
Expand All @@ -194,3 +208,4 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
- `ske_custom_endpoint` (String) Custom endpoint for the Kubernetes Engine (SKE) service
- `sqlserverflex_custom_endpoint` (String) Custom endpoint for the SQL Server Flex service
- `token_custom_endpoint` (String) Custom endpoint for the token API, which is used to request access tokens when using the key flow
- `use_oidc` (Boolean) Should OIDC be used for Authentication? This can also be sourced from the `STACKIT_USE_OIDC` Environment Variable. Defaults to `false`.
16 changes: 13 additions & 3 deletions examples/provider/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ provider "stackit" {

# Authentication

# Token flow (scheduled for deprecation and will be removed on December 17, 2025)
# Workload Identity Federation flow
provider "stackit" {
default_region = "eu01"
service_account_token = var.service_account_token
default_region = "eu01"
service_account_email = var.service_account_email
service_account_federated_token = var.service_account_federated_token
use_oidc = true
}

# Workload Identity Federation flow (using path)
provider "stackit" {
default_region = "eu01"
service_account_email = var.service_account_email
service_account_federated_token_path = var.service_account_federated_token_path
use_oidc = true
}

# Key flow
Expand Down
21 changes: 11 additions & 10 deletions stackit/internal/conversion/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package conversion

import (
"context"
"crypto/tls"
"net/http"
"reflect"
"testing"

Expand Down Expand Up @@ -306,6 +308,9 @@ func TestParseProviderData(t *testing.T) {
}

func TestParseEphemeralProviderData(t *testing.T) {
var randomRoundTripper http.RoundTripper = &http.Transport{
TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS13},
}
type args struct {
providerData any
}
Expand Down Expand Up @@ -354,21 +359,17 @@ func TestParseEphemeralProviderData(t *testing.T) {
name: "valid provider data 2",
args: args{
providerData: core.EphemeralProviderData{
PrivateKey: "",
PrivateKeyPath: "/home/dev/foo/private-key.json",
ServiceAccountKey: "",
ServiceAccountKeyPath: "/home/dev/foo/key.json",
TokenCustomEndpoint: "",
ProviderData: core.ProviderData{
RoundTripper: randomRoundTripper,
},
},
},
want: want{
ok: true,
providerData: core.EphemeralProviderData{
PrivateKey: "",
PrivateKeyPath: "/home/dev/foo/private-key.json",
ServiceAccountKey: "",
ServiceAccountKeyPath: "/home/dev/foo/key.json",
TokenCustomEndpoint: "",
ProviderData: core.ProviderData{
RoundTripper: randomRoundTripper,
},
},
},
wantErr: false,
Expand Down
8 changes: 1 addition & 7 deletions stackit/internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,11 @@ const (

type EphemeralProviderData struct {
ProviderData

PrivateKey string
PrivateKeyPath string
ServiceAccountKey string
ServiceAccountKeyPath string
TokenCustomEndpoint string
}

type ProviderData struct {
RoundTripper http.RoundTripper
ServiceAccountEmail string // Deprecated: ServiceAccountEmail is not required and will be removed after 12th June 2025.
ServiceAccountEmail string
// Deprecated: Use DefaultRegion instead
Region string
DefaultRegion string
Expand Down
Loading
Loading