Ravindra BagaleCourses & study guides मराठी

Chapter 33: Terraform Providers Resources Variables Outputs Locals and Data Sources

33.3 Variables and tfvars — one code, many inputs

Story. An Instagram-style feature team wants the same web server in dev and prod, but with a smaller instance type in dev. Copying the file and editing it creates two codebases that drift apart. Variables let one codebase take inputs. The values come from a .tfvars file, a -var flag or a TF_VAR_ environment variable, and validation blocks reject bad input before AWS is ever called.

variables.tf declare dev.tfvars values plan -var- file var.name in code plan
Declare in variables.tf, give values in a tfvars file, read them as var.name.
# variables.tf
variable "region" {
  type        = string
  default     = "ap-south-1"
  description = "AWS region for the lab"
}

variable "instance_type" {
  type = string
  validation {
    condition     = contains(["t3.micro", "t3.small"], var.instance_type)
    error_message = "Use t3.micro or t3.small in this course."
  }
}

variable "allowed_cidrs" {
  type    = list(string)
  default = []
}

# dev.tfvars
instance_type = "t3.micro"
allowed_cidrs = ["203.0.113.0/24"]

# command
terraform plan -var-file=dev.tfvars

What each line does

  • variable "region" { — Declares an input named region.
  • type = string — Only text values are accepted.
  • default = "ap-south-1" — Used when nobody supplies a value.
  • description = "..." — Shown in docs and prompts so readers know what the input is for.
  • variable "instance_type" { — Declares a required input because it has no default.
  • validation { — Adds a rule that is checked before any API call.
  • condition = contains([...], var.instance_type) — The rule passes only for the two small instance types.
  • error_message = "..." — The message shown when the rule fails.
  • type = list(string) — allowed_cidrs must be a list of text values.
  • instance_type = "t3.micro" — In dev.tfvars, sets the value for this environment.
  • allowed_cidrs = ["203.0.113.0/24"] — The network ranges allowed to reach the server; use your own office range.
  • terraform plan -var-file=dev.tfvars — Loads the dev values for this plan.

When it is green.

var.instance_type = "t3.micro"
Plan: 1 to add, 0 to change, 0 to destroy.

When it is red.

Error: Invalid value for variable
  on variables.tf line 8:
Use t3.micro or t3.small in this course.
This was checked by the validation rule at variables.tf:10,3-13.

Fix. Someone passed a large type such as m5.4xlarge. Correct the tfvars value. Files named terraform.tfvars or *.auto.tfvars load automatically; any other name needs -var-file.

What: every term in plain English

  • variable — An input to your code. Read it as var.NAME.
  • type — string, number, bool, list, map, object. Wrong types fail early.
  • default — The value used if nobody sets one.
  • .tfvars — A file of name = value lines, such as dev.tfvars.
  • terraform.tfvars — Loaded automatically. So is any *.auto.tfvars file.
  • -var-file — Loads a tfvars file you name.
  • TF_VAR_name — An env variable that sets var.name.
  • precedence — The order that decides which value wins.

Example 1: One code, dev and prod

An Instagram-style team runs the same server code in dev with t3.micro and prod with t3.medium.

# variables.tf
variable "env" {
  type = string
}
variable "instance_type" {
  type    = string
  default = "t3.micro"
}
variable "instance_count" {
  type    = number
  default = 1
}

# main.tf (uses the AMI data source from 33.2)
resource "aws_instance" "web" {
  count         = var.instance_count
  ami           = data.aws_ami.al2023.id
  instance_type = var.instance_type
  tags = {
    Name = "${var.env}-web-${count.index}"
  }
}
# dev.tfvars
env = "dev"

# prod.tfvars
env            = "prod"
instance_type  = "t3.medium"
instance_count = 2
terraform plan -var-file=dev.tfvars  | grep -E "instance_type|Plan:"
terraform plan -var-file=prod.tfvars | grep -E "instance_type|Plan:"

Output

      + instance_type = "t3.micro"
Plan: 1 to add, 0 to change, 0 to destroy.
      + instance_type = "t3.medium"
      + instance_type = "t3.medium"
Plan: 2 to add, 0 to change, 0 to destroy.

What it shows: In real projects, dev and prod also use separate state, as in chapter 34.

Example 2: Which value wins

A Flipkart engineer set the type in three places and got a surprise. Let us prove the order.

echo 'instance_type = "t3.small"' > terraform.tfvars
export TF_VAR_instance_type=t3.nano
terraform plan -var-file=dev.tfvars -var instance_type=t3.large | grep instance_type
unset TF_VAR_instance_type
rm terraform.tfvars

Output

+ instance_type = "t3.large"

What it shows: Lowest to highest: default, TF_VAR_, terraform.tfvars, *.auto.tfvars, -var-file and -var in the order given. The last one on the command line wins.

Example 3: Types and validation

A Paytm-style team wanted only Mumbai or Hyderabad regions. A typo like ap-south1 must fail at plan.

variable "region" {
  type    = string
  default = "ap-south-1"
  validation {
    condition     = contains(["ap-south-1", "ap-south-2"], var.region)
    error_message = "Region must be ap-south-1 (Mumbai) or ap-south-2 (Hyderabad)."
  }
}

variable "allowed_cidrs" {
  type    = list(string)
  default = ["10.0.0.0/8"]
  validation {
    condition     = alltrue([for c in var.allowed_cidrs : can(cidrhost(c, 0))])
    error_message = "Every item must be a valid CIDR like 10.0.0.0/16."
  }
}
terraform plan -var region=ap-south1
terraform plan -var 'allowed_cidrs=["10.0.0.0/33"]'

Output

Error: Invalid value for variable
Region must be ap-south-1 (Mumbai) or ap-south-2 (Hyderabad).

Error: Invalid value for variable
Every item must be a valid CIDR like 10.0.0.0/16.

What it shows: The plan stops before calling AWS. The message is yours.

Example 4: Sensitive input without a secret in Git

A database password must never be in a tfvars file in Git. A Razorpay-style team passes it through an env variable from a secret store.

variable "db_password" {
  type      = string
  sensitive = true
}

resource "aws_db_instance" "main" {
  identifier          = "demo-db"
  engine              = "mysql"
  instance_class      = "db.t3.micro"
  allocated_storage   = 20
  username            = "app"
  password            = var.db_password
  skip_final_snapshot = true
}
export TF_VAR_db_password="$(aws secretsmanager get-secret-value --secret-id demo/db --query SecretString --output text)"
terraform plan | grep password

Output

+ password = (sensitive value)

What it shows: The value is hidden in plan, but it is still stored in state. Encrypt state and limit who can read it. Even better: manage_master_user_password = true lets RDS keep the password in Secrets Manager.

Command cheat sheet

Command What it does When to use it
-​var-​file=​dev.​tfvars Load one env file Each environment
-var name=value Set one value Quick tests
export TF_​VAR_​name=​value Set from env CI and secrets
terraform console Try var. values and functions Debugging
sensitive = true Hide in output Passwords, tokens
validation { } Reject bad values Regions, sizes, CIDRs

Verify: did it work?

  1. See a value

    echo "var.instance_type" | terraform console -var-file=prod.tfvars
    

    Output

    "t3.medium"
    
  2. Secret is hidden

    terraform plan | grep password
    

    Output

    + password = (sensitive value)
    
  3. tfvars not leaking secrets

    git grep -n password -- "*.tfvars"
    

    Output

    (no output)
    

When it is red 1

Error: No value for required variable
  on variables.tf line 1: variable "env"
The root module input variable "env" is not set, and has no default value.

Cause. A required variable was not set.

Fix, step by step

  1. Pass -var-file=dev.tfvars, or set TF_VAR_env.

When it is red 2

Error: Invalid value for input variable
  The given value is not suitable for var.instance_count: a number is required.

Cause. Wrong type, such as "two".

Fix, step by step

  1. Use 2 with no quotes.

When it is red 3

Error: Reference to undeclared input variable
  on main.tf line 4: instance_type = var.instance_typ

Cause. Typo in the name.

Fix, step by step

  1. Fix the spelling to match variables.tf.

Cleanup

terraform destroy -var-file=dev.tfvars -auto-approve

Ravindra Bagale's Tip

Never commit a tfvars file with a password. Secrets come from a secret store through TF_VAR_ at run time.

Practice tasks

Easy Two tfvars

Why: One code, two envs.

What to do: Make dev.tfvars and prod.tfvars.

How:
  1. Plan both.

Check: Different instance types.

Medium Prove precedence

Why: Know who wins.

What to do: Set one variable in three places.

How:
  1. Plan and read.

Check: You can say the order.

Medium Validation

Why: Fail early.

What to do: Allow only t3.micro and t3.small.

How:
  1. Pass t3.large.

Check: Your message shows.

Hard Secret input

Why: No secrets in Git.

What to do: Pass a DB password from Secrets Manager.

How:
  1. TF_VAR_db_password.

Check: Plan shows (sensitive value).

Easy Say it in two lines

Why: If you can say it simply, you understand it.

What to do: Write two short lines about this lesson for a fresher.

How:
  1. Write the problem it solves.
  2. Write the one command or file you use.
  3. Read it aloud.

Check: A fresher understands it without a question.

Easy Start a safe lab folder

Why: Practice needs a throwaway place.

What to do: Make a new folder with versions.tf and a random_pet resource, so no AWS cost.

How:
  1. mkdir tf-lab and cd into it.
  2. Add the hashicorp/random provider and one random_pet.
  3. Run terraform init and terraform apply.

Check: Apply complete! with 1 added.

Medium Break it on purpose

Why: You must know the red output by face.

What to do: Cause one error from the red part of this lesson and fix it.

How:
  1. Follow one red section.
  2. Copy the exact message to your notes.
  3. Write the fix in one line.

Check: You fixed it without searching the web.

Medium Run it twice

Why: A second run must be quiet.

What to do: Run the same plan two times with no edits.

How:
  1. Run terraform plan.
  2. Run it again.
  3. Explain any change it still shows.

Check: The second plan says No changes, or you can explain each line.

Medium Write a runbook page

Why: At 2 a.m. nobody remembers flags.

What to do: Write a one-page runbook for this lesson.

How:
  1. Title, when to use, exact commands, how to check, how to undo.

Check: A teammate can follow it alone.

Medium Run it from a pipeline

Why: Real teams run Terraform from CI, not from laptops.

What to do: Run the main command of this lesson inside a GitHub Actions or Jenkins job.

How:
  1. Add a job with hashicorp/setup-terraform or a Terraform Docker image.
  2. Give it AWS access through an IAM role, never a key in the file.
  3. Run init, then the command from this lesson.

Check: The job log shows the same output you saw on your laptop.

Medium Check the cost

Why: Labs left running cost real money.

What to do: Find what this lesson would cost per day if you forgot to destroy it.

How:
  1. Open the AWS Pricing Calculator.
  2. Add each resource from your plan.
  3. Write the daily cost in your notes.

Check: You know the daily cost and you ran terraform destroy.

Hard Draw the flow top to bottom

Why: A drawing shows the order of work.

What to do: Draw this lesson as boxes from top to bottom.

How:
  1. Start at your code, go through plan and state, end at AWS.
  2. Mark where each command reads or writes.

Check: Each box matches a real step you ran.

Hard Explain it like an interview

Why: Interviewers want real stories.

What to do: Record a 60 second answer about this lesson.

How:
  1. Say the problem, the command, and one error you hit.

Check: Your answer has a real command and a real error message.

Homework: try at home

Try at home

Take one of your old .tf files. Find three hard-coded values. Turn them into variables with types, defaults and one validation.

Recap

  • Variables are inputs: var.NAME.
  • tfvars files hold values per environment.
  • -var on the command line wins.
  • Types and validation stop bad input early.
  • Secrets come from env at run time, marked sensitive.

FAQ

Is terraform.tfvars loaded automatically?

Yes, and so is *.auto.tfvars.

Can a variable use another variable in its default?

No. Use a local for that.

Does sensitive encrypt the value?

No. It only hides it in output. State still holds it.

Where do I put defaults?

In variables.tf, only when a safe default exists.

Should prod.tfvars be in Git?

Yes, if it holds no secrets.

Interview questions

  1. How do you use the same code for dev and prod?

    Variables with per-env tfvars files and separate state.

  2. Variable precedence order?

    default, env TF_VAR_, terraform.tfvars, *.auto.tfvars, then -var-file and -var in command order.

  3. How do you pass secrets?

    TF_VAR_ from a secret store at run time, sensitive = true, and encrypted state.

  4. What does validation do?

    Checks the value at plan and fails with your message.

  5. Variable vs local?

    A variable is an input from outside; a local is a named expression inside.

Quick quiz

Q1. How do you read a variable?

var.NAME

Q2. Env var form?

TF_VAR_NAME

Q3. Which wins: tfvars or -var?

-var

Q4. Does sensitive keep it out of state?

No.

Q5. Auto-loaded file?

terraform.tfvars

Samajla ka?

Ravi Sir's Cards for this lesson

मराठी cards