teriac CLI Using teriac in CI

Using teriac in CI

Install the version each project pins in GitHub Actions, GitLab CI and any other pipeline, cache the store between runs, and test modules against several versions.

In CI, teriac does the same job as on your laptop: it reads the version your project pins and installs exactly that, checksum-verified. Your pipeline then always matches what developers run.

The pattern

Every pipeline follows the same three steps:

npm install -g @marcdomain/teriac
export PATH="$HOME/.teriac/bin:$PATH"   # so `terraform` resolves in this job
teriac auto-switch                      # install + use the pinned version

On a laptop, restarting the terminal is enough. A CI job is different: it runs in a fresh shell that never reloads ~/.bashrc, and teriac can't change the PATH of the shell that started it. So add ~/.teriac/bin to PATH in the job yourself. Or skip PATH entirely and use the teriac commands (teriac plan, teriac exec), which run the managed binary directly.

GitHub Actions

jobs:
  plan:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infra
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Cache teriac versions
        uses: actions/cache@v4
        with:
          path: ~/.teriac/store
          key: teriac-${{ runner.os }}-${{ hashFiles('**/.terraform-version', '**/.tool-versions') }}

      - name: Set up Terraform with teriac
        run: |
          npm install -g @marcdomain/teriac
          echo "$HOME/.teriac/bin" >> "$GITHUB_PATH"
          teriac auto-switch
        env:
          GITHUB_TOKEN: ${{ github.token }}   # raises GitHub's rate limit for OpenTofu/Terragrunt lists

      - run: terraform init -input=false
      - run: terraform plan -input=false

Writing to $GITHUB_PATH makes terraform available in every later step.

OpenTofu and Terragrunt

      - run: |
          teriac auto-switch -p tofu
          teriac auto-switch -p tg

GitLab CI

plan:
  image: node:22
  variables:
    TERIAC_HOME: "$CI_PROJECT_DIR/.teriac"     # inside the project, so it can be cached
  cache:
    key: teriac
    paths: [.teriac/store]
  before_script:
    - npm install -g @marcdomain/teriac
    - export PATH="$TERIAC_HOME/bin:$PATH"
    - cd infra && teriac auto-switch
  script:
    - terraform init -input=false
    - terraform plan -input=false

Test a module against several versions

exec runs one command with a given version without switching, which suits a matrix:

jobs:
  validate:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        terraform: ["1.5.7", "1.7.5", "1.9.0"]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm install -g @marcdomain/teriac
      - run: teriac install ${{ matrix.terraform }}
      - run: teriac exec ${{ matrix.terraform }} -- init -backend=false -input=false
      - run: teriac exec ${{ matrix.terraform }} -- validate

Behind a firewall

Point teriac at your internal mirror with TERIAC_HASHICORP_MIRROR and TERIAC_GITHUB_MIRROR; see mirrors.

Tips

  • Cache ~/.teriac/store (or $TERIAC_HOME/store), keyed on your version files. A cache hit skips the download; auto-switch just copies the binary.
  • Turn on the plugin cache with teriac plugin-cache on and cache ~/.terraform.d/plugin-cache to skip provider downloads too.
  • Fail fast with teriac doctor if a runner image ships its own terraform that comes first on PATH.
  • Want plans in pull requests? That's what teriac server does, with permissions, approvals and locks that a CI job can't give you.