Bird
Raised Fist0
Terraformcloud~5 mins

Terraform in GitLab CI - Commands & Configuration

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Introduction
Terraform helps you create and change cloud resources automatically. GitLab CI runs Terraform commands in a pipeline to apply these changes safely and repeatedly.
When you want to automatically create cloud servers every time you update your code.
When you need to check if your cloud setup is correct before making changes.
When you want to share your cloud setup steps with your team in a repeatable way.
When you want to keep track of changes to your cloud resources in your code repository.
When you want to avoid manual mistakes by automating cloud resource updates.
Config File - .gitlab-ci.yml
.gitlab-ci.yml
stages:
  - validate
  - plan
  - apply

variables:
  TF_ROOT: "terraform"
  TF_STATE_BUCKET: "my-terraform-state"
  TF_STATE_KEY: "example/terraform.tfstate"
  TF_BACKEND_CONFIG: "bucket=${TF_STATE_BUCKET} key=${TF_STATE_KEY} region=us-east-1"

validate:
  stage: validate
  image: hashicorp/terraform:1.5.7
  script:
    - cd $TF_ROOT
    - terraform init -backend-config="$TF_BACKEND_CONFIG"
    - terraform validate

plan:
  stage: plan
  image: hashicorp/terraform:1.5.7
  script:
    - cd $TF_ROOT
    - terraform init -backend-config="$TF_BACKEND_CONFIG"
    - terraform plan -out=tfplan
  artifacts:
    paths:
      - $TF_ROOT/tfplan

apply:
  stage: apply
  image: hashicorp/terraform:1.5.7
  script:
    - cd $TF_ROOT
    - terraform init -backend-config="$TF_BACKEND_CONFIG"
    - terraform apply -auto-approve tfplan
  when: manual
  dependencies:
    - plan

This GitLab CI file defines three stages: validate, plan, and apply.

Variables set the Terraform folder and backend state storage details.

Each job uses the official Terraform Docker image version 1.5.7.

The validate job checks the Terraform files for errors.

The plan job creates a plan file showing what changes Terraform will make.

The apply job applies the changes from the plan file and is set to run manually to avoid accidental changes.

Commands
Push your Terraform code and GitLab CI file to the main branch to start the pipeline.
Terminal
git push origin main
Expected OutputExpected
Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 8 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 350 bytes | 350.00 KiB/s, done. Total 3 (delta 2), reused 0 (delta 0) remote: Resolving deltas: 100% (2/2), completed with 2 local objects. To https://gitlab.com/yourusername/yourrepo.git abc1234..def5678 main -> main
Initialize Terraform in the terraform folder with backend config to store state remotely in S3-compatible storage.
Terminal
terraform init -backend-config="bucket=my-terraform-state key=example/terraform.tfstate region=us-east-1"
Expected OutputExpected
Initializing the backend... Successfully configured the backend "s3"! Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes.
-backend-config - Specifies backend storage details for Terraform state.
Check the Terraform files for syntax errors and correctness before planning or applying.
Terminal
terraform validate
Expected OutputExpected
Success! The configuration is valid.
Create a plan file that shows what changes Terraform will make to the cloud resources.
Terminal
terraform plan -out=tfplan
Expected OutputExpected
Refreshing Terraform state in-memory prior to plan... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_instance.example will be created + resource "aws_instance" "example" { + ami = "ami-12345678" + instance_type = "t2.micro" } Plan: 1 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ This plan was saved to: tfplan To perform exactly these actions, run the following command to apply: terraform apply "tfplan"
-out - Saves the plan to a file for later apply.
Apply the changes from the saved plan file automatically without asking for confirmation.
Terminal
terraform apply -auto-approve tfplan
Expected OutputExpected
aws_instance.example: Creating... aws_instance.example: Still creating... [10s elapsed] aws_instance.example: Creation complete after 20s [id=i-0abcd1234efgh5678] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
-auto-approve - Skips interactive approval before applying.
Key Concept

If you remember nothing else from this pattern, remember: automate Terraform commands in GitLab CI stages to safely validate, plan, and apply cloud changes.

Common Mistakes
Running terraform apply without a saved plan file.
This can cause unexpected changes because the apply runs a new plan without review.
Always run terraform plan -out=tfplan first and then terraform apply tfplan.
Not initializing Terraform with the correct backend config in each job.
Terraform won't know where to store or read the state, causing errors or state conflicts.
Run terraform init with the same backend-config in every job before plan or apply.
Setting apply job to run automatically without manual approval.
This risks unintended changes to cloud resources on every pipeline run.
Set apply job to manual trigger to review plan before applying.
Summary
Push Terraform code and GitLab CI config to trigger the pipeline.
Use terraform init with backend config to set up remote state storage.
Run terraform validate to check code correctness.
Run terraform plan to create a change plan and save it.
Run terraform apply manually using the saved plan to update cloud resources safely.

Practice

(1/5)
1. What is the main purpose of using Terraform in a GitLab CI pipeline?
easy
A. To write application code
B. To automate the creation and management of cloud resources
C. To monitor server performance
D. To manage user access permissions

