DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Automating AWS Infrastructure Testing With Terratest
  • Keep Your Application Secrets Secret
  • Container Checkpointing in Kubernetes With a Custom API
  • Terraform Tips for Efficient Infrastructure Management

Trending

  • Developers Beware: Slopsquatting and Vibe Coding Can Increase Risk of AI-Powered Attacks
  • What Is Plagiarism? How to Avoid It and Cite Sources
  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models
  • A Developer's Guide to Mastering Agentic AI: From Theory to Practice
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Testing Your Infrastructure as Code Using Terratest

Testing Your Infrastructure as Code Using Terratest

As more and more infrastructure becomes code, it is essential to have unit and integration tests for your IaC. What is IaC and testing your Infrastructure code mean?

By 
Akash Warkhade user avatar
Akash Warkhade
·
Jun. 23, 22 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
7.7K Views

Join the DZone community and get the full member experience.

Join For Free

Setting Up infrastructure manually can be a time-consuming and hectic process. That is when we can make use of Infrastructure as Code (IaC) tools to automate the infrastructure. IaC automation can be done for any kind of Infrastructure i.e, virtual machine, storage, etc. As more and more infrastructure becomes code, it is essential to have unit and integration tests for your IaC. We will briefly discuss what is IaC and testing your Infrastructure code mean. Then we deep dive into how we can use Terratest for IaC testing.

Let’s begin, shall we?

Infrastructure as Code (IaC)

Infrastructure as Code is the process of provisioning and configuring an environment through code instead of manually setting up the required infrastructure and supporting system for it through GUI. For example, provisioning a virtual machine, configuring it, and setting up monitoring for it. Some of the IaC examples are Terraform, Packer, Ansible, etc. With the help of infrastructure as code, you can also track your infrastructure into a version control system such as Git, modularize and templatize in order to reuse the same code for multiple environments and regions. Disaster recovery is one of the important benefits you get from coding your infrastructure. With IaC, you can replicate your Infrastructure in other regions or environments as quickly as possible.

Testing Infrastructure Code

IaC testing can be divided into multiple stages:

  1. Sanity or Static Analysis
  2. Unit testing
  3. Integration testing

Sanity or Static Analysis

This is the very initial phase of testing your infrastructure code. In static analysis, we ensure that we have the correct syntax for our code. It also helps to ensure that our code is as per the industry standards and follows the best practices. Linters fall into this category. Some examples of sanity testing tools are foodcritic for Chef, hadolint for Docker, tflint for Terraform, etc.

Unit Testing

With the help of unit testing, we assess our code without actually provisioning the infrastructure. Examples can be restricting your container to run as a non-root user, or your group should only have TCP protocols. Some of the unit testing examples are Conftest for Terraform and Chefspecs for Chef Cookbooks.

Conftest example for executing as a non root user:

 
package main

deny[msg] {
  input.kind == "Deployment"
  not input.spec.template.spec.securityContext.runAsNonRoot

  msg := "Containers must not run as root"
}


Integration Testing

In integration testing, we want to test our IaC by actually deploying it into the required environment. For example, you deployed a virtual machine and hosted an Nginx server on port 80 on that machine. So you will check if port 80 is listening after deployment.

Below is the example of doing that with ServerSpec:

 
describe port(80) do
  it { should be_listening }
end


In this post, we are exploring integration testing of infrastructure code using Terrratest.

What Is Terratest? What Can We Achieve With It?

Terratest is a Go library developed by Gruntwork that helps you create and automate tests for your Infra as Code written with Terraform, Packer for IaaS providers like Amazon, Google, or for a Kubernetes cluster. It provides you with various functions and patterns for tasks such as:

  • Testing Docker images, Helm charts, and Packer templates.
  • Allows to work with various cloud provider APIs such as AWS, and Azure.

Terratest executes sanity and functional testing for your Infrastructure code. With Terratest, you can easily identify issues in your current infrastructure code and fix the issue as soon as possible. We can also leverage Terratest for compliance testing of your infrastructure, for example, to have versioning and encryption enabled on any new S3 bucket created through your IaC.

