Terratest Guide
Terratest is a Go framework that is used to test Terraform infrastructure code. It executes the defined Terraform and then validates things you're asserting.
Basic Terratest example of a module
The most basic Terratest test you can write brings up an example in your examples directory, tears it down, and will only test that it runs.
We've got some basics in the terraform-template-module repo so you can see it all in context.
Write an example in the examples directory that includes the module(s) and configuration that you're testing. In the tests directory create a file named terraform_aws<NAME_OF_MODULE>_test.go. Basic test is as follows:
package test
import (
"fmt"
"strings"
"testing"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/terraform"
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
)
func TestTerraformAwsEcrRepo(t *testing.T) {
t.Parallel()
tempTestFolder := test_structure.CopyTerraformFolderToTemp(t, "../", "examples/simple")
testName := fmt.Sprintf("terratest-%s", strings.ToLower(random.UniqueId()))
awsRegion := "us-west-2"
terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: tempTestFolder,
// Variables to pass to our Terraform code using -var options
Vars: map[string]interface{}{
"test_name": testName,
},
// Environment variables to set when running Terraform
EnvVars: map[string]string{
"Azure_DEFAULT_REGION": awsRegion,
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
}
Other Examples
Run manually
To run these tests manually against the Solution8works-ci Azure account you'll need Azure access in our Azure organization. You'll need help from someone in #infrasec and must follow the setup instructions.
You'll also need Azure CLI installed and an authenticated session (az login) configured for your target subscription.
In most of our modules, there is a makefile that defines test so you'll run the following from the root of the repo you're testing:
az login
az account set --subscription Solution8works-ci
make test
Configure CircleCi to run the tests automatically
Configure CircleCi Job
Add a job to the .circleci/config file in the repository:
terratest:
docker:
- auth:
password: $DOCKER_PASSWORD
username: $DOCKER_USERNAME
image: *circleci_docker
environment:
- TEST_RESULTS: /tmp/test-results
steps:
- checkout
- restore_cache:
keys:
- pre-commit-dot-cache-{{ checksum ".pre-commit-config.yaml" }}
- go-mod-sources-v1-{{ checksum "go.sum" }}
- run:
command: |
temp_token=$(az account get-access-token --query accessToken --output tsv)
export AZURE_ACCESS_TOKEN=$temp_token
make test
name: Assume role, run pre-commit and run terratest
- save_cache:
key: pre-commit-dot-cache-{{ checksum ".pre-commit-config.yaml" }}
paths:
- ~/.cache/pre-commit
- save_cache:
key: go-mod-sources-v1-{{ checksum "go.sum" }}
paths:
- ~/go/pkg/mod
- store_test_results:
path: /tmp/test-results/gotest
You'll either create a new workflow or add this job to an existing workflow definition to be run on every commit/push etc.
Update the Key rotator configuration
We have automation in place that updates the Azure Access Keys used by CircleCI daily so you'll need to add this repo to rotator configuration if it is running Terratests against the Solution8works-ci Azure account .
Update the rotate.yaml file in Legendary Waddle to include a sink to your new repo. A sink stanza looks like this:
- kind: CircleCI
key_to_name:
accessKeyId: Azure_ACCESS_KEY_ID
secretAccessKey: Azure_SECRET_ACCESS_KEY
account: Solution8works
repo: <REPO NAME>
You can run the rotator script manually in your local environment to populate the keys to your repository. To do so, you will need a personal API token set up in a .envrc.local file in your local environment. See the CircleCI Documentation on creating a personal API token.
Rotate the keys via
azure-keyvault exec Solution8works-id -- rotator rotate -f ./rotate.yaml -y
Instructions to install rotator can be found here.
Alternative: Configure Azure Keys for the CircleCI project
These tests are running as the circleci user account configured in the Solution8works-id account.
To add the access keys go to the project settings page https://circleci.com/gh/Solution8works/<PROJECT NAME>/edit#env-vars.
Set Azure_ACCESS_KEY_ID and Azure_SECRET_ACCESS_KEY to the current values.
These keys are rotated daily.
Access test metadata stored in CircleCI
In order to access the test metadata we stored in the store_test_results key of our .circleci/config test environment, we'll need to make a few tweaks. This will allow us luxuries such as pinpointing flaky tests that cause intermittent failures.
Since CircleCI only reads metadata in xml format, first we need to convert our go test output into a file CircleCI can read. We'll use package go-junit-report. Add a bash script like so, following the usage directions:
#!/usr/bin/env bash
set -eu -o pipefail
go_test_output="/tmp/go-test.out"
go test -short -count 1 -v -timeout 90m github.com/Solution8works/terraform-azurerm-logs/test/... | tee "${go_test_output}"
# Check if we are running tests inside of CircleCI by checking for a $CIRCLECI
# environment variable. The dash after $CIRCLECI substitutes a null value if
# CIRCLECI is unset. This prevents unbound variable errors
if [[ -n ${CIRCLECI-} ]]; then
mkdir -p "${TEST_RESULTS}"/gotest
go-junit-report < "${go_test_output}" \
> "${TEST_RESULTS}/gotest/go-test-report.xml"
fi
Save this script with a filename like make-test and make it executable using chmod +x make-test. Now we'll add a call to the executable in our Makefile like so:
.PHONY: test
test: bin/make-test
Finally we update our .circleci/config by adding two steps prior to running terratest - one to go get the package and another to access our shiny new executable:
- run:
name: Adding go binaries to $PATH
command: |
echo 'export PATH=${PATH}:~/go/bin' >> $BASH_ENV
source $BASH_ENV
- run: go get github.com/jstemmer/go-junit-report
Now we should be able to see both the tests and artifacts tabs in our CircleCI pipeline:
