Luke
Luke

Reputation: 623

Terraform - Use data source output in variable default

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}"
}

Upvotes: 8

Views: 21302

Answers (3)

Facundo Olano
Facundo Olano

Reputation: 2609

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)
}

Upvotes: 7

dkniffin
dkniffin

Reputation: 1383

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 ....
}

Upvotes: 2

KJH
KJH

Reputation: 2430

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?

Upvotes: 7

Related Questions