GitHub Actions workflow w/multiple environments

I’m trying to figure out a reasonable branching strategy for GitHub Actions using Terraform with multiple environments. In my previous company, we used Atlantis and had a more traditional setup where you have a directory per environment (i.e. ./dev and ./prod). I’ve been using Terraform Workspaces in my new repository and really like that approach better as it leads to a lot less duplication and ensures consistency between environments.

My current GH action setup is basically the same as Automate Terraform with GitHub Actions | Terraform - HashiCorp Learn where I’m setting TF_WORKSPACE to “dev” where PRs run plan and merges to main run apply. I’m now trying to think through how I want this to work with production. A couple things I want:

  1. I want to be able to push dev changes first prior to pushing prod so I don’t want prod changes running at the same time
  2. I want to keep the PR workflow so that you can run plan on a PR and potentially require a code review to merge to prod

My current solution is to create a “tf_prod” branch and have pull requests open from “main” to “tf_prod”, but I was curious how others are solving this. For posterity, I’m sharing my whole GH action in case it helps anyone else out in the future. Thanks for your thoughts!

@ewhauser
I am just reading your article here and it seems you have solved in the past the problems I am going through right now. I am using GitHub Actions to deploy my code using Terraform. Whenever code is pushed to the Testing branch, a GitHub Action is triggered that builds the code and runs terraform apply . This works well. The problem is that now I want to have a Prod environment too. Whenever code is pushed to the Prod branch, it should be built using its own s3 remote backend and its AWS account. The problem I’m having is that I am not sure how to configure my terraform files so that terraform GithubAction can use the backend for Prod to store the state file.please if you are able to guide with that.

I used workspaces + Terraform Cloud for this. I believe the remote workspace automatically determines the backend off of the TF_WORKSPACE environment variable (you’ll have to check the docs), but here’s the GitHub action and backend config that I was using:

       - name: Set Workspace
         id: set_workspace
         run: |
           prod="${{ github.event.pull_request.base.ref == 'prod' || github.ref == 'refs/head/prod' }}"
           if [ "$prod" = true ]; then
             echo "::set-output name=environment::prod"
           else
             echo "::set-output name=environment::dev"
           fi
       - name: Terraform Plan
         id: plan
         if: github.event_name == 'pull_request'
         run: terraform plan -no-color
         continue-on-error: true
         env:
           TF_WORKSPACE: ${{ steps.set_workspace.outputs.environment }}

and backend config:

terraform {
   backend "remote" {
     organization = "myorg"

     workspaces {
       prefix = "myorg-"
     }
   }
}

Thank you for your reponse.I didnt use Terraform Cloud…I have 2 environments Dev-Env and Prod-Env which I am trying to set up using Github Actions CI/CD.I want to have remote statefile for each environment to be stored in different aws account.I specified which backend to be used for this environment " run terraform init -backend-config=“backend.Dev-Env.tf”.I am using Terraform backend partial configuration.I am getting error saying terraform configuration must be valid before initialization.Also i can see from the error output that terraform is using all the backend files in my terraform configuration.I only want it to use the backend.Dev-Env.tf only and not backend.Prod-Env.tf

this is how my configuration looks like in github

@ewhauser
I bumped into the topic while trying to solve similar challenges. I noticed that Terraform is recommending not to use remote backend while using terrafrom cloud as per url below.

https://www.terraform.io/language/settings/backends/remote

Have you happen to set the workspace name dynamically based on environment (e.g. dev, uat prod etc) using cloud provider and not remote backend?

Obviously a variable is not allowed in workspaces name while using a cloud provider, which I find it strange. How do we then deploy to multi environment keeping our code DRY?

  cloud {
    organization = "org_name"

    workspaces {
      name = "workspace_name"
    }
  }

Thank you so much.
Ash

I was able to use TF_WORKSPACE, omitting workspaces and organization block in cloud section , and that worked for me.