Solution

  1. Step 1: Understand Terraform's role

    Terraform is a tool designed to automate cloud infrastructure setup and changes.
  2. Step 2: Understand GitLab CI's role

    GitLab CI automates running tasks like Terraform commands in a pipeline.
  3. Final Answer:

    To automate the creation and management of cloud resources -> Option B
  4. Quick Check:

    Terraform automates cloud resource management = B [OK]
Hint: Terraform manages infrastructure automatically in CI pipelines [OK]
Common Mistakes:
  • Confusing Terraform with application code tools
  • Thinking GitLab CI monitors servers directly
  • Mixing user access management with infrastructure automation
2. Which GitLab CI stage is typically used to check Terraform configuration syntax before planning?
easy
A. deploy
B. apply
C. validate
D. build

Solution

  1. Step 1: Identify Terraform stages in GitLab CI

    Common stages are validate, plan, and apply.
  2. Step 2: Match stage to syntax check

    The validate stage checks Terraform files for syntax errors before any changes.
  3. Final Answer:

    validate -> Option C
  4. Quick Check:

    Syntax check stage = validate [OK]
Hint: Validate stage checks syntax before planning [OK]
Common Mistakes:
  • Confusing apply with validation
  • Using deploy which is not a Terraform stage
  • Thinking build is related to Terraform syntax
3. Given this GitLab CI snippet:
stages:
  - validate
  - plan
  - apply

validate:
  script:
    - terraform validate

plan:
  script:
    - terraform plan -out=tfplan

apply:
  script:
    - terraform apply tfplan
  when: manual

What happens when the pipeline reaches the apply stage?
medium
A. Terraform waits for manual approval before applying changes
B. Terraform apply is skipped because of manual trigger
C. Terraform plan is rerun before applying
D. Terraform applies changes automatically without user input

Solution

  1. Step 1: Understand the 'when: manual' keyword

    This setting means the apply job waits for a user to start it manually.
  2. Step 2: Check apply stage behavior

    Apply will not run automatically; it requires manual approval to proceed.
  3. Final Answer:

    Terraform waits for manual approval before applying changes -> Option A
  4. Quick Check:

    Manual apply means wait for approval = D [OK]
Hint: 'when: manual' means manual approval needed [OK]
Common Mistakes:
  • Assuming apply runs automatically
  • Thinking manual means skip permanently
  • Confusing plan rerun with apply stage
4. You have this GitLab CI job:
apply:
  script:
    - terraform apply tfplan
  when: manual
  only:
    - main

But the apply job runs on every branch, not just main. What is the likely cause?
medium
A. The 'only' keyword is deprecated and ignored; use 'rules' instead
B. The 'when: manual' overrides branch filtering
C. The job name 'apply' is reserved and runs always
D. The pipeline is misconfigured and needs a restart

Solution

  1. Step 1: Recognize GitLab CI syntax changes

    GitLab deprecated 'only' in favor of 'rules' for better control.
  2. Step 2: Understand effect on job filtering

    Using 'only' may not filter branches correctly, causing job to run everywhere.
  3. Final Answer:

    The 'only' keyword is deprecated and ignored; use 'rules' instead -> Option A
  4. Quick Check:

    Use 'rules' not 'only' for branch filters [OK]
Hint: 'only' is deprecated; use 'rules' for branch filters [OK]
Common Mistakes:
  • Thinking 'when: manual' affects branch filtering
  • Believing job names control execution
  • Restarting pipeline without fixing config
5. You want to ensure Terraform plans only run on merge requests and applies only happen after manual approval on the main branch. Which GitLab CI configuration snippet achieves this?
hard
A.
plan:
  script:
    - terraform plan
  only:
    - merge_requests

apply:
  script:
    - terraform apply
  only:
    - main
  when: manual
B.
plan:
  script:
    - terraform plan
  only:
    - main

apply:
  script:
    - terraform apply
  when: manual
C.
plan:
  script:
    - terraform plan
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: always

apply:
  script:
    - terraform apply
  rules:
    - if: '$CI_MERGE_REQUEST_ID'
      when: manual
D.
plan:
  script:
    - terraform plan
  rules:
    - if: '$CI_MERGE_REQUEST_ID'
      when: always
    - when: never

apply:
  script:
    - terraform apply
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual
    - when: never

Solution

  1. Step 1: Configure plan job for merge requests only

    Using rules with '$CI_MERGE_REQUEST_ID' ensures plan runs only on MRs.
  2. Step 2: Configure apply job for manual approval on main branch

    Rules with branch check and 'when: manual' ensure manual apply on main only.
  3. Step 3: Confirm why other configurations fail

    Other configurations either use the deprecated 'only' keyword or reverse the conditions (plan on main and apply on merge requests).
  4. Final Answer:

    The configuration using rules with $CI_MERGE_REQUEST_ID for plan and $CI_COMMIT_BRANCH == "main" for manual apply -> Option D
  5. Quick Check:

    Use 'rules' with MR and branch checks for plan/apply [OK]
Hint: Use 'rules' with MR and branch checks for plan/apply [OK]
Common Mistakes:
  • Using deprecated 'only' keyword
  • Mixing up branch and merge request conditions
  • Forgetting 'when: manual' for apply