What is Terraform?
Terraform is an open source infrastructure-as-code tool developed by Hashicorp. Terraform offers a solution that allows you to define resources and infrastructure in simple, readable configuration files, and automates the entire lifecycle of your infrastructure.
According to the official Terraform documentation:
Terraform is an infrastructure as code tool that lets you build, change, and version cloud and on-prem resources safely and efficiently.
Terraform allows us to work with different providers. In the following example, we create a main.tf
file with a configuration to create a simple AWS EC2.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "app_server" {
ami = "ami-830c94e3"
instance_type = "t2.micro"
tags = {
Name = "ExampleAppServerInstance"
}
}
Code language: JavaScript (javascript)
Once we have our infrastructure in code ready, there are a number of recommended steps to provision.
To make the initial dependency selections that will initialize the dependency lock file, run the following:
terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 4.16"...
- Installing hashicorp/aws v4.58.0...
- Installed hashicorp/aws v4.58.0 (signed by HashiCorp)
Terraform has been successfully initialized!
Code language: JavaScript (javascript)
If we want to provision this EC2, it is preferable to first make a terraform plan to create an execution plan and confirm that we have everything we need. This happens if we do it with the current state.
terraform plan
Error: configuring Terraform AWS Provider: no valid credential sources for Terraform AWS Provider found.
We have not provisioned valid credentials for the provider we are using, AWS. Therefore, we need to have the credentials first. This can be done using environment variables:
export AWS_ACCESS_KEY_ID=<Insert your ACCESS_KEY>
export AWS_SECRET_ACCESS_KEY=<Insert your SECRET_KEY>
Code language: HTML, XML (xml)
Now we can launch the terraform apply
command without problems and the EC2 machine will be provisioned with the parameters that we have indicated in the main.tf
document.
This is an example of Terraform 101, but you will find more on this page:
- Terraform versioning.
- Test Terraform manifests.
- Use variables to make modules reusables.
- Use environment variables.
- Troubleshoot Terraform – Language errors.
Terraform versioning
Always use Terraform versioning to ensure that your Terraform manifests are compatible with the correct version of Terraform. From a Terraform security perspective, this should help avoid compatibility issues and ensure that your infrastructure code is consistent across all environments.
#!/bin/bash
# Set the path to your Terraform code
TERRAFORM_PATH="./my-terraform-code"
# Set the required Terraform version
TERRAFORM_VERSION="1.0.11"
# Check if the required Terraform version is installed
if ! command -v terraform &> /dev/null
then
echo "Terraform is not installed.
Please install the Terraform version $TERRAFORM_VERSION and try again."
exit
fi
# Check the installed Terraform version
INSTALLED_VERSION=$(terraform version | head -n 1 | cut -d ' ' -f 2)
# Compare the installed version with the required version
if [ "$INSTALLED_VERSION" != "$TERRAFORM_VERSION" ]
then
echo "Terraform version $TERRAFORM_VERSION is required but version $INSTALLED_VERSION
is installed. Please install the required version and try again."
exit
fi
# Run Terraform commands on the specified path
terraform plan "$TERRAFORM_PATH"
Code language: PHP (php)
Once you’ve saved the script, you can run it by navigating to the directory where it’s saved and executing the following command in your terminal:
sh my-script.sh
Code language: CSS (css)
This will execute the script and check the installed version of Terraform against the required version specified in the script. If the installed version doesn’t match the required version, the script will output an error message and exit. If the installed version matches the required version, the script will continue and run Terraform commands on the specified path.
Alternatively, Flora is a solid open-source tool that can be used to store Terraform manifests and improve Terraform security.
Using Flora can also help improve Terraform security. It provides an additional layer of security by storing your Terraform manifests in a separate Git repository, which can help prevent unauthorized access and ensure that changes are tracked and audited. Additionally, Flora allows you to use Git’s built-in access controls to manage permissions and ensure that only authorized users can make changes to your infrastructure code.
Here is an example of how Flora can be used to improve Terraform security by enforcing policies using Checkov:
1. Install Flora and Checkov
First, you will need to install Flora and Checkov.
Flora can be installed using pip, and Checkov can be installed using your package manager or directly from GitHub.
pip install floraapt install checkov
2. Initialize a new Flora repository
Next, initialize a new Flora repository to store your Terraform manifests.
Flora will create a new Git repository and configure it for use with Terraform.
flora init my-infrastructurecd my-infrastructure
3. Add Terraform manifests to the Flora repository
Now, add your Terraform manifests to the Flora repository.
You can add your manifests directly to the repository or use Terraform modules.
flora add terraform/manifests
4. Configure Checkov to run on commit
Next, you will configure Checkov to run automatically when changes are made to your Terraform manifests. This will help enforce security policies and prevent issues from being introduced into your infrastructure code.
flora hooks add --name checkov --cmd "checkov -d ./terraform/manifests" --on commit
Code language: JavaScript (javascript)
5. Test the Flora repo
Finally, test the Flora repository to ensure that Checkov is working correctly. Make a change to one of your Terraform manifests and commit the change. Checkov should run automatically and identify any policy violations.
Failed checks: CKV_AWS_19: "Ensure the S3 bucket has versioning enabled"
Code language: JavaScript (javascript)
In this example, Checkov is used to enforce a policy that requires all S3 buckets to have versioning enabled. If versioning is not enabled, Checkov will identify the issue and prevent the commit from being made.
By always using Terraform versioning, you can ensure that your Terraform manifests are compatible with the correct version of Terraform and avoid compatibility issues. This helps ensure that your infrastructure code is consistent across all environments and reduces the risk of security issues related to compatibility issues.
Test terraform manifests
Testing your Terraform manifests can help catch errors and ensure that your infrastructure code is working as expected. Use a tool like Terratest to automate testing and ensure that your Terraform manifests are working as expected.
#!/bin/bash
# Set the path to your Terraform code
TERRAFORM_PATH="./my-terraform-code"
# Set the path to your Terratest code
TERRATEST_PATH="./my-terratest-code"
# Run Terraform init on the specified path
terraform init "$TERRAFORM_PATH"
# Run Terraform apply on the specified path
terraform apply -auto-approve "$TERRAFORM_PATH"
# Run Terratest on the specified path
go test -v "$TERRATEST_PATH"
Code language: PHP (php)
Note: As always, these scripts assume you have installed Terraform CLI and Terratest on your machine and have added them to your system’s PATH variable. In the case of Terratest, like Hashicorp Vault, this is a third-party solution. Please check the official terratest.gruntwork.io documentation if you have concerns with the tooling. Once again, we save the script and proceed to run it by navigating to the directory where it’s saved and executing the following command in your terminal:
sh my-script.sh
Code language: CSS (css)
This will execute the script and run Terraform commands to apply your infrastructure code, and then run Terratest to test it. Terratest is a Go library that makes it easier to write automated tests for your infrastructure code. By testing your Terraform manifests with Terratest, you can catch errors and ensure that your infrastructure code is secure.
Use variables to make the modules reusable
Use variables to make your Terraform modules reusable. This will allow you to avoid hardcoding “Secrets” values, for example, making it easier to customize your modules for different environments. Use environment variables or input variables to pass values to the Terraform module, making it easier to manage secrets and other sensitive information.
# variables.tf
variable "region" {
type = string
default = "eu-west-1"
}
variable "instance_type" {
type = string
default = "t2.micro"
}
variable "ami" {
type = string
default = "ami-0c55b159cbfafe1f0"
}
# main.tf
provider "aws" {
region = var.region
}
resource "aws_instance" "example" {
ami = var.ami`
instance_type = var.instance_type
# ...
}
Code language: PHP (php)
In the above example, we defined three variables in the variables.tf file.
These variables are used in the main.tf file to configure the aws_instance resource.
- region
- instance_type
- ami
Use environment variables
By using variables, we can reuse this module in multiple environments without hardcoding the values.
For example, we can pass different values for these variables when we apply this module in a different environment.
module "example_instance" {
source = "./example-instance"
region = "eu-west-1"
instance_type = "t2.small"
ami = "ami-03d315ad33b9d49c4"
}
Code language: JavaScript (javascript)
In the above example, we used the module block to reference the example-instance module and passing values for the region, instance_type, and AMI variables. By using input variables, we can easily manage secrets and other sensitive information without exposing them in the Terraform configuration files.
We can pass these values through environment variables or use a secret management system, like Vault or AWS Secrets Manager, to retrieve the values dynamically.
Troubleshoot Terraform – Language errors
Use a linter like Terraform Validate to check the syntax of your Terraform code. This will help catch errors before you apply changes to your infrastructure. From a security perspective, linting can also help enforce coding standards and best practices.
#!/bin/bash
# Set the path to your Terraform code
TERRAFORM_PATH="./my-terraform-code"
# Run Terraform validate on the specified path
terraform validate "$TERRAFORM_PATH"
Code language: PHP (php)
This script assumes that you have installed the Terraform CLI on your machine and have added it to your system’s PATH variable. Once you’ve saved the script, you can run it by navigating to the directory where it’s saved and executing the following command in your terminal:
sh my-script.sh
Code language: CSS (css)
Alternatively, you can make the script executable and then run the new executable:
Chmod +x my-script.sh
./my-script.sh
This will execute the script and run terraform validate on your Terraform code. If there are any syntax errors or issues with your code, Terraform will output an error message indicating the problem. You can use this process to catch errors before applying changes to your infrastructure.
Conclusion
Getting started is not complicated, but becoming an expert takes time and experience. We hope that following these recommendations will make it easier to provision infrastructure through code using Terraform. If you are interested in learning more about security, we recommend the Terraform security best practices.