antonbabenko/pre-commit-terraform

Validate failing to upgrade lock file locally on version change

DavOpz opened this issue · 3 comments

Describe the bug

Version (Local) - v1.91.0

Docker Image (GitHub Workflow) - ghcr.io/antonbabenko/pre-commit-terraform:v1.91.0

Running the following pre-commit hook steps

  • terraform_validate
  • terraform_providers_lock

Locally - The "terraform_validate" hook shows no issues and passes with Success, however it will then fail on "terraform_providers_lock" because - correctly - my .terraform.lock.hcl refers to an old version of registry.terraform.io/hashicorp/azurerm.

GitHub Workflow - When I run this same process via Github workflow, "terraform_validate" correctly flags the need to make changes and fails on the file diff.

Expected behaviour is parity between local and github workflow as they are using the same version, main expected behaviour is that locally the results seen in github would occur.

My main guess on what is happening is because my .terraform dir is stored locally that the validate is working because the providers are present, however when running in github the files are not as they are in the .gitignore, so it will then run terraform init and this would have fixed the terraform.lock.hcl file as well if it were able to write.

Request an option to force terraform init -upgrade on hook terraform_validate, this will make it possible to replicate the CICD environment by ensuring the init is performed the same as if there were no .terraform files locally.

How can we reproduce it?

See section Files for a copy of the files used, then perform the following steps

  1. Run terraform init to generate lockfile
  2. Run pre-commit run -a --color=always and view the two hooks displaying Passed
  3. Update the versions.tf file from initial to the other file specified, changing the version of the hashicorp/azurerm provider from 3.103.1 to 3.108.0
  4. Run pre-commit run -a --color=always and the validate will show Passed however terraform lock will display Failed, there is no attempt to resolve and adding the --tf-init-args=-upgrade arg to validate does not ensure the lock file will be updated prior to the next step failing.
    • Local Output
    Terraform validate.......................................................Passed
    Lock terraform provider versions.........................................Failed
    - hook id: terraform_providers_lock
    - exit code: 1
    
    - Fetching hashicorp/null 3.2.2 for darwin_arm64...
    - Retrieved hashicorp/null 3.2.2 for darwin_arm64 (signed by HashiCorp)
    ╷
    │ Error: Could not retrieve providers for locking
    │ 
    │ Terraform failed to fetch the requested providers for darwin_arm64 in order to calculate their checksums: some providers could not be installed:
    │ - registry.terraform.io/hashicorp/azurerm: locked provider registry.terraform.io/hashicorp/azurerm 3.103.1 does not match configured version constraint 3.108.0; must use terraform init -upgrade to allow selection of new versions.
  5. Comment out the terraform_providers_lock hook in .pre-commit-config.yaml or you will be unable to commit to test the disparity in GitHub.
  6. Push to a repository in GitHub to trigger the CICD, here the validate step will attempt to fix the file, as would be expected locally prior to getting to a PR run.
    • CICD Output
    Run pre-commit run -a --color=always
    pre-commit run -a --color=always
    shell: bash --noprofile --norc -e -o pipefail {0}
    [INFO] Initializing environment for https://github.com/antonbabenko/pre-commit-terraform.
    Terraform validate.......................................................Failed
    - hook id: terraform_validate
    - files were modified by this hook
    
    Command 'terraform init' successfully done: .
    
    Error: Process completed with exit code 1.

You can see that when ran locally terraform_validate reports success because of the existence of the .terraform, however running via CICD fails because when running the terraform init it would be required to update the .terraform.lock.hcl file, which it isn't doing on the local pre-commit hook.

Environment information

  • OS: MacOS Sonoma 14.3 M3 Pro

  • uname -a and/or systeminfo | Select-String "^OS" output:

Darwin MacPro-F6WMC6WQG9 23.3.0 Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:59 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6030 arm64
  • Tools availability and versions:
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin23)
pre-commit 3.7.1
bash: line 3: tofu: command not found
Terraform v1.8.5
python SKIPPED
Python 3.12.3
checkov SKIPPED
Infracost v0.10.37
terraform-docs version v0.18.0 228c7a7 darwin/arm64
terragrunt version 0.59.3
terrascan version: v1.19.1
TFLint version 0.51.1
+ ruleset.terraform (0.7.0-bundled)
tfsec SKIPPED
trivy Version: 0.52.1
Check Bundle:
  Digest: sha256:cfb65621a1f55d9d099c4c28931b252716fcda8bba5081eb43f1001668e79d85
  DownloadedAt: 2024-06-14 08:38:10.74048 +0000 UTC
tfupdate 0.8.2
hcledit 0.2.11

Files

.pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
  rev: v1.91.0
  hooks:
  - id: terraform_validate
    args:
      - --hook-config=--retry-once-with-cleanup=true
      - --tf-init-args=-upgrade
  - id: terraform_providers_lock
    args:
      - --hook-config=--mode=always-regenerate-lockfile
