What's Actually Happening

You're passing values to Terraform variables that don't match the declared type. Terraform's type system is strict about type matching, and mismatches cause immediate failures during validation or plan phases.

The Error You'll See

Basic type mismatches:

``` Error: Invalid value for input variable

on variables.tf line 15: The given value is not suitable for var.instance_count declared at variables.tf:15,1: number required. ```

Collection type errors:

``` Error: Invalid value for input variable

on variables.tf line 23: The given value is not suitable for var.subnet_ids declared at variables.tf:23,1: list of string required. ```

Object type errors:

``` Error: Invalid value for input variable

on variables.tf line 45: The given value is not suitable for var.tags declared at variables.tf:45,1: all object attributes must have corresponding variable attributes.

Attribute "Environment" is required. ```

Complex nested types:

``` Error: Invalid value for input variable

on variables.tf line 67: The given value is not suitable for var.server_config declared at variables.tf:67,1: attribute "instance_type": string required. ```

Why This Happens

Common causes of type mismatch errors:

  1. 1.Wrong primitive type - Passing string when number expected
  2. 2.Null vs empty values - Passing null instead of empty string/list
  3. 3.Collection type mismatch - List vs set vs tuple
  4. 4.Map key type errors - Non-string keys in maps
  5. 5.Object attribute missing - Required object field not provided
  6. 6.TFVARS formatting - Incorrect .tfvars syntax
  7. 7.Environment variable strings - All env vars are strings
  8. 8.JSON parsing issues - Improper JSON to HCL conversion

Step 1: Understand Variable Type Declarations

Terraform supports several type constraints:

```hcl # Basic types variable "instance_count" { type = number default = 1 }

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

variable "enabled" { type = bool default = true }

# Collection types variable "subnet_ids" { type = list(string) default = [] }

variable "availability_zones" { type = list(string) default = ["us-east-1a", "us-east-1b"] }

variable "tags" { type = map(string) default = {} }

variable "unique_ids" { type = set(string) default = [] }

# Tuple (fixed-size list with specific types) variable "coordinates" { type = tuple([number, number, string]) default = [10, 20, "point-a"] }

# Object (map with required attributes) variable "server_config" { type = object({ instance_type = string ami_id = string volume_size = number }) default = { instance_type = "t3.micro" ami_id = "ami-12345678" volume_size = 20 } } ```

Step 2: Fix Basic Type Mismatches

When passing values via command line or tfvars:

```bash # Wrong: passing string as number terraform apply -var="instance_count=3" # This is actually correct terraform apply -var="instance_count=three" # Wrong - string not number

# Correct: pass number terraform apply -var="instance_count=3"

# Wrong: passing string as bool terraform apply -var="enabled=true" # String "true", not bool true

# Correct: for bools, use proper format # In terraform.tfvars: enabled = true # Boolean, not string

# Or via environment (all env vars are strings!) export TF_VAR_enabled=true # This becomes string "true" # Terraform will convert, but be explicit ```

In terraform.tfvars:

hcl
# Correct type assignments
instance_count = 3                  # number
environment   = "production"        # string
enabled       = true                # bool
subnet_ids    = ["subnet-1", "subnet-2"]  # list(string)
tags = {                            # map(string)
  Name        = "web-server"
  Environment = "prod"
}

Step 3: Handle Null vs Empty Values

Null and empty values behave differently:

```hcl variable "optional_tags" { type = map(string) default = null # Can be null or empty map }

variable "required_tags" { type = map(string) default = {} # Empty map, not null } ```

When using these variables:

```hcl # Handle null safely resource "aws_instance" "web" { ami = var.ami_id instance_type = var.instance_type

tags = var.optional_tags != null ? var.optional_tags : {} }

# Or use coalesce tags = coalesce(var.optional_tags, {})

# For optional object attributes variable "config" { type = object({ name = string value = optional(string, "default") }) } ```

Step 4: Fix List and Set Confusion

Lists and sets are different:

```hcl # List - ordered, allows duplicates variable "subnet_ids" { type = list(string) default = ["subnet-1", "subnet-2", "subnet-1"] # Duplicates allowed }

# Set - unordered, unique values only variable "unique_security_groups" { type = set(string) default = ["sg-1", "sg-2"] # Duplicates removed automatically }

# Converting between them variable "my_list" { type = list(string) default = ["a", "b", "c"] }

output "as_set" { value = toset(var.my_list) # Convert list to set }

output "as_list" { value = tolist(toset(var.my_list)) # Convert back (deduplicated) } ```

