For_each and count conditionally with aws_subnet_ids

Hi all.

I’m trying to get Terraform to conditionally deploy 4 instances in different AZs in AWS.

resource "aws_instance" "slave02" {
  for_each                    = var.pg_slave02_db_ebs_snapshot_id != "" && terraform.workspace == "prod" ? var.db_subnets : {}
  ami                         = data.aws_ami.ubuntu.id
  key_name                    = aws_key_pair.default_ssh_key[0].key_name
  instance_type               = var.pg_slave02_instance_type
  vpc_security_group_ids      = [aws_security_group.database_sg[0].id]
  subnet_id                   = each.value

var.sb_subnets corresponds to:

# data.tf
data "aws_subnet_ids" "database" {
  vpc_id = data.terraform_remote_state.network.outputs.vpc_id

  filter {
    name   = "tag:Name"
    values = ["*-db-*"]
  }

  filter {
    name   = "tag:Environment"
    values = [local.workspace["environment"]]
  }

  filter {
    name   = "tag:Terraform"
    values = ["true"]
  }
}

# main.tf
db_subnets = data.aws_subnet_ids.database.ids

The error that I get is: var.db_subnets is set of string with 3 elements. The true and false result expressions must have consistent types. The given expressions are set of string and object, respectively..

Any suggestions are much appreciated.

Instead of {} use this in your ternary:

var.db_subnets : toset([])
1 Like

Thanks! And how do I reference that resource?

For example:

resource "aws_cloudwatch_metric_alarm" "cw_health_slave02" {
  count               = var.pg_slave02_db_ebs_snapshot_id != "" && terraform.workspace == "prod" ? 1 : 0
  alarm_name          = "pgsql-slave02-${terraform.workspace}-StatusCheckFailed"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "1"
  metric_name         = "StatusCheckFailed"
  namespace           = "AWS/EC2"
  period              = "60"
  statistic           = "Average"
  threshold           = "1"
  alarm_description   = "This metric monitors ec2 health status"
  alarm_actions       = [var.slack_topic_arn]
  ok_actions          = [var.slack_topic_arn]

  dimensions = {
    InstanceId = one(aws_instance.slave02[*].id)
  }
}

Using InstanceId = [for s in aws_instance.slave02 : s.id] does not work:

 Error: Incorrect attribute value type
│ 
│   on modules/database/cloudwatch.tf line 53, in resource "aws_cloudwatch_metric_alarm" "cw_health_slave02":
│   53:   dimensions = {
│   54:     InstanceId = [for s in aws_instance.slave02 : s.id]
│   55:   }
│     ├────────────────
│     │ aws_instance.slave02 is object with 3 attributes
│ 
│ Inappropriate value for attribute "dimensions": element "InstanceId": string required.

Using InstanceId = [for s in aws_instance.slave02 : s.id][0]

Interesting! It works, thanks for your help!

Glad to help. Btw. Its the same syntax used in python or javascript to access the entry of a list (python) or an array (javascript) using the position, starting at 0.

Yep… thanks!

Unfortunately the part where I deploy each instance in a different AZ is not working.

subnet_id                   = each.value
availability_zone           = "ap-southeast-1a"

Error is:

 Value (ap-southeast-1a) for parameter availabilityZone is invalid. Subnet 'subnet-08f561963ceb6467f' is in the availability zone ap-southeast-1b

Do you have any suggestions there?

I’ve opened a new issue for this last question: Aws_instance with specific AZ - #2 by lpossamai