Installation of Required Binaries for Terratest

Terratest mainly requires Terraform and Go for execution. In this blog post, we have used Terraform version 1.0.0 and Go version 1.17.6 for our testing.

Installing Terraform

Follow the downloads section from Terraform website to install Terraform on your machine; you can use the package manager or download the binary and make it available in PATH.

After installation, verify if it is installed properly by running the below command:

 
terraform version


Go & test dependency installation can be done with the following steps:

Installing Go

You can use your Linux distribution’s package manager to install Go or follow the installation documentation of Go.

go test Requires gcc for Test Execution

The go test command might require gcc, you can install it using your distribution’s package manager. For example, on CentOS/Amazon Linux 2, you can use yum install -y gcc.

Terratest in Action

Now we will execute some integration tests using terratest. Once the installation steps are complete, clone the terratest-sample repository to start executing terratests. We’ll start by writing the test using Go and execute it.

First things first:

  1. Your 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. Your test function name should start with Test with T being in capital letter. For example, TestFunction would work, but testFunction will give you an error “no tests to run”.

Setup AWS Auth Configuration

We need AWS credentials to set up the infrastructure in AWS; we can configure it using environment variables or shared credentials file. Refer to the Terraform documentation for more details.

Terraform code for infrastructure can be found in the respective folder of the component For ec2, it’s under ec2_instance, and for API gateway, it’s under api_gateway folder. Terratest takes the output from Terraform’s output.tf as input for its tests. Below is the snippet for testing if we have the same ssh key on the ec2 instance we have used.

 
package terratest

import (
   "testing"
   "github.com/stretchr/testify/assert"
   "github.com/gruntwork-io/terratest/modules/terraform"
)

func TestEc2SshKey(t *testing.T) {
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform",
    })
    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)
    ec2SshKey  := terraform.Output(t, terraformOptions, "instance_ssh_key")
    assert.Equal(t, "terratest", ec2SshKey)
}


We will divide it into different parts for proper understanding: In the first step, we are define a Go package named terratest, and then we are importing different packages required for test execution.

 
package terratest

import (
   "testing"
   "github.com/stretchr/testify/assert"
   "github.com/gruntwork-io/terratest/modules/terraform"
)


Once we have all the prerequisites, we will create a function to execute the actual test:

 
func TestEc2SshKey(t *testing.T) {
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../terraform",
    })
    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)
    ec2SshKey  := terraform.Output(t, terraformOptions, "instance_ssh_key")
    assert.Equal(t, "terratest", ec2SshKey)
}


In the below section, we are defining the directory where terratest should look for Terraform manifests i.e main.tf,output.tf for infrastructure creation.

 
 terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
     TerraformDir: "../terraform",
 })


In Go we use defer method to perform a cleanup task, it should be terraform destroy.

We are defining that using the below snippet:

defer terraform.Destroy(t, terraformOptions)

Now we can move forward to actual execution:

With terraform.InitAndApply we are invoking Terraform functions terraform init and apply which we generally use for Terraform execution:

 
 
  terraform.InitAndApply(t, terraformOptions)


As mentioned earlier, Terratest looks for output from output.tf for variable definition.

In the below snippet we are taking the ssh key from Terraform output and matching it with the ssh key name we have defined:

 
 ec2SshKey  := terraform.Output(t, terraformOptions, "instance_ssh_key")
    assert.Equal(t, "terratest", ec2SshKey)


Executing Tests

Switch your directory to the location where you have cloned the repository. Navigate to the location where you have test files located.

Initialize Go modules, and download the dependencies. Take a look at Setting up your project section of Terratest documentation for more details.

 
go mod init ec2_instance
go mod tidy


And finally, execute test:

 
$ go test -v

--- PASS: TestEc2SshKey (98.72s)
PASS
ok      command-line-arguments  98.735s


Let’s Go a Bit Advance With Terratest

In the previous section, we performed some basic levels of testing using Terratest. Now, we will perform an advanced test by deploying an API Gateway with Lambda and ALB as backend.

High-Level Functionality

