hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
42.5k stars 9.52k forks source link

Allow multiple conditions for count in terraform ternary operations #22131

Closed ghost closed 1 year ago

ghost commented 5 years ago

### Terraform Version Terraform v0.11.7

provider.aws v1.42.0 provider.external v1.2.0

Terraform Configuration Files My configuration for my resource is as below :

count = “${var.size[terraform.workspace] == "big" ? && local.ips[terraform.workspace] != “<none>“} : 10
elseif
${var.size[terraform.workspace] == "small"  && local.ips[terraform.workspace] != “<none>” } :  20
else 0 

Expected Behaviour Terraform should allow me to set multiple conditions in ternary operations

Actual Behaviour Terraform does not support multiple ternary conditions.

teamterraform commented 5 years ago

Hi @vforums! Thanks for sharing this use-case.

It looks like you're writing this as a complex chain of if statements against different maps because of limitations of Terraform 0.11, and sadly the featureset of Terraform 0.11 is now fixed and will not change even if there is a future 0.11.x release for maintenance reasons. For that reason, we'll focus this comment on approaches for Terraform 0.12, which is where any possible language improvements would be happening right now.

A direct way to write what you wanted here would be to use a nested conditional as the "else if" condition:

count = (
  var.size[terraform.workspace] == "big" && local.ips[terraform.workspace] != "<none>" ? 10 : (
    var.size[terraform.workspace] == "small" && local.ips[terraform.workspace] != "<none>" ? 20 : 0
  )
)

Not particularly readable, though. A more "Terraform 0.12"-like way to represent this would be to flip these settings around so that it's a single map with the workspace as the key:

variable "workspace_settings" {
  type = map(object({
    size = string
    ips  = set(string)
  }))
}

Using Terraform 0.12 features, we'd probably prefer to break this down into multiple simpler expressions, giving each a name (as a named local value) that can help the reader understand what it's representing. Since your comment didn't fully describe the use-case we're limited in how meaningful these example names can be, but for example:

locals {
  settings = var.workspace_settings[terraform.workspace]

  # The original example didn't include what this was a count for, so we're just calling
  # it "something" here. Better to replace "something" with whatever object type we're
  # producing a count for here.
  workspace_size_something_counts = {
    small = 10
    big   = 20
  }
  something_count = lookup(workspace_size_something_counts, local.settings.size, 0)
}

resource "something" "example" {
  count = length(local.settings.ips) == 0 ? local.something_count : 0

  # ...
}

Here we use a map to represent the relationship between the "sizes" and the counts, and then encode the special case that there must be no IPs as a simpler conditional directly inside the count expression. That could also be written out as one complex expression by inlining most of the locals like this:

locals {
  settings = var.workspace_settings[terraform.workspace]
}

resource "something" "example" {
  count = length(local.settings.ips) == 0 ? lookup({
    small = 10
    big   = 20
  }, local.settings.size, 0) : 0

  # ...
}

We generally want to encourage writing and combining multiple simple expressions, rather than writing compound expressions with many branches. However, we can see that in your particular example the problem is framed in a way that is difficult to decompose into smaller parts, in spite of the examples above.

We'll use this issue to collect other real-world examples where multiple different independent settings all affect the outcome in arbitrary ways, and thus where a simple map lookup is insufficient. If others share examples here, we'd prefer to see a fuller description of the scenario in question: what are you trying to achieve in the broader sense, rather than what specific Terraform configuration are you trying to write? That will help us see if your situation might be solved in a different way by other potential features or existing features.

vitdolphin commented 3 years ago

i have an example where i need a nested statements to define which one should go into a config block :

resource "cloudflare_access_application" "allow_exceptions" {
  for_each         = var.public_protection_endpoints_app
  zone_id          = var.zoneid
  name             = each.key
  domain           = "${each.value[1]}${var.global_zonename}${each.value[2]}"
  session_duration = each.value[0]
  allowed_idps     = each.value[3] == "google" ? [var.google_idp_name] : [] //[var.google_idp_name,var.azure_idp_name]  //[var.google_idp_name,var.azure_idp_name]
  cors_headers {
    allow_all_origins = true
    allow_all_headers = true
    allow_all_methods = true
  }
} 

in this case, i have multiple IDPs but in the variable map, i specify which one should be allowed for a particular application. currently i cant find a way to properly do this within the constrains of tf 1.0.3 any help would be appreicated

atheiman commented 1 year ago

I think this works now and can be closed?

crw commented 1 year ago

Correct. Thanks for the nudge!

github-actions[bot] commented 1 year ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.