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}"
}
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.
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.
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.
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?
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)
}
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 ....
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With