README
ΒΆ
terraform-gitlab-drift
Detect GitLab resources not managed by Terraform and generate Terraform code to bring them under management.
Uses the GitLab Terraform Provider resource definitions.
Features
- π Drift Detection: Scan GitLab groups and projects to identify resources not managed by Terraform
- π Code Generation: Automatically generate Terraform code for unmanaged resources
- π Diff Comparison: Show differences between existing and generated Terraform configurations
- π¦ Import Commands: Generate
terraform importcommands for new resources - π Merge Request Creation: Automatically create or update GitLab MRs with generated
.tffiles - π³ Docker-ready: Designed for CI/CD pipelines
Quick Start
Local Installation
go install github.com/xMoelletschi/terraform-gitlab-drift@latest
terraform-gitlab-drift scan --group my-group
GitLab CI Usage
Basic Drift Check
drift-check:
image: ghcr.io/xmoelletschi/terraform-gitlab-drift:latest
script:
- terraform-gitlab-drift scan --group $CI_PROJECT_ROOT_NAMESPACE
Auto-create MR on Drift
drift-remediation:
image: ghcr.io/xmoelletschi/terraform-gitlab-drift:latest
script:
- terraform-gitlab-drift scan --group $CI_PROJECT_ROOT_NAMESPACE --create-mr --show-diff=false
When drift is detected, this creates (or updates) a merge request in the current repository with the generated .tf files. The target repo is auto-detected from the git remote; use --target-repo to override.
Configuration
Command-line Flags
| Flag | Environment Variable | Default | Description |
|---|---|---|---|
--gitlab-token |
GITLAB_TOKEN |
- | GitLab API token (required) |
--gitlab-url |
- | https://gitlab.com |
GitLab instance URL |
--group |
- | - | Top-level group to scan (required for gitlab.com) |
--terraform-dir |
- | . |
Path to Terraform directory |
--overwrite |
- | false |
Overwrite files in terraform directory |
--show-diff |
- | true |
Show diff between generated and existing files |
--skip |
- | - | Resource types to skip (comma-separated). Use premium to skip all Premium-tier resources |
--include |
- | - | Resource types to opt back in (comma-separated). Use to enable resources skipped by default (currently branch_protection) |
--create-mr |
- | false |
Create a merge request with generated Terraform code |
--target-repo |
- | (auto-detected) | GitLab project path or ID for the MR |
--mr-branch |
- | drift/backtrack |
Branch name for the drift MR |
--mr-dest-path |
- | (root) | Path within target repo where .tf files go |
--verbose, -v |
- | false |
Enable verbose (debug) logging |
--json |
- | false |
Output logs in JSON format |
Directory Structure
The tool generates one .tf file per GitLab namespace, using normalized names (lowercase, / and - replaced with _). Your Terraform directory should follow this structure to get accurate drift detection:
terraform/
βββ backend.tf
βββ providers.tf
βββ my_group.tf # generated: top-level group + its projects
βββ my_group_sub_group.tf # generated: sub-group + its projects
βββ group_membership.tf # generated: variable with group β user memberships
βββ project_membership.tf # generated: variable with project β shared groups
βββ group_labels.tf # generated: variable with group β labels
βββ project_labels.tf # generated: variable with project β labels
βββ ci_variables.tf # generated: group and project CI/CD variables
βββ pipeline_schedules.tf # generated: variable with project β pipeline schedules
βββ hooks.tf # generated: project and group webhooks
βββ branch_protections.tf # generated: project branch protection rules
βββ ...
Important: The drift check only compares files that match the generated filenames. If you have resources defined in differently named files (e.g.
main.tf,projects.tf), they will not be detected and the tool will report those resources as unmanaged.To fix this, move your resource definitions into the files matching the generated naming convention, or use
--overwriteto let the tool manage the file structure for you.
Supported Resources
- β
GitLab Groups (
gitlab_group) - β
GitLab Group Memberships (
gitlab_group_membership) - β
GitLab Projects (
gitlab_project) - β
GitLab Project Share Groups (
gitlab_project_share_group) - β
GitLab Group Labels (
gitlab_group_label) - β
GitLab Project Labels (
gitlab_project_label) - β
GitLab Pipeline Schedules (
gitlab_pipeline_schedule) - β
GitLab Pipeline Schedule Variables (
gitlab_pipeline_schedule_variable) - β
GitLab Group Variables (
gitlab_group_variable) * - β
GitLab Project Variables (
gitlab_project_variable) * - β
GitLab Project Hooks (
gitlab_project_hook) - β
GitLab Group Hooks (
gitlab_group_hook) (requires Premium/Ultimate) - π§ GitLab Branch Protection (
gitlab_branch_protection) β skipped by default, opt in with--include branch_protection** - β
GitLab Project Job Token Scopes (
gitlab_project_job_token_scopes) - π§ More resources coming soon
* CI/CD Variable Filtering: Masked variables and file-type variables are automatically skipped. Masked variables are excluded because the GitLab API returns redacted values (
[MASKED]), which would produce invalid Terraform state. File-type variables are excluded because they typically contain sensitive data such as SSH private keys or certificates.Important: If you store secrets (SSH keys, API tokens, etc.) as
env_var-type CI/CD variables, they will be written to.tffiles in plaintext. To prevent this, store sensitive values as file-type variables β this is also GitLab's recommended approach for multi-line secrets like SSH keys, since they cannot be masked.** Branch Protection (skipped by default): The
gitlab_branch_protectionresource is currently skipped by default because of an upstream provider bug β the provider'sReadfunction does not populateunprotect_access_levelinto Terraform state on Free tier, which causes everyterraform planto mark every protected branch for forced replacement. Enable it explicitly with--include branch_protectiononce your instance is Premium/Ultimate or once the upstream provider bug is fixed.When enabled, the
allowed_to_push,allowed_to_merge,allowed_to_unprotect,unprotect_access_level, andcode_owner_approval_requiredattributes require a GitLab Premium/Ultimate instance. They are emitted by default but can be excluded with--skip premium. When skipped, only the free-tier attributes (push_access_level,merge_access_level,allow_force_push) are generated.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please make sure to:
- Add tests for new features
- Update documentation as needed
- Ensure CI checks pass
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Acknowledgments
Built with:
- Cobra - CLI framework
- GitLab Go SDK - GitLab API client
- HCL - Terraform configuration parsing
Note: This tool is not affiliated with HashiCorp or GitLab.
Documentation
ΒΆ
There is no documentation for this package.