Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform - Use data source output in variable default

Tags:

terraform

I am creating a module to spin up a basic web server.

I am trying to get it so that if the user does not specify an AMI then the ubuntu image for that region is used.

I have a data block to get the AMI ID of the ubuntu 16.04 image for that region but I cannot set this as the default for a variable as interpolation does not work.

My module is as follows:-

main.tf

resource "aws_instance" "web" {
  ami = "${var.aws_ami}"
  instance_type = "${var.instance_type}"
  security_groups = ["${aws_security_groups.web.id}"]

  tags {
      Name = "WEB_SERVER"
  }
}

resource "aws_security_groups" "web" {
  name = "WEB_SERVER-HTTP-HTTPS-SG"

  ingress {
      from_port = "${var.http_port}"
      to_port = "${var.http_port}"
      protocol = "tcp"
      cidr_blocks = ["0.0.0.0/0"] 
  }

  ingress {
      from_port = "${var.https_port}"
      to_port = "${var.https_port}"
      protocol = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
      from_port = 0
      to_port = 0
      protocol = "-1"
      cidr_blocks = ["0.0.0.0/0"]
  }
}

variables.tf

variable "instance_type" {
  description = "The instance size to deploy. Defaults to t2.micro"
  default = "t2.micro"
}

variable "http_port" {
  description = "The port to use for HTTP traffic. Defaults to 80"
  default = "80"
}

variable "https_port" {
  description = "The port to use for HTTPS traffic. Defaults to 443"
  default = "443"
}



data "aws_ami" "ubuntu" {
    filter {
        name = "state"
        values = ["available"]
    }

    filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"]
  }

    filter {
        name   = "virtualization-type"
        values = ["hvm"]
    }

    owners = ["099720109477"]

}

locals {
  default_ami = "${data.aws_ami.ubuntu.id}"
}

variable aws_ami {
    description = "The AMI used to launch the instance. Defaults to Ubuntu 16.04"
    default = "${local.default_ami}"
}
like image 462
Luke Avatar asked Aug 22 '18 12:08

Luke


People also ask

How do you use Terraform output as a variable?

Using a variable A variable's value can be accessed from within the terraform module block by using var. <variable_name> . Below we have an example demonstrating this. The variable's value can only be accessed in an expression within the modules where it was declared.

How do you pass output value in Terraform?

Terraform Output Command To get the raw value without quotes, use the -raw flag. To get the JSON-formatted output, we can use the -json flag. This is quite useful when we want to pass the outputs to other tools for automation since JSON is way easier to handle programmatically.

What is the difference between a variable and data source in Terraform?

Data sources provide dynamic information about entities that are not managed by the current Terraform and configuration. Variables provide static information. Referencing a resource defined in a data source won't create the resource itself, and your plan will fail if you reference nonexistent data or infrastructure.


3 Answers

Try using a ternary operator interpolation:

variable "user_specified_ami" {
  default = "ami-12345678"
}

resource "aws_instance" "web" {
  ami = "${var.user_specified_ami == "" ? data.aws_ami.ubuntu.id : var.user_specified_ami}"
  # ... other params omitted ....
}

Set user_specified_ami's default to something to use that AMI. Set it to blank to use the AMI ID Terraform gets from the AWS provider.

i.e. if user_specified_ami is anything other blank (""), then it will be chosen for the AMI, else the AMI Terraform gets the one from AWS.

BTW, maybe you want to use the most_recent = true param in the data "aws_ami" resource?

like image 68
KJH Avatar answered Oct 09 '22 06:10

KJH


A similar solution to the other answers, but using the coalesce function:

variable "user_specified_ami" {
  default = ""
}

resource "aws_instance" "web" {
  ami = coalesce(var.user_specified_ami, data.aws_ami.ubuntu.id)
}
like image 4
Facundo Olano Avatar answered Oct 09 '22 04:10

Facundo Olano


KJH's answer works great, but it felt a bit messy to me to have that logic inline, so I made an abstraction using null_data_source. Here's what that would look like:

variable "ami" {
  default = ""
}

data "null_data_source" "data_variables" {
  inputs = {
    ami = "${var.ami == "" ? data.aws_ami.ubuntu.id : var.ami}"
  }
}

resource "aws_instance" "web" {
  ami = "${data.null_data_source.data_variables.outputs["ami"]}"
  # ... other params omitted ....
}
like image 1
dkniffin Avatar answered Oct 09 '22 06:10

dkniffin