Quick start : Terratest with Terraform

Reading Time: 4 minutes

Hi Readers, In this blog we will see how we can test our Terraform code that it applies all the infrastructure as per stated in the code.

Terratest is a Go library developed at Gruntwork, that makes it easier to write automated tests for our infrastructure code.

It provides a variety of helper functions and patterns for common infrastructure testing tasks but here we will be discussing about Testing Terraform code. For other helper functions and patterns, see this.

However for understanding how terraform works you can see previous blogs.

Requirements

Terratest uses Go testing framework. To use terratest we would need Go installed in our system. To install, you can follow below script.

#!/bin/bash
#Download the latest version
sudo wget https://golang.org/dl/go1.15.5.linux-amd64.tar.gz
#ext, extract the tarball to /usr/local directory.
sudo tar -C /usr/local -xzf go1.15.5.linux-amd64.tar.gz
#Add the go binary path to .bashrc file /etc/profile (for a system-wide installation).
export PATH=$PATH:/usr/local/go/bin
#After adding the PATH environment variable, you need to apply changes immediately by running the following command.
source ~/.bashrc
#Now verify the installation by simply running the go version in the terminal.
go version

Project Structure

  • Create two folders named examples and test.
    • examples will contain your infrastructure code.
    • test would contain test code written in Go.

Content within examples folder

instance.tf
resource "aws_instance" "example" {
  ami           = var.AMIS[var.AWS_REGION]
  instance_type = "t2.micro"
  vpc_security_group_ids = [aws_security_group.instance.id]

  # When the instance boots, start a web server on port 8080 that responds with "Hello, World!".
  user_data = <<EOF
#!/bin/bash
echo "Hello, World!" > index.html
nohup busybox httpd -f -p 8080 &
EOF
}
provider.tf
provider "aws" {
  access_key = var.AWS_ACCESS_KEY
  secret_key = var.AWS_SECRET_KEY
  region     = var.AWS_REGION
}
securitygroup.tf
# Allow the instance to receive requests on port 8080.
resource "aws_security_group" "instance" {
  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
var.tf
variable "AWS_ACCESS_KEY" {
}

variable "AWS_SECRET_KEY" {
}

variable "AWS_REGION" {
  default = "ap-south-1"
}

variable "AMIS" {
  type = map(string)
  default = {
    ap-south-1 = "ami-0860c9429baba6ad2"
  }
}
output.tf
# Output the instance's public IP address.
output "public_ip" {
  value = aws_instance.example.public_ip
}

terraform.tfvars
AWS_ACCESS_KEY = "XXXX"
AWS_SECRET_KEY = "XXXXXXXX"
AWS_REGION="ap-south-1"

Contents within test folder

terraform_test.go
package test

import (
	"fmt"
	"testing"
	"time"

	http_helper "github.com/gruntwork-io/terratest/modules/http-helper"

	"github.com/gruntwork-io/terratest/modules/terraform"
)

func TestTerraformAwsHelloWorldExample(t *testing.T) {
	t.Parallel()

	/*Construct the terraform options with default retryable errors to handle the most common retryable errors in terraform testing. */
	terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
		/* The path to where our Terraform code is located */
		TerraformDir: "../examples",
	})

	/* At the end of the test, run `terraform destroy` to clean up any resources that were created.*/
	defer terraform.Destroy(t, terraformOptions)

	/* Run `terraform init` and `terraform apply`. Fail the test if there are any errors. */
	terraform.InitAndApply(t, terraformOptions)

	/* Run `terraform output` to get the IP of the instance */
	publicIp := terraform.Output(t, terraformOptions, "public_ip")

	/* Make an HTTP request to the instance and make sure we get back a 200 OK with the body "Hello, World!" */
	url := fmt.Sprintf("http://%s:8080", publicIp)
	http_helper.HttpGetWithRetry(t, url, nil, 200, "Hello, World!", 30, 5*time.Second)
}

Configure Dependencies

 cd test
 go mod init "<MODULE_NAME>"
 # MODULE_NAME would be github.com/<YOUR_USERNAME>/<YOUR_REPO_NAME> 

Run the tests

 cd test
 go test

Once you hit go test, there would be series of steps performed on the terminal.

I. Back end and provider plugin initialisation using terraform init.

II. After that, estimation of plan for adding resources using terraform plan.

III. Then applying the configuration by adding resources as per plan using terraform apply.

IV. After that, estimation of plan for destroying resources.

V. After that, destroying the configuration by removing the resources as per plan using terraform destroy.

On observing those steps you would understand that these steps are the same as of running terraform init, terraform apply and terraform destroy.

In conclusion, the only difference here is that all steps are executed automatically and they ensure that our infrastructure code is written correctly and doing things as expected.

For all the source codes mentioned above you can see this GitHub repository .

That’s all for this blog. However If you have any doubt, feel free to contact me at nitin.mishra@knoldus.com.

References

Thank you for sticking to the end. If you like this blog, please do show your appreciation by giving thumbs ups and share this blog and if you feel, give me suggestions on scope of improvements.

Written by 

Nitin Mishra is a Software Consultant at Knoldus Software LLP. He has done MCA from GGSIPU and completed Bachelor of Science in Computer Science from Delhi University. He is a tech enthusiast with good knowledge of Java. He is majorly focused in DevOps practice. On personal front he loves to travel mountains and writes poetry.

Leave a Reply