Protected Terraform States: Add database migration and model [PART 1]

What does this MR do and why?

Create the data layer for Terraform state protection rules, enabling project maintainers to define rules that restrict who can write to a Terraform state based on project role and request source (PAT, CI job, CI on protected branch).

This is the foundational MR (1 of 7) for the protected Terraform states feature. It creates the terraform_state_protection_rules database table and the Terraform::StateProtectionRule model following the packages_protection_rules pattern.

What's included:

  • Database migration creating terraform_state_protection_rules with:
    • project_id FK (cascade delete)
    • state_name (text, max 255 chars) — exact match against Terraform state name
    • minimum_access_level_for_write (smallint enum: developer, maintainer, owner, admin)
    • allowed_from (smallint enum: anywhere, ci_only, ci_on_protected_branch_only)
    • Unique composite index on (project_id, state_name)
  • Model (Terraform::StateProtectionRule) with enums using Gitlab::Access constants, validations, and for_state_name scope
  • Project association: has_many :terraform_state_protection_rules
  • Factory and full model spec (14 examples)
  • Database dictionary YAML metadata

No behavior changes yet — rules can only be created via Rails console. Enforcement, CRUD services, API endpoints, and UI follow in subsequent MRs.

🛠️ with ❤️ at Siemens

References

Screenshots or screen recordings

Not applicable — backend-only changes.

How to set up and validate locally

  • Run the migration: bundle exec rails db:migrate
  • Open Rails console and create a rule:
    project = Project.first
    rule = project.terraform_state_protection_rules.create!(
      state_name: 'production',
      minimum_access_level_for_write: :maintainer,
      allowed_from: :ci_only
    )
  • Verify uniqueness — creating a second rule with the same project and state name raises ActiveRecord::RecordInvalid
  • Verify cascade delete — deleting the project also deletes associated protection rules
  • Verify the association: project.terraform_state_protection_rules returns the created rules
  • Verify the scope: Terraform::StateProtectionRule.for_state_name('production') returns matching rules

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

MR Checklist (@gerardo-navarro)

Merge request reports

Loading