DEV Community

Michael Ethridge
Michael Ethridge

Posted on Originally published at 404-code-not-found.com

Workstation Setup for Terraform

Introduction

In my last post, I covered how I set up
VSCode for Terraform. The second most
popular question is how I set up my workstation for Terraform development.

As with most things in the tech world, there are many ways to do things. The
following is the way that works for me. I am always looking for ways to improve
my workflow, so if you have suggestions, please let me know.

Additionally, I am a Mac user, so some of the tools I use are harder to use on
Windows. However, the tools can be used in WSL or DevContainers if you use
either of those tools.

On a Mac, I use Homebrew to install my tools. Also, Since we
have to store all our code in a version control system, I installed the latest
version of git with Homebrew.

# Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install Git
brew install git
Enter fullscreen mode Exit fullscreen mode

Installing Terraform

I have to use multiple versions of Terraform for different projects, so I use
TFSwitch to switch the version of Terraform
installed based on the directory I am in and the version constraint in my
Terraform configuration.

brew install warrensbox/tap/tfswitch
Enter fullscreen mode Exit fullscreen mode

I have configured tfswitch to link the correct version of Terraform to
~/bin/terraform as the ~/bin directory is in my $PATH. This is done by
creating a ~/.tfswitch.toml file with the following contents:

bin="~/bin/terraform"
Enter fullscreen mode Exit fullscreen mode

I use the following zsh function to load tfswitch in a directory with
Terraform configuration files.

load-tfswitch() {
  local tfswitchrc_path="${HOME}/.tfswitch.toml"

  # if [[ -f "$tfswitchrc_path" ]] && [[ -f "terraform.tf" ]]; then
  if [[ -f "$tfswitchrc_path" ]]; then
    if [[ $(ls -l *.tf | wc -l ) -gt 0 ]] 2> /dev/null; then
      tfswitch
    fi
  fi
}

add-zsh-hook chpwd load-tfswitch
load-tfswitch
Enter fullscreen mode Exit fullscreen mode

Now, if you don't need to support multiple versions of Terraform, you can
install it with Homebrew from HashiCorp's tap.

  brew install hashicorp/tap/terraform
Enter fullscreen mode Exit fullscreen mode

Otherwise, you can download it directly from the
Terraform Install page.

Git Tools

Storing your Terraform configuration in a version control system is a must. I
don't do development daily, so I forget to format my code and check for secrets
or other vulnerabilities. One of the worst is that I forgot to update the
README.md with any variable or output changes. The most annoying thing is that I
forgot to create a new branch for my changes, as most of the git repos I work in
have the default branch protected, so I can't push directly to it.

To help me with all of these problems, I use
Git Hooks,
specifically the pre-commit hook to check for all of these things before I
commit my changes. If you are unfamiliar with Git Hooks, they are scripts that
run automatically when specific actions occur in a Git repository. You can use
these scripts to enforce coding standards, check for vulnerabilities, or even
run tests before you commit your changes.

Traditionally, you would have to write these scripts yourself, but there is a
tool called pre-commit that makes it easy to install
and manage these hooks.

brew install pre-commit
Enter fullscreen mode Exit fullscreen mode

Once you have pre-commit installed, you can create a .pre-commit-config.yaml
file in the root of your repository with the following contents:

# yaml-language-server: $schema=https://json.schemastore.org/pre-commit-config.json

repos:
  - repo: local
    hooks:
      - id: trufflehog
        name: TruffleHog
        description: Detect secrets in your data.
        entry: trufflehog git file://. --since-commit HEAD --fail
        language: golang
        pass_filenames: false
        stages: ["pre-commit", "pre-push"]
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: check-merge-conflict
      - id: end-of-file-fixer
      - id: no-commit-to-branch
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.97.0
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
        args:
          - --hook-config=--retry-once-with-cleanup=true
      - id: terraform_docs
        args:
          - --args=--lockfile=false
      - id: terraform_tflint
        args:
          - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl
      - id: terraform_checkov
      - id: infracost_breakdown
        args:
          - --args=--path=.
          - --args=--terraform-var-file="terraform.tfvars"
        # verbose: true
Enter fullscreen mode Exit fullscreen mode

Every time you commit your changes, the pre-commit tool runs and checks each
hook you have defined. If any hook fails, the commit will abort, and you must
fix the issues before committing your changes.

We haven't covered all of the hooks in the .pre-commit-config.yaml file, but
I'll list the ones I use here:

  • trufflehog: Scans your git repo for committed secrets 😱.
  • check-merge-conflict: Checks for files that contain merge conflict strings.
  • end-of-file-fixer: Ensures that files end with a newline.
  • no-commit-to-branch: Prevents commits directly to a branch (default branch in our case).
  • terraform_fmt: Formats your Terraform code.
  • terraform_validate: Validates your Terraform code.
  • terraform_docs: Dynamically updates your README.md with information on your module's inputs, outputs, and requirements.
  • terraform_tflint: A Terraform linter that checks for best practices and errors in your Terraform code.
  • terraform_checkov: A tool that checks your Terraform code for security vulnerabilities.
  • infracost_breakdown: Gives you a cost estimate for the cloud resources your module would deploy.

