DELIVERY · CI/CD AND IAC

CI/CD and infrastructure-as-code delivery on STACKIT

Deliver the migrated infrastructure from a pipeline, not from a laptop: assess with a strict gate, keep the generated package as a build artifact, authenticate the STACKIT provider with a short-lived OIDC token instead of a stored key, put state in a backend you protect, and apply only a plan file somebody reviewed. This guide follows a small AWS estate through that chain and marks which parts come from a StackToIT run and which from the STACKIT provider documentation.

The delivery chain, in order

  1. 1. Assess in the pipeline

    Run the assessment with --strict and --format json. The report is written even when the gate fails, so the job keeps the output folder as an artifact and records the exit code beside it.

  2. 2. Verify the folder

    stacktoit verify checks the run against its manifest and checks that the generated files agree with each other. It is not proof that anything ran in a cloud, and it is not a signature.

  3. 3. Answer the open requests in files

    The run writes inputs-template.yaml and decisions-template.yaml into its output folder. Copy them outside the generated folder, fill them, commit them, and run again with --inputs and --decisions. A recorded review alone does not rewrite the draft.

  4. 4. Commit the reviewed draft

    The terraform folder pins the provider to one version and requires Terraform or OpenTofu 1.11 or later. Treat the draft as a proposal: it goes through a normal pull request.

  5. 5. Authenticate without a stored key

    Create a service account, attach a federated identity provider for your CI system, and let the provider exchange the pipeline's OIDC token. Keep the assertions as narrow as the token allows.

  6. 6. Choose and protect the state

    The draft declares no backend. An S3 backend on Object Storage works with the AWS-specific checks skipped, but locking does not; and the state holds some secrets in plain text.

  7. 7. Plan, review, apply the plan file

    Run init, validate and plan -out as separate steps, have a person read the plan, then apply that exact file.

  8. 8. Re-run after every change

    New inputs or decisions change the draft and the execution plan. Assess again and review the new plan rather than patching the old one.

The gate, as the run reports it

An estate of five resources written for this page, assessed by StackToIT on 4 November 2026 with --strict and --format json, with --no-validate, so OpenTofu validation was skipped. First: the two commands the StackToIT automation documentation gives for a gate, each verbatim from its own code block. Then: the verification event and the summary record the run printed, pretty-printed, with only the fields discussed here (the records hold more, such as run id, file list and step counts).

Pipeline step: strict gateStackToIT docs: CLI automation
stacktoit assess ./terraform --strict --format json > assessment-events.jsonl
Pipeline step: verifyStackToIT docs: CLI automation
stacktoit verify ./terraform-stacktoit --output json
Console output of the runassessment-events.jsonl
{
  "type": "event",
  "event_code": "RUN_VERIFIED",
  "phase": "publish",
  "severity": "INFO",
  "human_summary": "The package passed the checks of stacktoit verify (7 checks, PASSED) before it was published; the record is in run_verification of manifest.json."
}
{
  "type": "summary",
  "exit_code": 7,
  "exit_reasons": [
    {
      "code": 7,
      "reason": "PACKAGE_NOT_EXECUTION_READY",
      "detail": "package PARTIALLY_EXECUTABLE"
    }
  ],
  "run_status": "COMPLETE",
  "package": "FULL",
  "verification": "PASSED",
  "summary": {
    "package_state": "PARTIALLY_EXECUTABLE",
    "architect_approval": "NOT_PROVIDED",
    "safe_to_apply": "NOT_ASSERTED"
  }
}
  • Exit 7 is the strict gate working

    The package was complete and verified, yet the exit code is 7 with the reason that the package is not ready to execute: it is partially executable. Of its 12 automated steps, one waits for a design decision (the boot image), and none can run as written until project_id has a value. A first assessment will normally fail a strict gate; that is the point of the gate.

  • Treat the code and the artifact together

    The documentation asks automation to record the exit code and keep the generated files where they exist. Other codes: 3 means validation rejected the source or the draft, 4 an incomplete source, 5 a locked output folder, 6 a failed verification.

  • Approval stays a human field

    The summary shows architect approval not provided and safe-to-apply not asserted. A pipeline does not fill those in; a person does, through the decisions file.

What the draft hands to the pipeline

Same run. Left: terraform/terraform.tf, which pins the STACKIT provider. Right: the commands execution/terraform/apply.md lists. The run applies nothing; the file says nothing in this folder has been run.

STACKIT Terraform by StackToITterraform/terraform.tf
terraform {
  # Write-only attributes (e.g. a VPN pre-shared key) need Terraform/OpenTofu 1.11 or later.
  required_version = ">= 1.11.0"

  required_providers {
    stackit = {
      source  = "stackitcloud/stackit"
      version = "0.121.0"
    }
  }
}

