Run Terraform within GitHub Codespaces#

Using GitHub Codespaces allows you to work on your code from almost any place in the world without an Internet connection. Only the devcontainers powering Codespaces are mended to be short-lived and not contain any credentials. This may pose a challenge when you’re depending on remote services like Terraform Cloud that require an API-token to work properly.

Modifying a devcontainer#

Most devcontainers are following the Microsoft devcontainer template and those are based on Debian which gives you access to a huge repository of packaged software. Only Terraform isn’t part of the standard Debian repository, but HashiCorp provides its own repository that can be added. Let’s start by extending the Dockerfile to add the repository and install the Terraform package as highlighted below.

Install package via .devcontainer/Dockerfile#
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install --no-install-recommends rcm \
    && curl https://apt.releases.hashicorp.com/gpg | gpg --dearmor > /usr/share/keyrings/hashicorp-archive-keyring.gpg \
    && echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" > /etc/apt/sources.list.d/hashicorp.list \
    && apt update \
    && apt -y install --no-install-recommends terraform

Note

VSCode is previewing the option called features to customize devcontainers that will run additional scripts to do the installation of Terraform, AWS-CLI, or GitHub-cli by extending .devcontainer/devcontainer.json as shown in the example below.

Enable feature Terraform in .devcontainer/devcontainer.json#
    "features": {
      "ghcr.io/devcontainers/features/github-cli:1": {},
      "ghcr.io/devcontainers/features/terraform:1": {}
    }
}

Custom features can also be written by referring to a repository, but most used demands already have been turned into features for VSCode.

Access Terraform Cloud#

After rebuilding the devcontainer the command terraform init can be executed, but it will require to be logged in into Terraform Cloud. The command terraform login allows the creation of a new token to authenticate, but this has to be done every time the container is created and leaves credentials behind. An alternative is to generate an API-token for Terraform Cloud and put it as a secret with TF_TOKEN_APP_TERRAFORM_IO as its name for Codespaces on your account.

Verify if the environment variable has been set correctly#
$ echo $TF_TOKEN_APP_TERRAFORM_IO
<randomkeystring>

Installing Terraform Providers#

The command terraform init will not ask for credentials anymore and will install all the defined dependencies. Running this command every time a devcontainer is created manually is something that can break the workflow. To solve this the option postCreateCommand exists for VSCode. First, we create a shell script to run all commands like installing the required Python packages for the project, and in the second step running terraform init with the option -chdir to use the configuration in the terraform directory.

The contents of .devcontainer/postCreateCommand.sh for a Python devcontainer#
#!/usr/bin/sh

# Install all Python dependencies
pipenv install --dev

# Initialize Terraform
#
# Environment variable TF_TOKEN_APP_TERRAFORM_IO has to be set
terraform -chdir=terraform init

The final step for automating this whole workflow is to specify that VSCode needs to run the shell script when the container has been created as highlighted in line 46. The option postCreateCommand only supports one command or all commands must be seperated by &&, but this reduces the readability of the configuration a lot. Using the sh command is a good way to make sure that the commands is always executed even if the execute bits are missing.

The content of .devcontainer/devcontainer.json for a Python devcontainer#
    "forwardPorts": [],
    "postCreateCommand": "sh ./.devcontainer/postCreateCommand.sh",
    "remoteUser": "vscode"
}

While this setup may not be optimal, it does enable the user to run terraform plan and verify the changes that will be made deploying this version of the Terraform plan. Also, the latest version of Terraform will be installed within the devcontainer, for the plan to run it still relies on the version set within Terraform Cloud for the project reducing the risk of unwanted behavior.