DEV Community

Cover image for Terraform Type Constraints
Anil KUMAR
Anil KUMAR

Posted on

Terraform Type Constraints

Today marks the Day 7 of 30 Days Terraform challenge and today we will deep dive into the Terraform Type Constraints and how we have multiple type constraints in Terraform based on the value.

Variables Based on Values in Terraform

Terraform divides the variables based on the values they hold. They are divided into 3 types such as Primitive, Complex, Any type.
As the name indicates Primitive types indicates simple data value type whereas Complex constraint type indicates complex data values.

Primitive Type Constraint:

Primitive types are the simplest and most commonly used variable types in Terraform. Primitive types are powerful because they enforce strict validation and avoid unexpected results especially when performing comparisons or arithmetic operations.There are exactly three of them: Strings, Numbers and Bool.

1. String Constraint:

A string is simply text. Anything inside quotes i.e. "hello", "production", "ap-south-1" is considered a string. It’s one of the most frequently used types in Terraform because so much of our infrastructure depends on names, IDs, regions, and descriptions… all of which are text.
Used for text-based values. Examnple, we can use this to set the AWS Provider configuration and apply a basic naming convention to S3 or any other resources.

 # string type
 variable "environment" {
     type = string
     description = "the environment type"
     default = "dev" 
 }
Enter fullscreen mode Exit fullscreen mode
provider "aws" {
    region = var.environment
}

resource "aws_s3_bucket" "example" {
  bucket = "my-tf-test-bucket-${var.environment}"

  tags = {
    Name        = "My bucket"
    Environment = var.environment
  }
}
Enter fullscreen mode Exit fullscreen mode

2: Number Constraint:

A number is exactly what it sounds like i.e. a numeric value like 10, 1, or 500. It seems simple, but defining it correctly in Terraform is important, because it tells Terraform what kind of value to expect.

 variable "instance_count" {
     type = number
     description = "the number of Ec22 instances to create"
     default = 1 
 }