GET request for API Gateway will be served by ALB and ANY method will be served by Lambda through API Gateway. After deployment, we will do an HTTP GET request against the gateway deployment URL and check if it’s returning a success code.

Note: In our execution, we are not using any API_KEY for authentication, but you should utilize it to replicate a more realistic use of API Gateway.

Terraform output.tf

 
output "lb_address" {
  value = aws_lb.load-balancer.dns_name
  description = "DNS of load balancer"
}


output "api_id" {
  description = "REST API id"
  value       = aws_api_gateway_rest_api.api.id
}



output "deployment_invoke_url" {
  description = "Deployment invoke url"
  value       = "${aws_api_gateway_stage.test.invoke_url}/resource"
}


Code Snippet for Test Execution

In the first scenario we have explained the basic syntax, so will directly go for the test function.

 
func TestApiGateway(t *testing.T) {
    //awsRegion := "eu-west-2"
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
		TerraformDir: "../",
	})
    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)
    stageUrl := terraform.Output(t, terraformOptions,"deployment_invoke_url")
    time.Sleep(30 * time.Second)
    statusCode := DoGetRequest(t, stageUrl)
    assert.Equal(t, 200 , statusCode)
}

func DoGetRequest(t terra_test.TestingT, api string) int{
   resp, err := http.Get(api)
   if err != nil {
      log.Fatalln(err)
   }
   //We Read the response status on the line below.
   return resp.StatusCode
}


In the above snippet, we have defined the function DoGetRequest to run an HTTP GET test. Then we used the output of this function as an input for the TestApiGateway function.

Test Execution and Output

 
TestApiGateway 2022-03-01T06:56:18Z logger.go:66: deployment_invoke_url = "https://iuabeqgmj2.execute-api.eu-west-1.amazonaws.com/test/resource"
TestApiGateway 2022-03-01T06:56:18Z logger.go:66: lb_address = "my-demo-load-balancer-376285754.eu-west-1.elb.amazonaws.com"
TestApiGateway 2022-03-01T06:56:18Z retry.go:91: terraform [output -no-color -json deployment_invoke_url]
TestApiGateway 2022-03-01T06:56:18Z logger.go:66: Running command terraform with args [output -no-color -json deployment_invoke_url]
TestApiGateway 2022-03-01T06:56:19Z logger.go:66: "https://iuabeqgmj2.execute-api.eu-west-1.amazonaws.com/test/resource"
--- PASS: TestApiGateway (42.34s)
PASS
ok      command-line-arguments  42.347s


As you can see it has executed our test function TestApiGateway, in which it has performed an HTTP GET test on deployment_invoke_url of API Gateway and returned test status.

Terratest Modules Extensibility and Compliance Testing Using Terratest

We can utilize Terratest for compliance testing also. Some of the examples can be:

  • Check if Encryption is enabled on your SQS Queue or S3 bucket.
  • Verify if you have a particular throttling limit set for the API gateway.

We have developed a Terratest check for API gateway. In this example, we are verifying if we have Authorizer added for your API gateway. You can find out more about what are authorizers.

Currently, Terratest does not have an API Gateway module in their AWS modules. You can find out available AWS modules in the Terratest AWS modules directory. Other Terratest modules such as Docker, Packer, and Helm can be found in the Terratest modules directory.

Enterprises and their customers want products to be shipped faster. Infrastructure as Code provides just that with faster provisioning of infrastructure. As more and more infrastructure becomes code, the need for testing increases. In this post, we discussed how a tool like Terratest can help validate your code before you deploy it to production. We showed how Terratest works and even executed test cases to show how it’s done. One of the good things about Terratest is its extensibility which can be achieved by means of using modules as talked about in the post.

API AWS Infrastructure Infrastructure as code Docker (software) Terraform (software) Testing

Published at DZone with permission of Akash Warkhade. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Automating AWS Infrastructure Testing With Terratest
  • Keep Your Application Secrets Secret
  • Container Checkpointing in Kubernetes With a Custom API
  • Terraform Tips for Efficient Infrastructure Management

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!