How to combine 2 blocks into the same module, when only one of the blocks is allowed at a time

I am working with a resource ( google_monitoring_uptime_check_config) which only allows one of these blocks to be present.
Either http_check or tcp_check.

I tried setting them both up as a dynamic block, but the terraform plan sees them both as being there, and gives this error.

Error: "tcp_check": only one of http_check,tcp_check can be specified, but http_check,tcp_check were specified.

If you leave both out, you get this error.

Error: "tcp_check": one of http_check,tcp_check must be specified

How can I build one module, which will complete only the block I need?

Can you copy/paste the ‘dynamic block’ attempt you made?

Hi @kpfleming, sorry I don’t have that code any longer. When it did not work I moved on.
I do have another idea which I read about today. It it works I will post it here.

I found a clue in another post on this site. The answer is to define 2 resources in the same file, and use a boolean and a count as a conditional to build one or the other resource.

I will include the code here so hopefully it will help someone else that comes across this issue.

main .tf file

locals {
  monitored_resource_labels = {
    uptime_url    = {
      project_id  = var.project_id
      host        = var.host
    }
    gce_instance  = {
      project_id  = var.project_id
      instance_id = var.instance_id
      zone        = var.zone
    }
    gae_app       = {
      project_id  = var.project_id
      module_id   = var.module_id
      version_id  = var.version_id
      zone        = var.zone
    }
  }
}

# This resource builds a http based uptime check
resource "google_monitoring_uptime_check_config" "uptime_check_https" {
  count = var.is_http_check ? 1 : 0

  display_name     = var.display_name
  timeout          = var.timeout
  period           = var.period
  selected_regions = var.selected_regions

  http_check {
    path         = var.path
    port         = var.port
    use_ssl      = var.use_ssl
    validate_ssl = var.validate_ssl
    headers      = var.headers
    mask_headers = var.mask_headers
  }

  monitored_resource {
    type   = var.type
    labels = local.monitored_resource_labels[var.type]
  }

  content_matchers {
    content = var.content
  }
}

# This resource builds a tcp based uptime check
resource "google_monitoring_uptime_check_config" "uptime_check_tcp" {
  count = var.is_tcp_check ? 1 : 0

  display_name     = var.display_name
  timeout          = var.timeout
  period           = var.period
  selected_regions = var.selected_regions

  tcp_check {
    port         = var.tcp_port
  }

  monitored_resource {
    type   = var.type
    labels = local.monitored_resource_labels[var.type]
  }

  content_matchers {
    content = var.content
  }
}

Extract from vars.tf file

# This module handles both HTTP(S) and TCP uptime checks
# ONE and ONLY ONE of these two boolean variables MUST be set to true.
variable "is_http_check" {
  type        = bool
  default     = false
  description = "(Required) Is this uptime check a HTTP(S) type request"
}

variable "is_tcp_check" {
  type        = bool
  default     = false
  description = "(Required) Is this uptime check a TCP type request"
}
1 Like

Hi @mwohlin,

What you originally tried with dynamic blocks should be possible as long as the for_each expression in the dynamic blocks is derived only from values that can be known during the plan phase. That is, either constant values or references to constant values elsewhere, and to any resource result attributes that the provider is able to predict during planning.

A problem arises whenever the for_each expression is derived from something that isn’t predictable during planning. These are values that would be marked with the placeholder (known after apply) in the rendered plan. If Terraform can’t know the exact length of the collection given in for_each then it will pessimistically assume that the length is at least one and perform validation under that assumption.

Since you weren’t able to share the original configuration you tried I can’t offer any advice specific to it, but hopefully this information is useful for if you encounter a similar situation in future.

(It’s also possible for there to be provider-specific validation bugs that might affect the behavior I’m describing here in some cases, but I don’t think that’s true in this case: the error text you reported looks like the standard “conflicting arguments” reporting that’s part of the shared validation code in the Terraform SDK, common to all providers.)

2 Likes

@apparentlymart Thank you for the insight and all of your help with this. I suspected something as such was going on. That is very useful information which I will remember.

I am happy with the current configuration and I feel it is easy for users to understand.