Tools needed for the Pre-commit hooks that I use

You will need to install a few of these tools to use all of the pre-commit
hooks that I have listed above.

Trufflehog scans your git repo
for secrets 😱. Doing this as a pre-commit hook lets you catch secrets before
committing them. This way, they don't end up in your git history, keeping your
security team happy.

brew install trufflesecurity/trufflehog/trufflehog
Enter fullscreen mode Exit fullscreen mode

Terraform-docs dynamically
updates your README.md with information on your module's inputs, outputs, and
requirements.

brew install terraform-docs
Enter fullscreen mode Exit fullscreen mode

In your README.md file, you can add the following comments to have
terraform-docs update the file.

<!-- BEGIN_TF_DOCS -->
<!-- END_TF_DOCS -->
Enter fullscreen mode Exit fullscreen mode

Infracost provides a cost estimate for
the cloud resources on which your configuration will be deployed.

brew install infracost
Enter fullscreen mode Exit fullscreen mode

Jq is a lightweight and flexible command-line
JSON processor. required for terraform_validate with
--retry-once-with-cleanup flag, and for infracost_breakdown hook.

brew install jq
Enter fullscreen mode Exit fullscreen mode

TFLint is a Terraform linter that
checks for best practices and errors in your Terraform code.

brew install tflint
Enter fullscreen mode Exit fullscreen mode

Checkov is a static code analysis
tool for infrastructure as code (IaC).

brew install checkov
Enter fullscreen mode Exit fullscreen mode

Convenience Tools

With Terraform installed via tfswitch and the pre-commit hooks setup, I have
a solid foundation for my Terraform development workflow. But I use a few more
tools to make my life easier.

First, I use 1Password to store my secrets, API keys,
and passwords. I also use the
1Password CLI to access my
secrets via environment variables and shell scripts.

brew install 1password 1password-cli
Enter fullscreen mode Exit fullscreen mode

With 1Password and the 1Password CLI installed, I can access my secrets via the
op command.

export TFE_TOKEN=$(op read --cache "op://Vault/Item/Key")
Enter fullscreen mode Exit fullscreen mode

This allows me to easily set environment variables for my secrets in my shell.
However, I need to remember to unset these variables when I'm done with them. To
help with this, I use Direnv to set and unset variables
based on my directory.

brew install direnv
Enter fullscreen mode Exit fullscreen mode

I use the following .envrc file in the root of my Terraform configuration to
set my secrets.

# Exports
export TFE_TOKEN=$(op read --cache "op://Vault/Item/Key")

# Terraform Variable exports
export TF_VAR_okta_token=$(op read --cache "op://Vault/Item/Section/Key")
export TF_VAR_okta_client_id=$(op read --cache "op://Vault/Item/Section/Key")
export TF_VAR_okta_client_secret=$(op read --cache "op://Vault/Item/Section/Key")
Enter fullscreen mode Exit fullscreen mode

The above example exports the TFE_TOKEN and a few Terraform variables. When I
change into or out of the directory with the .envrc file, direnv will set
and unset these variables for me.

With so many tools in use, remembering all the commands to run can be a pain.
Yes, I could create aliases for them, but I like to keep my shell clean. So, I
use Task to create a Taskfile.yml with all the
commands I use.

# yaml-language-server: $schema=https://taskfile.dev/schema.json
# https://taskfile.dev

version: "3"

dotenv: [".envrc"]

vars:
  CURRENT_DATE:
    sh: date +"%Y-%m-%dT%H:%M:%S%Z"

tasks:
  default:
    cmds:
      - task: pre

  hog:
    cmds:
      - trufflehog git file://. --since-commit HEAD --only-verified --fail

  pre:
    cmds:
      - pre-commit autoupdate
      - pre-commit run -a

  push:
    cmds:
      - git add .
      - git commit -m "{{.CURRENT_DATE}}"
      - git push
    silent: true

  tag:
    cmds:
      - git push
      - git tag -s {{.CLI_ARGS}} -m "{{.CLI_ARGS}}"
      - git push --tags
Enter fullscreen mode Exit fullscreen mode

Now I can run task hog to scan my git repo for secrets, task pre to run all
of my pre-commit hooks, task push to commit my changes and push them to the
remote, and task tag -- v1.0.0 to tag my release.

Conclusion

This is how I set up my workstation for Terraform development. I hope you found
it helpful. If you have any suggestions or tools that you use, please let me
know. I am always looking for ways to improve my workflow.

Top comments (0)