Creating resources in a loop with dynamic values

I need to set up a set of GCE instances for example 5 of them, and I need to do them in a blue/green deployment way. So have we have 5 nodes as “blue” that are active and also potential 5 nodes as “green”. Now I’d like to either have 1 of them deployed or both at the same time running in “dual” mode.

I’d also like this to be a module that can be reused for multiple set of deployments.

What I’ve done so far is the following:

variables.tf:

variable "runner_managers" {
  type = map(object({
    zone            = string
    service_account = string // If empty use default_service_account_email
  }))
}

variable "deployment" {
  type = object({
    blue  = bool
    green = bool
  })
  description = "The deployments that are active."

  default = {
    blue  = true
    green = false
  }
}

main.tf:

resource "google_compute_instance_template" "runners_manager" {
....
}

######################
# Deployment - blue #
######################
resource "google_compute_instance_from_template" "runners_manager_blue" {
  for_each = var.deployment.blue ? var.runner_managers : {}

  name = "${var.runner_type}-runners-manager-${each.key}-blue"
  zone = each.value.zone

  labels = merge(google_compute_instance_template.runners_manager.labels, {
    runner_manager_name = "${var.runner_type}-runners-manager-${each.key}"
    deployment          = "blue"
  })

  source_instance_template = google_compute_instance_template.runners_manager.id
}

######################
# Deployment - Green #
######################
resource "google_compute_instance_from_template" "runners_manager_green" {
  for_each = var.deployment.green ? var.runner_managers : {}

  name = "${var.runner_type}-runners-manager-${each.key}-green"
  zone = each.value.zone

  labels = merge(google_compute_instance_template.runners_manager.labels, {
    runner_manager_name = "${var.runner_type}-runners-manager-${each.key}"
    deployment          = "green"
  })

  source_instance_template = google_compute_instance_template.runners_manager.id

  metadata = merge(google_compute_instance_template.runners_manager.metadata, {
    "CHEF_NODE_NAME" = "${var.runner_type}-runners-manager-${each.key}-blue"
  })
}

As you can see there is a lot of duplication here between runners_manager_blue and runners_manager_green, and I’m not sure how I can simplify this so that we end up creating X amount of instances depending on var.runner_managers but also have both blue and green at the same time.

Is duplicating the resources the only possible way here? I’ve tried looking into dynamic blocks but it doesn’t like it fit my needs since I don’t want blocks I want resources :thinking:

You could do it with a single resource with the for_each block by creating a new local variable containing entries for both the blue & green instances (depending on what is activated), with the blue/green type being included within that variable as needed.

@stuart-c interesting idea! I’ll give this a go and report back.

Thank you so much!

I ended up with the following solution, which is exactly what I wanted, thank you so much @stuart-c !

locals  {
  instances = merge(
    {
      for id, info in var.runner_managers : "${var.runner_type}-runners-manager-${id}-blue" => info
      if var.deployment.blue
    },
    {
      for id, info in var.runner_managers : "${var.runner_type}-runners-manager-${id}-green" => info
      if var.deployment.green
    }
  )
}

resource "google_compute_instance_from_template" "runners_manager" {
  for_each = local.runners_manager.instances

  name = each.key
  zone = each.value.zone
}

Other resources I used to help me figure this out: