Read, Generate, Modify configuration
Count
An example usage of count
in Terraform is to create multiple instances of a resource based on a specified count. For example, the following code creates three instances of an AWS EC2 instance:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
count = 3
}
This will create three separate EC2 instances with unique identifiers.
In the below example ensure to add new values for instance_names at the end of the map else it will update or recreate all resources.
variable "instance_names" {
type = map(string)
default = {
"example-instance-1" = "ami-0c55b159cbfafe1f0"
"example-instance-2" = "ami-0c55b159cbfafe1f0"
"example-instance-3" = "ami-0c55b159cbfafe1f0"
}
}
resource "aws_instance" "example" {
count = length(var.instance_names)
ami = var.instance_names["example-instance-${count.index + 1}"]
instance_type = "t2.micro"
tags = {
Name = "example-instance-${count.index + 1}"
}
}
Default and Empty Value
In Terraform, if a variable has a default value or not and then in tfvars file you assign an empty value, the variable will finally have an empty value.
Custom Functions
You cannot create custom functions in Terraform. You have to use the ones you have. Please refer to the Terraform page with all functions listed here.
Console Command
You can get a prompt by using the terraform console
command.
Data Block and Filters
In Terraform, a data block is used to fetch read-only information from a provider's API. This data can then be used elsewhere in the configuration. Please refer to additional details regarding filters.
data "aws_ami" "amazon_linux" {
most_recent = true
filter {
name = "name"
values = ["amzn-ami-hvm-*"]
}
owners = ["amazon"]
}
Log Levels
Log levels can be set in Terraform by using the TF_LOG
environment variable. This environment variable controls the verbosity of the logs with the following levels: TRACE, DEBUG, INFO, WARN, and ERROR. If you want to direct the logs to a file, you can set the TF_LOG_PATH
environment variable to a filename where the logs should be written.
export TF_LOG_PATH=./terraform.log
Validation
The terraform validate
command is used to check whether a Terraform configuration is syntactically valid and internally consistent, regardless of any provided variables or existing state. Note that terraform validate
does not check whether the proposed changes will work against the real infrastructure. It's a static check of the configuration files.
terraform init -backend=false
terraform validate
Dynamic Blocks
Dynamic blocks in Terraform provide a way to dynamically construct repeating nested configuration blocks based on a provided complex value like a list or a map.
variable "ingress_rules" {
description = "List of maps of ingress rules"
default = [
{
description = "TLS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
},
{
description = "SSH from VPC"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
},
]
}
resource "aws_security_group" "example" {
name = "example"
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
# Example with iterator
resource "aws_security_group" "example" {
name = "example"
dynamic "ingress" {
for_each = var.ingress_rules
iterator = rule
content {
description = rule.value.description
from_port = rule.value.from_port
to_port = rule.value.to_port
protocol = rule.value.protocol
cidr_blocks = rule.value.cidr_blocks
}
}
}
Drift Detection and Manual Changes
Drift detection: Terraform drift detection is the process of comparing a Terraform state file to monitoring metrics provided by the actual infrastructure to find discrepancies that would indicate configuration drift. However, Terraform cannot detect drift of resources and their associated attributes that are not managed using Terraform. Please refer to the Terraform Cloud documentation.
Manual changes in cloud: terraform refresh
updates the state file when physical resources change outside of the Terraform workflow. You can use the terraform refresh
command to update the state file when there are manual changes in the cloud. You can run terraform apply
again to revert your manual changes. However, it is important to note that this will also revert any other changes made since the last successful terraform apply
. Please refer to this article.
****terraform refresh
is deprecated.
Taint and Replace
Summary: terraform taint
and replace
are useful for managing resources modified outside of Terraform's control. Taint
marks a resource as tainted, and Terraform destroys and recreates it the next time it runs. Replace
provides more control over how the resource is recreated. You cannot replace multiple resources using the terraform taint
command.
terraform taint
marks a resource as tainted and proposes to replace itterraform replace
provides more control over how the resource is recreatedterraform taint
only works on one resource at a time
Syntax below:
terraform taint aws_instance.example
terraform apply -replace=aws_instance.example
terraform apply -replace=aws_instance.example1 -replace=aws_instance.example2
terraform plan -replace=aws_instance.example
Here are a few real-world scenarios where you might use terraform taint
:
- Debugging a Configuration Issue: If a resource was misconfigured and you need to force Terraform to recreate it during the next apply to test changes or debug issues.
- Updating Immutable Resources: Some cloud resources (like certain types of AWS IAM roles or Lambda functions) don't support updates to all fields after creation. Tainting such resources can force a recreate to apply changes.
- Refreshing Credentials or Secrets: If you need to rotate secrets or credentials managed by Terraform and want to ensure that the new values are picked up, tainting the resource can enforce this update.
- Recovering from a Corrupted State: In cases where a resource's state file does not match the actual state in the cloud (due to manual changes or errors), tainting the resource allows Terraform to reconcile the state by recreating it.
Splat Expression *
The splat expression *
in Terraform references all of the attributes of the aws_instance.example
resource.
Example:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
output "all_attributes" {
value = aws_instance.example.*
}
Graph
The terraform graph
command generates a visual representation of the Terraform dependency graph. To view the graphical picture view, you can copy the output of the terraform graph
command and paste it into a Graphviz visualization tool such as WebGraphviz.
Command:
terraform graph
Terraform apply on plan file
terraform plan -out=tfplan
terraform apply tfplan
Workspaces
Terraform workspaces are a way to manage multiple environments with the same Terraform configuration. Workspaces allow you to create multiple instances of state data inside the same Terraform working directory.
Example:
terraform workspace new dev
terraform workspace select dev
Workspaces and environments
You can use different variable definition files for each environment along with Terraform workspaces. Here’s a streamlined way to do it:
- Organize Variable Files: Create a
.tfvars
file for each environment, such asdev.tfvars
for development andtest.tfvars
for testing. -
Select a Workspace: Use Terraform workspaces to manage different environments. Example command:
bash bashCopy code terraform workspace select dev
-
Apply Configuration with Variable File: When applying your Terraform configuration, specify the variable file for the selected workspace. Example command:
bash bashCopy code terraform apply -var-file="dev.tfvars"
Repeat the selection and application steps for each workspace using the corresponding variable file. This method isolates environment configurations and maintains a clean, DRY codebase.
Terraform Output
The terraform output
command is used to view the values of outputs in a Terraform configuration.
You explicitly call terraform output
after terraform apply
to display all outputs.
output "instance_id" {
value = aws_instance.example.id
}
terraform output instance_id
ZipMap function
Let's say you have a list of AWS security group names and another list of AWS security group IDs, and you want to create a map that maps the security group names to their corresponding IDs. You can use the zipmap
function to create this map:
locals {
security_group_names = ["web", "db", "app"]
security_group_ids = ["sg-0123456789abcdefg", "sg-abcdef0123456789", "sg-9876543210fedcba"]
security_group_map = zipmap(local.security_group_names, local.security_group_ids)
}
In this example, security_group_names
and security_group_ids
are two lists that are combined into a single map called security_group_map
. The resulting map would look like this:
{
"web" = "sg-0123456789abcdefg"
"db" = "sg-abcdef0123456789"
"app" = "sg-9876543210fedcba"
}
zipmap
is useful when you need to map one set of values to another set of values. In this example, we used it to map security group names to their corresponding IDs.
Comments
There are different ways to add comments in Terraform code:
- Single line comments:
# This is a comment
- Multi-line comments:
/* This is a comment */
- Documentation comments:
/** This is a comment */
- Inline comments:
resource "aws_instance" "example" { # This is a comment }
- Commented code:
# resource "aws_instance" "example" {}
Set function
- To define a set in Terraform, use the
toset()
function. - The
toset()
function takes a list as an argument and returns a set. - Sets are unordered and contain only unique values.
- You can use sets to remove duplicates from a list of values.
- Sets can be useful when performing set operations, such as union, intersection, or difference, on two lists.
- When using sets, it's important to remember that they are unordered, so you cannot rely on the order of elements within the set.
For Each
Here's an example Terraform configuration:
provider "aws" {
region = "us-west-2" # Set your desired AWS region here
}
# Define a map of EC2 instance configurations
locals {
instance_configs = {
"web-server" = {
instance_type = "t2.micro"
ami_id = "ami-0c55b159cbfafe1f0" # Replace with your desired AMI ID
subnet_id = "subnet-12345678" # Replace with your desired subnet ID
security_group = "sg-abcdefgh" # Replace with your desired security group ID
}
"db-server" = {
instance_type = "t2.small"
ami_id = "ami-0123456789abcdef0" # Replace with your desired AMI ID
subnet_id = "subnet-87654321" # Replace with your desired subnet ID
security_group = "sg-ijklmnop" # Replace with your desired security group ID
}
}
}
# Create EC2 instances using the for_each meta-argument
resource "aws_instance" "ec2_instance" {
for_each = local.instance_configs
ami = each.value.ami_id
instance_type = each.value.instance_type
subnet_id = each.value.subnet_id
vpc_security_group_ids = [each.value.security_group]
tags = {
Name = each.key
}
}
The each.key
represents the unique identifier (e.g., "web-server"
, "db-server"
) and each.value
represents the corresponding configuration parameters.
Retrieving remote state
The data.terraform_remote_state
data source uses the latest state snapshot from a specified state backend to retrieve the root module output values from some other Terraform configuration. Here’s an example of how to use the data.terraform_remote_state
data source in a Terraform configuration:
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = "mybucket"
key = "path/to/my/key"
region = "us-east-1"
}
}
output "vpc_id" {
value = data.terraform_remote_state.vpc.outputs.vpc_id
}
In this example, the data.terraform_remote_state.vpc
data source is used to retrieve the root module output values from a Terraform configuration stored in an S3 bucket. The backend
and config
arguments are used to specify the state backend and its configuration. The output
block is used to define an output value that references the vpc_id
output value from the remote state.
Implicit and Explicit Depandancy
Implicit Dependencies
Implicit dependencies occur when one resource configuration references attributes of another, causing Terraform to automatically determine the order of creation.
Example:
hclCopy code
resource "aws_instance" "example" {
subnet_id = aws_subnet.example.id
}
Here, the creation of aws_instance
implicitly depends on aws_subnet
.
Explicit Dependencies
Explicit dependencies are declared using the depends_on
attribute, allowing you to specify dependencies that Terraform can't automatically infer.
Example:
hclCopy code
resource "aws_instance" "example" {
# other configuration
depends_on = [aws_iam_role_policy.example]
}
In this case, aws_instance
creation explicitly waits for aws_iam_role_policy
to be created.