DEV Community

Cover image for Day 07 : Mastering Terraform Type Constraints(Lists,Sets,Tuples,Maps&Objects)
Zakariyau Mukhtar
Zakariyau Mukhtar

Posted on

Day 07 : Mastering Terraform Type Constraints(Lists,Sets,Tuples,Maps&Objects)

Today was all about Terraform type constraints.I finally understood how Terraform handles different data types, how to structure them properly, and how powerful they become when you combine them inside real infrastructure code.

What I Learned Today

1. Type Constraints Make Terraform Safer and Cleaner

I finally understood why Terraform allows you to define types for your variables.
It forces consistency, reduces errors, and makes your configuration more predictable.

2. List & Set Types (and how to index them)

  • List : ordered, allows duplicates, access with index ([0], [1])
  • Set : unordered, unique values, must convert to list for indexing

I used list and set types heavily today especially for selecting regions, VM types, and CIDR blocks.

3. Tuples and Map Types

  • Tuple : ordered collection of mixed types
  • Map : key/value structure, perfect for tags and metadata

Using a tuple for ingress rules was interesting because each element had a different type:
number, string, number.

4. Nested Types (Objects Inside Objects)

I learned how to nest complex constraints using the object() type and how to pass an entire structure (region, monitoring, instance_count) into resource blocks cleanly.
It felt like switching from Terraform beginner scripting to Terraform engineering.

Commands I Used Today

Only two commands were used today, simple but essential:

  • terraform init
  • terraform plan

Because today was more about variable types and configuration structure than provisioning new resources.

My Terraform Files for Day 07

Below is exactly what I used today nothing added, nothing removed.

main.tf

resource "aws_instance" "example" {
  count = var.config.instance_count
  ami           = "resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
  instance_type = var.allowed_vm_type[1]
  #region        = tolist(var.allowed_region)[0]
  region = var.config.region
  monitoring = var.config.monitoring
  associate_public_ip_address = var.associate_public_ip
  tags = var.tags
}

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic and all outbound traffic"
  tags = var.tags
}

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

resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
}
Enter fullscreen mode Exit fullscreen mode

variable.tf

variable "environment" {
  type    = string
  default = "dev"
}

variable "instance_count" {
  description = "Number of EC2 instances to create"
  type        = number
}

variable "region" {
  type    = string
  default = "us-east-1"
}

variable "monitoring_enabled" {
  description = "Enable detailed monitoring for EC2 instances"
  type        = bool
  default     = true
}

variable "associate_public_ip" {
  description = "Associate public IP address with EC2 instance"
  type        = bool
  default     = true
}

variable "cidr_block" {
  description = "CIDR block for the VPC"
  type        = list(string)
  default     = ["10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12"]
}

variable "allowed_vm_type" {
  description = "List of allowed VM types"
  type        = list(string)
  default     = ["t2.micro", "t2.small", "t3.micro", "t3.small"]
}

variable "allowed_region" {
  description = "List of allowed AWS regions"
  type        = set(string)
  default     = ["us-east-1", "us-west-2", "eu-west-1"]
}

variable "tags" {
  type = map(string)
  default = {
    Environment = "dev"
    Name        = "dev-Instance"
  }
}

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

variable "config" {
  type = object({
    region         = string,
    monitoring     = bool,
    instance_count = number
  })
  default = {
    region         = "us-east-1",
    monitoring     = true,
    instance_count = 1
  }
}
Enter fullscreen mode Exit fullscreen mode

backend.tf

terraform {
  backend "s3" {
    bucket       = "devopswithzacks-terraform-state"
    key          = "dev/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true
  }
}
Enter fullscreen mode Exit fullscreen mode

.gitignore

.terraform.lock.hcl
terraform.tfvars
.terraform/
*.tfvars.json
*.log
crash.log
*.terraform.*backup
*.tfstate
*.tfstate.backup
Enter fullscreen mode Exit fullscreen mode

Final Thoughts for Day 07

Understanding type constraints, nested data structures, and how to access values inside them completely transformed how I write Terraform configurations. These aren’t just “variables” anymore these are structured, predictable inputs that make scaling infrastructure easier and safer.

Every day, Terraform becomes more intuitive, and these building blocks are exactly what I’ll need for more complex modules later in the challenge.
Day 07 DONE.
On to Day 08.

Top comments (0)