Setting up infrastructure manually can be both time-consuming and challenging. This is where Infrastructure as Code (IaC) tools come into play, offering automation for provisioning and managing infrastructure. With IaC, automation is achieved for setting up resources like virtual machines, storage, and more. As infrastructure increasingly becomes code, implementing unit and integration tests becomes crucial to ensure its reliability. This blog explores what IaC is and why testing it matters, followed by a detailed look at using Terratest for infrastructure testing.
Let’s dive in!
Infrastructure as Code refers to the practice of automating the provisioning and configuration of infrastructure using code, rather than manually setting it up through a graphical interface. For instance, virtual machines can be defined and deployed, configured, and integrated with monitoring systems—all through code. Examples of IaC tools include Terraform, Packer, and Ansible.
Terratest is a Go-based library created by Gruntwork that assists in writing and automating tests for Infrastructure as Code (IaC) projects, particularly those developed with Terraform, Packer, or for cloud platforms like AWS and Google Cloud, as well as Kubernetes clusters. It provides several utilities and patterns to help with:
With Terratest, both basic sanity checks and detailed functional tests can be conducted on infrastructure code. This tool helps identify and resolve issues efficiently within infrastructure configurations. Additionally, Terratest supports compliance testing, ensuring that infrastructure components, such as newly created S3 buckets, adhere to best practices like enabling versioning and encryption.
Infrastructure as Code (IaC) has become a cornerstone of modern cloud infrastructure management, allowing teams to automate the provisioning and management of infrastructure using code. However, just like any software application, IaC must be tested to ensure that it functions correctly and securely in a production environment. Here are some key reasons why testing IaC is essential:
1. Testing helps catch configuration errors early, preventing costly issues in production.
2. Ensures the same infrastructure setup in development, staging, and production environments.
3. Validates security configurations, preventing vulnerabilities like misconfigured access control.
4. Automated tests in CI/CD pipelines allow for faster, more reliable infrastructure changes.
To run Terratest for testing Infrastructure as Code (IaC) in this blog, ensure the following prerequisites are met:
1. Go Lang (Version 1.20 or Above): Terratest is built using Go, requiring version 1.20 or higher. The current Go version can be checked by running:
go version
2. Terraform:Since this blog focuses on testing Terraform code, having Terraform installed is essential. The Terraform version can be checked with:
terraform version
Now we will execute some integration tests using terratest. Once the installation steps are complete, We’ll start by writing the test using Go and execute it.
First things first:
1. Test file name should have _test
in its name for example sample_test.go
. This is how a Go looks for the test files.
2. Test function name should start with Test
with T being in capital letter. For example, TestFunction
would work but testFunction
will throw
an error “no tests to run”.
//Create a new project directory:
terratest-example/
├── main.tf # Terraform configuration
├── outputs.tf # Terraform outputs
├── variables.tf # Terraform variables
├── test/
└── main_test.go # Terratest code
Here’s an example test for an AWS S3 bucket using Terratest:
1. Terraform Configuration
# main.tf
resource "aws_s3_bucket" "example" {
bucket = var.bucket_name
acl = "private"
}
# variables.tf
variable "bucket_name" {
description = "The name of the S3 bucket."
}
# outputs.tf
output "bucket_name" {
value = aws_s3_bucket.example.bucket
}
2. Test Code
// test/main_test.go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestS3BucketCreation(t *testing.T) {
t := testing.T()
// Define Terraform options
terraformOptions := &terraform.Options{
TerraformDir: "../",
Vars: map[string]interface{}{
"bucket_name": "terratest-example-bucket",
},
}
// Ensure infrastructure is destroyed after test
defer terraform.Destroy(t, terraformOptions)
// Run Terraform Init and Apply
terraform.InitAndApply(t, terraformOptions)
// Get output values
bucketName := terraform.Output(t, terraformOptions, "bucket_name")
// Assert bucket name matches expected value
assert.Equal(t, "terratest-example-bucket", bucketName)
}
Now, run the terraform init command. After that go to test directory and run the following commands to run the test:
go mod init //e.g. terratest-test
go mod tidy
go test -v
After running this command, the logs can be viewed where Terratest checks the Terraform code and identifies any errors or vulnerabilities, if present.
Terratest bridges the gap between IaC and software testing, enabling teams to validate infrastructure with confidence. By incorporating Terratest into workflows, robust, reliable, and cost-effective infrastructure provisioning can be ensured. Starting small, iterating, and gradually expanding test coverage allows for achieving the full benefits of IaC testing.