skip to Main Content

I was looking into the below GitHub Actions workflow, and was surprised to see that terraform apply terraform.tfplan was working, despite a full initialization wasn’t performed (terraform init -backend=false).

This made me dive deeper into the terraform init and terraform plan commands.

The -backend=false option is documented as follows:

Disable backend or Terraform Cloud initialization for this
configuration and use what was previously initialized instead.

What does this really mean? Doesn’t this assume that terraform init was already run on the configuration? Why would I run it again with -backend=false then?

Looking into the generated plan, I observed that it includes, among other things, the .terraform.lock.hcl lockfile, and the S3 backend configuration in the (binary) tfplan file:

➜  tfplan unzip terraform.tfplan
Archive:  terraform.tfplan
  inflating: tfplan                  
  inflating: tfstate                 
  inflating: tfstate-prev            
  inflating: tfconfig/m-/providers.tf  
  inflating: tfconfig/m-/main.tf     
  inflating: tfconfig/modules.json   
  inflating: .terraform.lock.hcl  

In the terraform plan documentation, it’s not stated that the generated plan includes backend configuration and the lockfile, so that terraform apply can be run on it without performing a full initialization (i.e. only downloading modules)? Should I avoid depending on this?

https://developer.hashicorp.com/terraform/cli/commands/plan

GitHub Actions workflow:

  ....

  terraform-plan:
    name: terraform-plan
    runs-on: ubuntu-latest
    timeout-minutes: 30
    defaults:
      run:
        working-directory: terraform
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: eu-west-1
          role-to-assume: arn:aws:iam::729187411107:role/github-oidc-role
          role-duration-seconds: 3600
          role-session-name: github-${{ github.sha }}
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.5.7
      - name: Terraform Init
        run: |
          terraform init 
          -backend-config="bucket=terraform-approve-before-apply-tfstate" 
          -backend-config="key=terraform.tfstate" 
          -backend-config="region=eu-west-1"
      - name: Terraform Plan
        run: terraform plan -out=terraform.tfplan
      - name: Upload Terraform Plan
        uses: actions/upload-artifact@v4
        with:
          name: tfplan
          path: |
            terraform/terraform.tfplan
            terraform/.terraform.lock.hcl
          if-no-files-found: error
    needs: terraform-validate

  terraform-apply:
    name: terraform-apply
    runs-on: ubuntu-latest
    timeout-minutes: 30
    environment: deploy
    defaults:
      run:
        working-directory: terraform
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: eu-west-1
          role-to-assume: arn:aws:iam::729187411107:role/github-oidc-role
          role-duration-seconds: 3600
          role-session-name: github-${{ github.sha }}
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.5.7
      - name: Terraform Init
        run: terraform init -backend=false
      - name: Download Terraform Plan
        uses: actions/download-artifact@v4
        with:
          name: tfplan
          path: terraform
      - name: Terraform Apply
        run: terraform apply terraform.tfplan
    needs: terraform-plan

2

Answers


  1. Terraform init does few things:

    • Download Provider Plugins
    • Backend Initialization
    • Child Module Initialization
    • Creation of .terraform directory: terraform init creates a .terraform directory in the root directory of your configuration if it doesn’t exist. This directory contains data that Terraform needs to maintain between runs.
    • Upgrade Previous State: If you’ve previously run terraform init with a different backend configuration, Terraform will offer to migrate your state to the new backend.
    • Lock File Creation

    And if you run this command -backend=false it skips backend initialization using one available.

    Of course if there is .terraform.lock.hcl it will reuse this instead of creating a new one.

    But still some other steps are necessary to run further commands.

    Login or Signup to reply.
  2. "Working directory initialization" as performed by the terraform init command largely consists of creating various files and directories under the .terraform directory that Terraform uses either to avoid repeating work that was already done by init, or to help ensure that the execution environment remains consistent between commands.

    A saved plan file does include a subset of the information that terraform init normally establishes. The primary purpose of duplicating that information is to help avoid a mistake of accidentally applying a saved plan in a different context than where it was created. For example, it aims to help avoid someone accidentally applying a plan to a different workspace than it was intended for, or with different provider versions than it was created with.

    The information in the saved plan file is not necessarily complete. It contains information necessary for Terraform to avoid applying the plan in a different context, and it may include some additional information beyond what’s strictly required just because implementation details lead to that being true, but a saved plan file is not designed to be a self-contained representation of everything needed to apply the plan.

    The details of what is strictly required to apply the saved plan have varied over time as Terraform’s implementation details have shifted, and so I would recommend against designing any workflow automation that makes assumptions about information stored in the plan file. Instead, you should think of the information in the plan file as part of a set of guardrails to help avoid mistakes.

    To directly answer your question, then: if you find that you are able to run terraform apply PLANFILE without fully completing the working directory initialization equivalent to what you performed before creating the plan then you are relying on an implementation detail that might not hold in other versions of Terraform. The intention is that the saved plan file format is private to Terraform and subject to change at any time, and that you will apply the plan in a working directory initialized equivalently to the one that created the plan file.


    Although it does not directly address the specific point you were asking about here, I suggest referring to Plan and Apply on Different Machines — a part of the tutorial Running Terraform in Automation — which describes the recommended strategy for applying a saved plan in a different working directory (typically, on a different computer) than where it was created, and some constraints and assumptions Terraform makes that you must honor for that process to be generally reliable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search