Enter fullscreen mode Exit fullscreen mode
resource "aws_instance" "example" {
  ami           = "ami-005e54dee72cc1d00"
  instance_type = "t3.micro"
  count = var.instance_count
  tags = {
    Environment = var.environment
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Boolean:

Booleans are one of those things that look almost too small to matter i.e. they can only ever be true or false. That’s it. Just two possibilities. If strings are like words and numbers are like quantities, then booleans are like the “yes” and “no” signals that help Terraform understand what we want.

Think of a boolean like a simple switch. It’s either on or off.

Examples: Should the EC2 instance have a public IP, or should S3 have an encryption enabled.

 variable "associate_public_ip" {
     type = bool
     description = "associate public ip to ec2 instance"
     default = true
 }

resource "aws_instance" "example" {
  ami           = "ami-005e54dee72cc1d00"
  instance_type = "t3.micro"
  count = var.instance_count
  associate_public_ip_address = var.associate_public_ip
  tags = {
    Environment = var.environment
  }
}
Enter fullscreen mode Exit fullscreen mode

If associate_public_ip variable sets it to true, Terraform will happily give the instance a public IP.
If it stays false, the instance stays private and quiet in the VPC.

Complex Type Constraints:

So far, we’ve talked about primitive types. i.e. numbers, strings, and booleans. These are simple because they hold just one value at a time. But as we start working with real-world Terraform configurations, we’ll notice that many things come in groups. And when we want to store multiple values together in a clean, predictable way, Terraform gives us something called complex types.

Complex types are again divided into 5 types such as List, Set, Tuple, Maps and Dictionary.

1. Lists:

A list is exactly what it sounds like i.e. a small collection of items arranged in a specific order. Think of it like writing down your goals for the week or the number of days in a week.

Each item is written one after another, and the order matters because it helps us keep track of what comes first and what follows. Terraform treats lists the same way. All the items inside a list must be of the same type i.e. all strings, or all numbers, or all booleans. Elements in the list are referenced with a index starting with 0. List can contain duplicates too.

 variable "allowed_cidr_blocks" {
     type = list(string)
     description = "list of allowed cidr blocks for security group"
     default = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
 }
 variable "allowed_instance_types" {
     type = list(string)
     description = "list of allowed ec2 instance types"
     default = ["t2.micro", "t2.small", "t3.micro"]
     # Order matters: index 0 = t2.micro, index 1 = t2.small
 }
Enter fullscreen mode Exit fullscreen mode

main.tf:

cidr_block = var.allowed_cidr_blocks[0]
instance_type = var.allowed_instance_types[0]
Enter fullscreen mode Exit fullscreen mode

2. Sets:

A set is exactly what it sounds like i.e. a small collection of items arranged in a specific order. Think of it like writing down your goals for the week or the number of days in a week. It is same as set but with the limitation that id doesn't contain duplicates, so no duplication is allowed.

 variable "allowed_instance_types" {
     type = set(string)
     description = "list of allowed ec2 instance types"
     default = ["t2.micro", "t2.small", "t3.micro"]
 }
Enter fullscreen mode Exit fullscreen mode

main.tf:

resource "aws_instance" "example" {
  ami           = "ami-005e54dee72cc1d00"
  instance_type = tolist(var.allowed_instance_types[0])
}
Enter fullscreen mode Exit fullscreen mode

We cannot directly reference by index in set, we need to convert the set into list first and then should reference them.

3. Maps:

Maps is a collection of multiple key-value pairs. Instead of a single key or value, we will have a collection of key-value pairs, Example for tags in a EC2 instance, we have multiple tags, so we can use these in those places for simple and clean usage.

 variable "tags" {
     type = map(string)
     description = "list of tags for EC2 instances"
     default = {
       Environment = "dev"
       Name = "dev-instance"
 }
Enter fullscreen mode Exit fullscreen mode

In main.tf:

resource "aws_instance" "example" {
  ami           = "ami-005e54dee72cc1d00"
  instance_type = "t3.micro"

  tags = var.tags
}
Enter fullscreen mode Exit fullscreen mode

4. Tuple:

A tuple is simply a way of storing multiple values of different types, all together, in a specific order.
Each item is different. But they all belong together, in a certain order. It can have multiple values and they can be of multiple data types too.

variable "ingress_values" {
    type = tuple ({number, string, number })
    default = [443, "tcp", 443]
}
Enter fullscreen mode Exit fullscreen mode

main.tf:

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv4" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv4         = aws_vpc.main.cidr_block
  from_port         = var.ingress_values[0]
  ip_protocol       = var.ingress_values[1]
  to_port           = var.ingress_values[2]
}
Enter fullscreen mode Exit fullscreen mode

5. Dictionary:

It is more of a type Object. It is also a collection of multiple data types but not in the list format.

 variable "tags" {
     description = "list of tags for EC2 instances
     type = object({
       environment = string
       instance_count = number
       name = string
     })
     default = {
       environment = "dev"
       instance_count = 1
       name = "dev-instance"
 }
Enter fullscreen mode Exit fullscreen mode

Under main.tf:

resource "aws_instance" "example" {
  ami           = "ami-005e54dee72cc1d00"
  instance_type = "t3.micro"
  count = var.tags.instance_count

  tags = {
    Environment = var.tags.environment
    Name = var.tags.name
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion:

Before we wind up the blog, There are few key points which you need to remember when working with Terraform Type Constraints.

  1. Whenever there is a key-value pair, You should always access them with a key.
  2. Whenever there is a List, You should always access them with index.
  3. Whenever there is a map, You should always access them with a key-value pair.
  4. Whenever there is a set, You should first convert that to a list and then access that with index.

This is a large blog compared to the previous days and it is because, we have been introduced to a large number of terraform type constraints which will be majorly used when we start building projects to keep the Terraform code clean, organized and reusable.

Below is the Youtube Video for reference: Tech Tutorials with Piyush — “Terraform Type Constraints”

Top comments (0)