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.
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.
"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.
$ 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.
#!/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.
"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.