provider "stackit" {
  default_region = var.region
}
Commands in the packageexecution/terraform/apply.md
cd terraform
tofu init
tofu validate
tofu plan -out=stackit.tfplan
tofu apply stackit.tfplan
  • One provider version

    The draft pins the provider to a single version, so every pipeline run plans against the same schema. Raise the pin on purpose, in a pull request.

  • Variables without defaults stay open

    The project ID, the image, the machine type and the boot volume size have no default. Supply them through a variables file or TF_VAR environment variables, never in a committed file when they are sensitive.

  • Manual steps and a two-run apply

    apply.md opens with "Do not apply yet": one OpenTofu step waits for a decision. It has a section for manual steps that must be complete before tofu apply (none in this run), because OpenTofu applies the whole draft at once and does not enforce them, and here it splits the apply into two runs: the two security groups first, then two STACKIT API calls that delete default egress rules, then the rest.

Authentication with workload identity federation

Verbatim from the STACKIT provider's Workload Identity Federation guide, not produced by a run. Left: the federated identity provider the guide configures on the service account for its own repository (replace the repository and branch in sub with yours). Right: the plan and apply steps of its GitHub Actions example; the job also needs the id-token: write permission.

Federated identity provider (GitHub)Provider guide: Workload Identity Federation
- **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
Workflow stepsProvider guide: GitHub Actions workflow
      - name: Terraform Plan
        run: |
          terraform plan -out=tfplan
        env:
          STACKIT_USE_OIDC: "1"
          STACKIT_SERVICE_ACCOUNT_EMAIL: "terraform-example@sa.stackit.cloud"

      - name: Terraform Apply
        run: terraform apply -auto-approve tfplan
        env:
          STACKIT_USE_OIDC: "1"
          STACKIT_SERVICE_ACCOUNT_EMAIL: "terraform-example@sa.stackit.cloud"
  • No static secret

    The provider documents this flow as the way that removes the need for long-lived secrets in CI/CD. It looks for the OIDC setting in the provider block, then in environment variables, then in a credentials file. The guide's example runs Terraform and applies with -auto-approve in the same job; the run's package uses tofu, and this page did not check the flow under OpenTofu. Put the plan review between the two steps.

  • The audience is mandatory

    The STACKIT documentation requires an assertion on the audience; only the equality operator is supported, and a token is accepted only when every assertion holds. The guide advises the most restrictive assertions available.

  • Other CI systems

    The guide covers GitHub Actions and Azure DevOps and says the principles may apply to other CI/CD platforms that support OIDC.

Stages, settings and gates

StageCommand or settingGate and what to keep
Assessstacktoit assess ./terraform --strict --format jsonExit 0 is a published report; 7 is a strict failure. Keep the output folder either way.
Verifystacktoit verify RUN_DIRExit 0 passes, 6 reports findings. Compare two runs with --same-as.
Review inputs--inputs and --decisions files in version controlReviews tied to a different source may be reported as stale.
AuthenticateSTACKIT_USE_OIDC and STACKIT_SERVICE_ACCOUNT_EMAILKey flow stays available through STACKIT_SERVICE_ACCOUNT_KEY_PATH.
StateS3 backend on Object Storage, or a PostgreSQL backenduse_lockfile has no effect on STACKIT Object Storage: run one apply job at a time.
Plan and applytofu plan -out, then tofu apply on the plan fileStore the plan; a person approves before apply.

The run did not test locking on any backend, and it says so. The state also holds the Object Storage secret access key in plain text, so the state is itself a secret.

Decisions the architect owns

  • Who approves the plan

    A reviewer from the owning team, with the plan file attached to the change. The run records approval as not provided until a person answers.

  • State location and encryption

    The run advises encrypting the state and limiting read access to the pipeline, and points to OpenTofu state encryption or an encrypted backend.

  • One job at a time

    Without working locks, serialise applies in the pipeline configuration.

  • Service account permissions

    The service account needs the roles of the services the draft uses; the requirements file names no role, so look up the minimum for each service.

  • Strict or advisory gate

    Decide whether exit 7 blocks the merge or only labels it. As practice: advisory for a first assessment, strict for the apply stage.

Delivery questions

Does StackToIT apply anything from the pipeline?

No. Assess reads and writes files. Terraform or OpenTofu applies, run by your pipeline or a person.

Why does my strict gate fail on a clean assessment?

Because strict mode turns partial and unverified outcomes into failures. A package that is partially executable, with decisions open, returns exit 7 although the report and the verification are fine.

Can I lock state on Object Storage?

Not with the S3 backend's lock file: the provider documentation states it has no effect on STACKIT Object Storage and points to a PostgreSQL backend for locking.

Should the pipeline hold a service account key?

Prefer workload identity federation. The key flow exists, needs a service account key file, and the key is a long-lived secret.

Where do secrets in the state come from?

The pinned provider writes some attributes, such as an Object Storage credential's secret access key, into the state. sensitive hides them from the plan output only.

Sources

Your next move: STACKIT.

Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.