Previously I could change the terraform script, run the terraform plan command and see the changes that would be applied locally. I do this, before committing the changes into git and pushing to the central repository.
Now at work we have moved to Terraform Cloud and it seems the workflow I used is no longer possible. Now with Terraform Cloud, I need to first commit and push the changes first before being apply to see the results of a command like terraform plan
I believe this reduces the feedback flow and diminishes the developer experience. It is like not being able to run unit test locally and one needs to push to a central repo first before being able to run unit tests.
Is there a way to reclaim this workflow when using Terraform cloud? That is, make changes, be able to run terraform plan locally (no need for terraform apply) and if satisfied push changes.
You can use the Terraform Cloud remote backend to run plans against your Terraform Cloud state with your local configuration. To do this, you might add a “backend.tf” file with the backend configuration, like:
Terraform Cloud will ignore that backend configuration, but once you log in with terraform login you’ll be able to run terraform plan locally to preview your work.
You won’t be able to run terraform apply, as Terraform prevents it while your workspace is connected to VCS:
$ terraform apply
Error: Apply not allowed for workspaces with a VCS connection
A workspace that is connected to a VCS requires the VCS-driven workflow to
ensure that the VCS remains the single source of truth.
I tested this before posting my reply, and it worked for me. What was the error when you tried?
You’re right that the code needs to be pushed to Terraform Cloud as a config version, because TFC needs the code to run your plan. But it does not need to be pushed to VCS, unless you have some workspace permissions settings that I’m not aware of.
Just to paint a full picture of your options, check out the documentation on Speculative Plans, which outlines all the options Terraform Cloud provides for running a plan w/o applying or committing to the main branch of your VCS repository. Specifically, two of those options seem very applicable here:
Add a block for the remote backend and run terraform plan on your CLI (what @alisdair said) This will not require you to push your configuration to VCS but will run a remote plan in Terraform Cloud to see potential changes. Note also that you can literally leave the backend configuration in the configuration pushed to VCS - it’s ignored when the plan/apply is executed within TFC. Having viewed the plan, you could then commit to your VCS repository to run an apply.
Pull requests against your main branch in VCS will also run a plan. This still is a commit to VCS, which you mentioned you wanted to avoid - but it’s no different than any other status check/CI job using a pull request workflow. Iterate and test within the pull request, and then having viewed the plan, you could then merge the PR to your main branch to run an apply.
I found that override.tf was included in the default .gitignore for Terraform on GitHub so it made sense to me to use it.
I tend to have multiple workspaces using the same VCS repo but with different variables defined. This setup allows me to easily switch between them using the terraform workspace CLI options.
I didn’t see anyone else mention it but you need to run your terraform init after setting up the backend and I’ve found it sometimes better to do so after I’ve cleaned out the .terraform/ directory.
When using the backend in this way, as @chrisarcand & @alisdair pointed out you can not run a terraform apply but you can still run the terraform plan. The variables used will be what is configured on the workspace in Terraform Cloud but it will upload your working files obeying the .gitignore or .terraformignore files. If you’re adding a new .tf file that hasn’t been pushed up to the VCS repo it will still be included when running terraform plan from your CLI if you have the backend configured.
Using this workflow is how I work on developing changes to my modules. I will perform the terraform plan locally to check that the output is what I would expect but then once it is committed and pushed back to the VCS repo it triggers the plan and apply on Terraform cloud.
Since you mentioned override files here, I want to note something that is typically thought of as an implementation detail but still worth knowing if you’re devising new patterns beyond what’s documented for Terraform Cloud:
@alisdair and @chrisarcand both above mentioned that the backend configuration is “ignored” when you run in Terraform Cloud. What’s actually going on there is that Terraform Cloud generates its own _override.tf file containing a backend "remote" block prior to running terraform init.
The reason I mention this is that if you are using override files to populate this too then there’s a risk that sending your override file up to Terraform Cloud would cause it to override Terraform Cloud’s own overrides, and thus defeat Terraform Cloud’s attempt to “ignore” the locally-provided configuration. I believe in the case of override.tf it happens that the priority order for processing files will still let Terraform Cloud’s take priority, but it would be safer to use a non-override file (not named override.tf or have an _override.tf suffix) for your local backend configuration to be sure that Terraform Cloud’s override will always be able to override it and thus get the expected behavior.
So when executing terraform commands my local file should be ignored in what is being transferred up to Terraform Cloud’s working directory given that the .gitignore and .terraformignore files are documented as being honored. Would it not? I only use the file for the local environment and it is never committed to the repository. I could however see this be an issue if the override.tf were not included in one of the 2 ignore files.
I want to say when I ran terraform init && terraform apply in the root of my project which had many files - it did not work. When I move my terraform files to any empty folder in and ran the same command - it worked. It seems Terraform will try to upload all the files in the directory that it executes in.