Protected terraform states: GraphQL API for protection rules

Problem Statement

As part of the Protected Terraform States epic, we are adding protection rules that control who can write to a Terraform state and from where.

This issue covers the GraphQL API for querying and managing Terraform state protection rules. This API is also a prerequisite for the frontend UI that will allow maintainers to configure protection rules from the project settings.

Proposal

Expose terraformStateProtectionRules on the Project GraphQL type and provide create, update, and delete mutations.

Querying protection rules

query {
  project(fullPath: "my-group/my-project") {
    terraformStateProtectionRules {
      nodes {
        id
        stateName
        minimumAccessLevelForWrite
        allowedFrom
      }
    }
  }
}

Returns all protection rules for the project. Unauthorized users receive empty results.

Creating a protection rule

mutation {
  createTerraformStateProtectionRule(input: {
    projectPath: "my-group/my-project"
    stateName: "production"
    minimumAccessLevelForWrite: MAINTAINER
    allowedFrom: CI_ON_PROTECTED_BRANCH_ONLY
  }) {
    terraformStateProtectionRule { id stateName minimumAccessLevelForWrite allowedFrom }
    errors
  }
}
  • stateName — the Terraform state to protect (must be unique per project)
  • minimumAccessLevelForWrite — minimum role for write operations: DEVELOPER, MAINTAINER, OWNER, or ADMIN
  • allowedFrom — source restriction (optional, defaults to ANYWHERE): ANYWHERE, CI_ONLY, or CI_ON_PROTECTED_BRANCH_ONLY

Updating a protection rule

mutation {
  updateTerraformStateProtectionRule(input: {
    id: "gid://gitlab/Terraform::StateProtectionRule/1"
    minimumAccessLevelForWrite: OWNER
  }) {
    terraformStateProtectionRule { id stateName minimumAccessLevelForWrite allowedFrom }
    errors
  }
}

Supports partial updates — only provided fields are changed.

Deleting a protection rule

mutation {
  deleteTerraformStateProtectionRule(input: {
    id: "gid://gitlab/Terraform::StateProtectionRule/1"
  }) {
    terraformStateProtectionRule { id stateName }
    errors
  }
}

Authorization

  • Query: Maintainer+ sees rules; Developer and below sees empty results
  • Mutations: Maintainer+ can create/update/delete; lower roles receive a permission error
  • Follows the same admin_terraform_state permission as existing Terraform state mutations

Feature flag

Gated behind terraform_state_protection_rules (gitlab_com_derisk).

Scope

In scope:

  • GraphQL type for protection rules with fields: id, stateName, minimumAccessLevelForWrite, allowedFrom
  • Enums for access levels and source restrictions
  • Query via project { terraformStateProtectionRules }
  • Create, update (partial), and delete mutations
  • Specs for all of the above

Out of scope:

  • REST API for rules CRUD (separate issue)
  • Frontend UI (separate issue)
  • Internal events / instrumentation (separate issue)

Reference

This follows the packages protection rules GraphQL pattern.

Implementation checklist

  • GraphQL type exposing protection rule fields
  • Enums for access levels (DEVELOPER, MAINTAINER, OWNER, ADMIN) and source restrictions (ANYWHERE, CI_ONLY, CI_ON_PROTECTED_BRANCH_ONLY)
  • Query on ProjectType to list protection rules
  • Create mutation
  • Update mutation with partial update support
  • Delete mutation
  • Specs for types, enums, query, and all mutations