.github/workflows/pre-commit.yml
name: pre-commit-terraform

on:
  pull_request:

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/antonbabenko/pre-commit-terraform:v1.91.0
    defaults:
      run:
        shell: bash
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.sha }}

      - run: |
          git config --global --add safe.directory $GITHUB_WORKSPACE
          git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*

      - name: fix tar dependency in alpine container image
        run: |
          apk --no-cache add tar
          # check python modules installed versions
          python -m pip freeze --local

      - name: Cache pre-commit since we use pre-commit from container
        uses: actions/cache@v4
        with:
          path: ~/.cache/pre-commit
          key: pre-commit-3|${{ hashFiles('.pre-commit-config.yaml') }}
      
      - name: Execute pre-commit on all files
        run: |
          pre-commit run -a --color=always
.gitignore
# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version 
# control as they are data points which are potentially sensitive and subject 
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc
main.tf
resource "null_resource" "foo" {
  triggers = {
    example = var.project_name
  }
}
variables.tf
variable "project_name" {
  default     = "default value"
  type        = string
  description = "Example variable."
}
versions.tf (Initial File)
terraform {
  required_version = "1.8.5"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.103.1"
    }
    null = {
      source  = "hashicorp/null"
      version = "3.2.2"
    }
  }
}
versions.tf (Change to file after running commands specified)
terraform {
  required_version = "1.8.5"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.108.0"
    }
    null = {
      source  = "hashicorp/null"
      version = "3.2.2"
    }
  }
}

To add to this, I've just tried running terraform init -upgrade locally to resolve this (and added back the other tests) and it all worked fine locally

Terraform fmt............................................................Passed
Terraform validate.......................................................Passed
Lock terraform provider versions.........................................Passed
Terraform validate with tflint...........................................Passed
Terraform validate with trivy............................................Passed
terrascan................................................................Passed
tfupdate.................................................................Passed
Terraform docs...........................................................Passed

However pushing to CICD shows the following

[INFO] Initializing environment for https://github.com/antonbabenko/pre-commit-terraform.
Terraform fmt............................................................Passed
Terraform validate.......................................................Failed
- hook id: terraform_validate
- files were modified by this hook
Command 'terraform init' successfully done: .
Lock terraform provider versions.........................................Passed
Terraform validate with tflint...........................................Passed
Terraform validate with trivy............................................Passed
terrascan................................................................Passed
tfupdate.................................................................Passed
Terraform docs...........................................................Passed
pre-commit hook(s) made changes.
If you are seeing this message in CI, reproduce locally with: `pre-commit run --all-files`.
To run `pre-commit` as part of git workflow, use `pre-commit install`.
All changes made by hooks:
diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl
index 1d61275..38a779c 100644
--- a/.terraform.lock.hcl
+++ b/.terraform.lock.hcl
@@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/azurerm" {
   constraints = "3.108.0"
   hashes = [
     "h1:36WHCMjguUKG15iS3WNqmk2/FQH2AwFL0mJl0VWCfps=",
+    "h1:RIFBFTXz4X48JDHjbQHX4y400ax1/uEzMVFZgX3/z3w=",
     "zh:2afecf948fd702bc08c87d9114595809d011f99a70a12dbf6bc67a12d0bee5fc",
     "zh:395b6d1384a579867064e62d49b0b91e15919c33b03ea8b5031c2779bfa16b3d",
     "zh:3e5594c59b6b02bc6e0f4c3de71aa2ab992494c53725fb3c64d36745f3814ef3",
@@ -26,6 +27,7 @@ provider "registry.terraform.io/hashicorp/null" {
   constraints = "3.2.2"
   hashes = [
     "h1:IMVAUHKoydFrlPrl9OzasDnw/8ntZFerCC9iXw1rXQY=",
+    "h1:zT1ZbegaAYHwQa+QwIFugArWikRJI9dqohj8xb0GY88=",
     "zh:3248aae6a2198f3ec8394218d05bd5e42be59f43a3a7c0b71c66ec0df08b69e7",
     "zh:32b1aaa1c3013d33c245493f4a65465eab9436b454d250102729321a44c8ab9a",
     "zh:38eff7e470acb48f66380a73a5c7cdd76cc9b9c9ba9a7249c7991488abe22fe3",
Error: Process completed with exit code 1.

I'm now thinking this is due to me creating the lockfile locally on a macbook and then running the tests in the linux container. Will try to confirm and close if so.

Yeah, apologies, worked it out, will close issue

Adding the following to .pre-commit-config.yaml fixed the issue

  - id: terraform_providers_lock
    args:
      - --hook-config=--mode=always-regenerate-lockfile
      - --args=-platform=linux_amd64
      - --args=-platform=darwin_arm64

I'm now thinking this is due to me creating the lockfile locally on a macbook and then running the tests in the linux container.

Yep, it is.