Hello!

In this programming, I will talk about conditions and lookups.

Conditions

Conditions in terraform are used to select one value depending on another. For example, if it is a dev environment, then use the t3.micro server type, and if it is a production environment, then use m5.large. This is easy to do with a condition. Conditions are written like condition ? true_val : false_val. For server type conditions will look like var.env == "prod" ? "m5.large" : "t3.micro". Here I check whether the parameter env has the value prod, and if so, I use m5.large, in all other cases it is t3.micro. The complete ec2 creation code will look like this.

variable "env" {
  default     = "dev"
  description = "Environment name"
  type        = string
}

resource "aws_instance" "foo" {
  ami                    = data.aws_ami.amzn.id
  instance_type          = var.env == "prod" ? "m5.large" : "t3.micro"
  subnet_id              = "subnet-5f70bf74"
  vpc_security_group_ids = ["sg-b04b8cd4"]
  tags = {
    Env = "Dev"
  }
  volume_tags = {
    "Env" = "Dev"
  }
}

First, I start terraform with the default value and the server type will be t3.micro

$ terraform plan
data.aws_ami.amzn: Reading...
data.aws_ami.amzn: Read complete after 1s [id=ami-05fa00d4c63e32376]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.foo will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-05fa00d4c63e32376"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t3.micro"

Now I run it with value env set to prod

$ terraform plan -var env=prod
data.aws_ami.amzn: Reading...
data.aws_ami.amzn: Read complete after 1s [id=ami-05fa00d4c63e32376]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.foo will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-05fa00d4c63e32376"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "m5.large"

Also, with the help of conditions, I can specify whether to create a resource or not. In the previous example for the dev environment, I used a smaller server size, but in some cases it may be necessary to not create resource at all. To do this, you need to add the count attribute and specify in it - if prod, then create one server, and if not prod, then 0

resource "aws_instance" "foo" {
  count                  = var.env == "prod" ? 1 : 0
  ami                    = data.aws_ami.amzn.id
  instance_type          = "m5.large"
  subnet_id              = "subnet-5f70bf74"
  vpc_security_group_ids = ["sg-b04b8cd4"]
  tags = {
    Env = "Dev"
  }
  volume_tags = {
    "Env" = "Dev"
  }
}
$ terraform plan
data.aws_ami.amzn: Reading...
data.aws_ami.amzn: Read complete after 1s [id=ami-05fa00d4c63e32376]

No changes. Your infrastructure matches the configuration.

And if I run it with the value prod, the server will be created

$ terraform plan -var env=prod
data.aws_ami.amzn: Reading...
data.aws_ami.amzn: Read complete after 1s [id=ami-05fa00d4c63e32376]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.foo[0] will be created
  + resource "aws_instance" "foo" {
      + ami                                  = "ami-05fa00d4c63e32376"

The only thing to pay attention to when the count attribute is used that the resource will no longer be able to be addressed as before, for example aws.instance.foo.private_ip. There will be such an error

terraform plan -var env=prod

 Error: Missing resource instance key

   on outputs.tf line 2, in output "instance_ip_addr":
    2:   value = aws_instance.foo.private_ip

 Because aws_instance.foo has "count" set, its attributes must be accessed on specific instances.

 For example, to correlate with indices of a referring resource, use:
     aws_instance.foo[count.index]

Now, in order to reference, you need to specify the index. Since I am creating only one server, my index will be 0, and the request will look like aws_instance.foo[0].private_ip. If more than one server is created, it will be possible to refer to them by their indexes 1,2,3 and so on.

Lookup

With the conditions, I could specify that if prod, then one type of server, and if not, then another. And what to do if we have several environments such as dev, test, staging, and prod and each of them requires different types of servers. Lookup will help us with this. The Lookup function allows you to get the value from the map depending on the value of the key.

To begin with, you need to create a mapping server type to the environment.

variable "env" {
  default     = "dev"
  description = "Environment name"
  type        = string
}

variable "instance_types" {
  type = map(string)
  default = {
    "prod"    = "m5.xlarge",
    "staging" = "m5.large",
    "test"    = "t3.large",
    "dev"     = "t3.micro"
  }
}

This can now be used in the aws_instance code. The lookup function requires three arguments. The first map itself, the second is the value of the key by which lookup is done, and the third is the default value in case there is no key in the map

resource "aws_instance" "foo" {
  count                  = var.env == "prod" ? 1 : 0
  ami                    = data.aws_ami.amzn.id
  instance_type          = lookup(var.instance_types, var.env, "t3.micro")
  subnet_id              = "subnet-5f70bf74"
  vpc_security_group_ids = ["sg-b04b8cd4"]
  tags = {
    Env = "Dev"
  }
  volume_tags = {
    "Env" = "Dev"
  }
}

And now whatever value of the env variable I would pass the desired server type will be used.

Video