terraform: create list based on resource count - terraform

We have a bunch of instances (I know... cattle, not pets, but in this case, these are really pets)
resource "aws_instance" "read_00" {
count = "${var.read_00_count}"
resource "aws_instance" "read_01" {
count = "${var.read_01_count}"
And we have an ELB where we want to dynamically add the instances based on their count variable, like so:
resource "aws_elb" "read_slaves" {
instances = ["${aws_instance.read_.*.id}"]
But that doesn't work, of course.
Is it possible to dynamically create a list of instance ids ONLY if their count is not zero?
I know this goes against the grain, but if this is possible, that would be awesome.

With Terraform 0.12 that will be much easier, but for now something like this would do:
[...]
resource "aws_instance" "read_01" {
[...]
count = "${var.read_01_count}"
tags {
Role = "read_slave"
}
}
data "aws_instances" "read-slaves" {
instance_tags {
Role = "read_slave"
}
// optional filters
}
resource "aws_elb" "read_slaves" {
instances = ["${data.aws_instances.read-slaves.ids}"]
listener {
...
}
}
Thus:
tagging each instance which acts as a read slave
collect the list of aws_intances
create the aws_elb based on the collected data

Related

Create multiple aws_cloudformation_stack based on parametrized name with Terraform

Is it possible to create multiple CloutFormation stacks with one aws_cloudformation_stack resource definition in terraform, based on parametrized name ?
I have the following resources defined and I would like to have a stack per app_name, app_env build_name combo:
resource "aws_s3_bucket_object" "sam_deploy_object" {
bucket = var.sam_bucket
key = "${var.app_env}/${var.build_name}/sam_template_${timestamp()}.yaml"
source = "../.aws-sam/sam_template_output.yaml"
etag = filemd5("../.aws-sam/sam_template_output.yaml")
}
resource "aws_cloudformation_stack" "subscriptions_sam_stack" {
name = "${var.app_name}---${var.app_env}--${var.build_name}"
capabilities = ["CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
template_url = "https://${var.sam_bucket}.s3-${data.aws_region.current.name}.amazonaws.com/${aws_s3_bucket_object.sam_deploy_object.id}"
}
When I run terraform apply when build_name name changes, the old stack gets deleted and a new one created, however I would like to keep the old stack and create a new one
One way would be to define your variable build_name as a list. Then, when you create new build, you just append them to the list, and create stacks with the help of for_each to iterate over the build names.
For example, if you have the following:
variable "app_name" {
default = "test1"
}
variable "app_env" {
default = "test2"
}
variable "build_name" {
default = ["test3"]
}
resource "aws_cloudformation_stack" "subscriptions_sam_stack" {
for_each = toset(var.build_name)
name = "${var.app_name}---${var.app_env}--${each.value}"
capabilities = ["CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
template_url = "https://${var.sam_bucket}.s3-${data.aws_region.current.name}.amazonaws.com/${aws_s3_bucket_object.sam_deploy_object.id}"
}
Then if you want second build for the stack, you just extend variable "build_name":
variable "build_name" {
default = ["test3", "new_build"]
}

Terraform - create list based on resource count query returned no results

I'm trying to create dynamically create a list of instance ids, to assign it to the load balancer. Based on this question I found I can do it using data "aws_instances". Unfortunately I'm getting this error:
Error: Your query returned no results. Please change your search criteria and try again.
This is the way I create my EC2 resources
resource "aws_instance" "one" {
instance_type = "${var.lc_instance_type}"
ami = "${var.dev_ami}"
count = "${var.instance_count}"
tags = {
Name = "${var.name_prefix}-id"
}
}
And this is the way I'm using the data aws_instance and how I planned to use it in the EB resource
data "aws_instances" "read-ec2" {
instance_tags= {
Name = "${var.name_prefix}-id"
}
}
resource "aws_elb" "loadbalancer" {
instances = ["${data.aws_instances.read-ec2.ids}"]
listener {
...
}
}
Not sure if I'm using the instance_tags option properly.

In terraform, is it possible to only add an optional resource argument if a particular variable is set

On my concrete example:
I want to create a rancher environment resource with preconfigured members. But the number of members is supposed to be depending on a variable list. I'd imaging something like
resource "rancher_environment" "renv" {
name = "renv"
project_template_id = "atmplid"
member {
count = "${length(var.memberlist)}"
external_id = "${var.memberlist[count.index]}"
external_id_type = "exttype"
role = "owner"
}
}
This obviously doesn't work. Is there a trick to achieve this behaviour?
You can use null_resource for this. Try this
resource "null_resource" "memberlist" {
count = "${length(var.memberlist)}"
triggers {
external_id = "${var.memberlist[count.index]}"
external_id_type = "exttype"
role = "owner"
}
}
resource "rancher_environment" "renv" {
name = "renv"
project_template_id = "atmplid"
member = ["${null_resource.memberlist.*.triggers}"]
}
At long last, Terraform has just released v0.12.0-alpha1, which contains a more elegant way of solving this exact problem.

Using count in nested block

Is there a way in Terraform to use the count param within a nested block? I don't want to create multiple instances of a resource, I want to generate a dynamic number of nested blocks with a resource. As an example:
variable "envNames" {
type = "list"
}
variable "envValues" {
type = "list"
}
resource "test_resource" "example" {
# If length(var.envNames) == 5, I would want 5 env blocks
env {
count = "${length(var.envNames)}"
name = "${element(var.envNames, count.index)}"
value = "${element(var.envValues, count.index)}"
}
}
It looks like in terraform v0.12 I would be able to use the dynamic keyword on the block along with the foreach declaration and a map variable, but is there a way to do this in v0.11?
If it helps, this is for a Kubernetes Deployment resource.

How can I get the Instance ID of the EMR master instance in Terraform?

The following code gives me a list of all EC2 instances that are part of my cluster:
data "aws_instances" "emrMaster" {
instance_tags {
Name = "emr-cluster-name"
}
}
But when I try to narrow the list using the AWS generated tag for the master instance, I get the same list.
data "aws_instances" "emrMaster" {
instance_tags {
Name = "emr-cluster-name"
"aws:elasticmapreduce:instance-group-role" = "MASTER"
}
}
If I remove the quotes on the key name, I get a illegal character error due to the colons in the key name.
data "aws_instances" "emrMaster" {
instance_tags {
Name = "emr-cluster-name"
aws:elasticmapreduce:instance-group-role = "MASTER"
}
}
Is there a better way to do this, am I doing something wrong, or have I uncovered a bug in Terraform?
I am using Terraform v0.11.7
I am looking to capture this value so I can build specific cloudwatch alerts for the master instance that are different from the Core instances.
For this purpose better use aws_instance (not aws_instances)
resource "aws_emr_cluster" "emr-cluster" {
....
}
data "aws_instance" "master" {
filter {
name = "tag:Name"
values = ["${aws_emr_cluster.emr-cluster.name}"]
}
filter {
name = "tag:aws:elasticmapreduce:instance-group-role"
values = ["MASTER"]
}
}
And then just use output:
output "master_id" {
value = "${data.aws_instance.master.id}"
}

Resources