When passing lists via command line:

```bash # Wrong: string instead of list terraform apply -var="subnet_ids=subnet-1"

# Correct: JSON format for complex types terraform apply -var='subnet_ids=["subnet-1","subnet-2"]'

# Or use -var-file terraform apply -var-file="prod.tfvars" ```

Step 5: Fix Map and Object Issues

Maps require string keys:

```hcl # Correct: string keys variable "tags" { type = map(string) default = { "Name" = "web-server" "Environment" = "production" } }

# Also correct: unquoted keys (valid identifiers) variable "tags" { type = map(string) default = { Name = "web-server" Environment = "production" } }

# Wrong: numeric keys (must be strings) variable "ports" { type = map(string) # default = { 80 = "http", 443 = "https" } # Error!

# Correct: convert to strings default = { "80" = "http", "443" = "https" } } ```

Object attribute mismatches:

```hcl variable "server" { type = object({ name = string instance_type = string port = number }) }

# Correct assignment server = { name = "web-01" instance_type = "t3.micro" port = 80 }

# Wrong: missing required attribute server = { name = "web-01" instance_type = "t3.micro" # port is missing - error! }

# Wrong: wrong type for attribute server = { name = "web-01" instance_type = "t3.micro" port = "80" # String, not number - error! } ```

Step 6: Handle Optional Object Attributes

Use optional() for flexible objects:

hcl
variable "instance_config" {
  type = object({
    name          = string
    instance_type = string
    ami           = optional(string)
    tags          = optional(map(string), {})
    monitoring    = optional(bool, false)
  })
  default = {
    name          = "default"
    instance_type = "t3.micro"
    # ami omitted - will be null
    # tags omitted - will be {}
    # monitoring omitted - will be false
  }
}

Step 7: Debug Variable Values

Inspect actual variable values:

```bash # Output all variables for debugging terraform console > var.instance_count 3 > type(var.subnet_ids) list(string) > var.tags {"Environment" = "production", "Name" = "web-server"}

# Use terraform output for computed values terraform output -json | jq .

# Debug in configuration output "debug_variables" { value = { instance_count_type = type(var.instance_count) subnet_ids_type = type(var.subnet_ids) tags_type = type(var.tags) } } ```

Step 8: Fix Environment Variable Issues

Environment variables are always strings:

```bash # Environment variables passed to Terraform export TF_VAR_instance_count=3 # This is STRING "3", not number

# Terraform will attempt to convert, but explicit is better # In terraform.tfvars or -var: instance_count = 3 # Proper number

# For complex types via environment, use JSON export TF_VAR_config='{"name":"web","port":80}'

# Then in variables.tf variable "config" { type = object({ name = string port = number }) } ```

Step 9: Validate Variable Files

Check .tfvars syntax:

```bash # Validate syntax terraform fmt -check terraform.tfvars

# Check if variables are valid terraform validate

# View parsed variables terraform console > var

# Use JSON format for validation cat terraform.tfvars.json { "instance_count": 3, "subnet_ids": ["subnet-1", "subnet-2"], "tags": { "Name": "web-server" } } ```

Step 10: Common Type Conversion Patterns

Explicit type conversions:

```hcl # Convert string to number number_value = tonumber("42")

# Convert number to string string_value = tostring(42)

# Convert to boolean bool_value = tobool("true")

# Convert list to set set_value = tolist(var.my_set)

# Convert map to object (careful!) object_value = { name = var.map_value["name"] type = var.map_value["type"] }

# Safe conversion with defaults safe_number = tonumber(var.string_number) != null ? tonumber(var.string_number) : 0

# Using can() for safe conversion safe_value = can(tonumber(var.input)) ? tonumber(var.input) : 0 ```

Verify the Fix

After correcting type mismatches:

```bash # Validate configuration terraform validate

# Success message: # Success! The configuration is valid.

# Plan with specific variables terraform plan -var="instance_count=3" -var="subnet_ids=['subnet-1','subnet-2']"

# Or with variable file terraform plan -var-file="prod.tfvars" ```

Check variable types in console:

bash
terraform console
> type(var.instance_count)
number
> type(var.subnet_ids)
list(string)
> var.subnet_ids
tolist(["subnet-1", "subnet-2"])