Terraform: Feature request: Allow using lists and maps with conditionals

Created on 6 Mar 2017  ·  47Comments  ·  Source: hashicorp/terraform

I found out today that if you try to use a list or a map with a conditional, you get the error:

* At column 3, line 1: conditional operator cannot be used with list values in:

It turns out this is an explicit check built into the TypeCheck method.

The comment above the code says "for now this is simply prohibited because it doesn't seem to be a common enough case to be worth the complexity." I thought I'd toss out at least one use case where this would be handy:

  • I'm creating a module that creates a number of resources, including an ELB.
  • One of the inputs to the module is a variable called is_internal_elb, which is used to set the internal parameter of the ELB to true for internal ELBs and false for public ELBs.
  • The ELB takes in a subnets parameter, which is a list of subnet IDs to attach to the ELB. When is_internal_elb is set to true, I'd like to set this to a list of private subnet IDs. When is_internal_elb is false, I'd like to set it to a list of public subnet IDs.

Rough pseudo code:

resource "aws_elb" "elb" {
  name = "${var.elb_name}"
  internal = "${var.is_internal_elb}"

  subnets = ["${var.is_internal_elb ? var.private_subnet_ids : var.public_subnet_ids}"]
}

This seems like a fairly straightforward use case, but with the current TypeCheck method, it won't work.

config enhancement

Most helpful comment

BTW, I did find one workaround:

subnets = ["${split(",", var.is_internal_elb ? join(",", var.private_subnet_ids) : join(",", var.public_subnet_ids))}"]

I'm using join to turn the lists into strings to get past the type check and then split to turn them back into lists. A bit silly, but I guess it works...

All 47 comments

BTW, I did find one workaround:

subnets = ["${split(",", var.is_internal_elb ? join(",", var.private_subnet_ids) : join(",", var.public_subnet_ids))}"]

I'm using join to turn the lists into strings to get past the type check and then split to turn them back into lists. A bit silly, but I guess it works...

This is addressed by hashicorp/hil#42.

@apparentlymart Conditional values in lists was not addressed nor resolved in any of the recent releases.

Please re-open this thread so that this can be properly tracked and implemented.

@jsalbright this issue is _already_ open, since as you noticed the referenced PR was not actually merged.

This is one of the things on the docket for a holistic revamp of the configuration language. We're still in the early stages of figuring that out, but do intend to fix this along with many other weird quirks with how the interpolation language deals with lists and maps.

It definitely requires some attention, because currently it takes more time to find some workarounds in terraform than to develop the infrastructure itself.

Until this is solved, here it is more workaround suggestions:

If arrays are of same length:

formatlist(var.cond ? "%[1]s" : "%[2]s", var.a_list, var.b_list)

If arrays may be of different length:

slice(concat(var.a_list, var.b_list),
        var.cond ? 0 : length(var.a_list),
        var.cond ? length(var.a_list) : length(var.a_list) + length(var.b_list))

I have to add that I just ran into the same use case. Please fix!

Same issue here, please fix

Posting here in case someone runs into a similar issue as I did. I had to create a conditional that would set propagating_vgws parameter on a route table only if a VPG was set to be created AND if the route table was enabled for propagation. This involved using list type variables in a conditional, something that apparently is not as straightforward as one would think. Mainly, things would break when no aws_vpn_gateway was present.

After reading about various workaround, this code was required to make this happen:

propagating_vgws = ["${compact(split(",", var.vpc_vpg == "true" && var.subnet_public_route_vpg == "true" ? join("", aws_vpn_gateway.vpg.*.id) : join("", list(""))))}"]

Why?

  • Terraform has no way to pass a value of nil. Using "0" in the above expression sets it to string and that breaks the conditional.
  • Even when an "empty" list was created, it was not truly a null list. So the propagating_vgws had a value that caused the apply to fail (it would try to set it to zero, then nothing).
~ module.dev_vpc.aws_route_table.public.0
    propagating_vgws.#:          "0" => "1"
    propagating_vgws.4108050209: "" => "0"

It seems crazy to me that I had to do this to make a simple conditional work, so if anyone has any suggestions for improvements / simplification, I would love to hear them.

Here is a workaround to use conditionals with maps

variable "bool" {
  default = false
}

variable "a" {
  type = "map"

  default = {
    "k1" = "v1"
    "k2" = "v2"
    "k3" = "v3"
  }
}

variable "b" {
  type = "map"

  default = {
    "k9" = "v9"
    "k8" = "v8"
    "k7" = "v7"
  }
}

resource "null_resource" "test" {
  triggers {
    uuid = "${uuid()}"

    hi = "${
      zipmap(
        split(",",
          element(
            split("=",
              var.bool
                ? format("%s=%s", join(",", keys(var.a)), join(",", values(var.a)))
                : format("%s=%s", join(",", keys(var.b)), join(",", values(var.b)))
            ),
            0
          )
        ),
        split(",",
          element(
            split("=",
              var.bool
                ? format("%s=%s", join(",", keys(var.a)), join(",", values(var.a)))
                : format("%s=%s", join(",", keys(var.b)), join(",", values(var.b)))
            ),
            1
          )
        )
      )
    }"
  }
}

😓

Just faced the same issue with public/private subnets for elb

@modax your first suggestion crashes Terraform for me on 0.10.2

panic: runtime error: index out of range

goroutine 3815 [running]:
github.com/hashicorp/terraform/config.interpolationFuncFormatList.func1(0xc0429f22a0, 0x3, 0x3, 0xc0429f22a0, 0x3, 0x3, 0x100000040)
        /opt/gopath/src/github.com/hashicorp/terraform/config/interpolate_funcs.go:662 +0x6ba
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.(*evalCall).Eval(0xc042516670, 0x35fa5c0, 0xc04240dcd0, 0xc04200c3d0, 0x0, 0x0, 0xc0429c2760, 0xc04200c3d0, 0xc04200c680)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/eval.go:283 +0x24d
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.(*evalVisitor).visit(0xc04200c3c0, 0x35fea80, 0xc042552280, 0x35febc0, 0xc04241fe90)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/eval.go:215 +0x111
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.(*evalVisitor).(github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.visit)-fm(0x35fea80, 0xc042552280, 0x35febc0, 0xc04241fe90)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/eval.go:175 +0x45
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/ast.(*Call).Accept(0xc042552280, 0xc0422d62f0, 0xc0422d62f8, 0xc04200c3c0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/ast/call.go:20 +0xef
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/ast.(*Output).Accept(0xc04200c340, 0xc0422d62f0, 0xc04200c3c0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/ast/output.go:20 +0x69
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.(*evalVisitor).Visit(0xc04200c3c0, 0x35feb80, 0xc04200c340, 0x0, 0x0, 0x0, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/eval.go:175 +0xb3
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.internalEval(0x35feb80, 0xc04200c340, 0xc04249b2e0, 0x0, 0xc0422d6160, 0xc042516610, 0x35feb80, 0xc04200c340)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/eval.go:153 +0x7c5
github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil.Eval(0x35feb80, 0xc04200c340, 0xc04249b2e0, 0x6c, 0x1, 0x1, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/hashicorp/hil/eval.go:52 +0x61
github.com/hashicorp/terraform/config.(*RawConfig).Interpolate.func1(0x35feb80, 0xc04200c340, 0x35feb80, 0xc04200c340, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/config/raw_config.go:132 +0x49
github.com/hashicorp/terraform/config.(*interpolationWalker).Primitive(0xc042012900, 0x1dd7660, 0xc04240de60, 0x194, 0xc042012900, 0xc042323b01)
        /opt/gopath/src/github.com/hashicorp/terraform/config/interpolate_walk.go:147 +0x166
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.walkPrimitive(0x1dd7660, 0xc04240de60, 0x194, 0x211c0a0, 0xc042012900, 0x98, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:240 +0x87
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.walk(0x1dd7660, 0xc04240de60, 0x194, 0x211c0a0, 0xc042012900, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:171 +0x246
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.walkSlice(0x1bd6680, 0xc04249b3c0, 0x97, 0x211c0a0, 0xc042012900, 0x97, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:272 +0x209
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.walk(0x1dd7660, 0xc0422d60e0, 0x94, 0x211c0a0, 0xc042012900, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:177 +0x4ef
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.walkMap(0x1e18ee0, 0xc04241f770, 0x15, 0x211c0a0, 0xc042012900, 0x3d094f4cc00, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:222 +0x2d9
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.walk(0x1e18ee0, 0xc04241f770, 0x15, 0x211c0a0, 0xc042012900, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:174 +0x2ff
github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk.Walk(0x1e18ee0, 0xc04241f770, 0x211c0a0, 0xc042012900, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/vendor/github.com/mitchellh/reflectwalk/reflectwalk.go:84 +0x138
github.com/hashicorp/terraform/config.(*RawConfig).interpolate(0xc042a1c9c0, 0xc04240dce0, 0xc042a1c9f8, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/config/raw_config.go:233 +0xf1
github.com/hashicorp/terraform/config.(*RawConfig).Interpolate(0xc042a1c9c0, 0xc042568c90, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/config/raw_config.go:138 +0xec
github.com/hashicorp/terraform/terraform.(*BuiltinEvalContext).Interpolate(0xc042251880, 0xc042a1c9c0, 0xc04221b0e0, 0x0, 0x0, 0x2e)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/eval_context_builtin.go:304 +0x116
github.com/hashicorp/terraform/terraform.(*EvalInterpolate).Eval(0xc042495920, 0x36064e0, 0xc042251880, 0x2, 0x2, 0x22cc0a8, 0x4)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/eval_interpolate.go:18 +0x52
github.com/hashicorp/terraform/terraform.EvalRaw(0x35f1c40, 0xc042495920, 0x36064e0, 0xc042251880, 0xc04205c050, 0x2, 0xc042568c30, 0x2b)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/eval.go:53 +0x17c
github.com/hashicorp/terraform/terraform.(*EvalSequence).Eval(0xc042495a00, 0x36064e0, 0xc042251880, 0x2, 0x2, 0x22cc0a8, 0x4)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/eval_sequence.go:14 +0xc8
github.com/hashicorp/terraform/terraform.EvalRaw(0x35f1f00, 0xc042495a00, 0x36064e0, 0xc042251880, 0x1dd6040, 0xc042377b0c, 0x1c0b520, 0xc042377be0)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/eval.go:53 +0x17c
github.com/hashicorp/terraform/terraform.Eval(0x35f1f00, 0xc042495a00, 0x36064e0, 0xc042251880, 0xc042495a00, 0x35f1f00, 0xc042495a00, 0xc0424bdc40)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/eval.go:34 +0x54
github.com/hashicorp/terraform/terraform.(*Graph).walk.func1(0x2206cc0, 0xc042058558, 0x0, 0x0)
        /opt/gopath/src/github.com/hashicorp/terraform/terraform/graph.go:126 +0xd54
github.com/hashicorp/terraform/dag.(*Walker).walkVertex(0xc042b15490, 0x2206cc0, 0xc042058558, 0xc04249c480)
        /opt/gopath/src/github.com/hashicorp/terraform/dag/walk.go:387 +0x399
created by github.com/hashicorp/terraform/dag.(*Walker).Update
        /opt/gopath/src/github.com/hashicorp/terraform/dag/walk.go:310 +0x9d1

Would love to see this fixed. Any plans to do so?

This would be super useful

I've been fighting a few issues in this realm, trying to come up with concise, reusable patterns for parameterization and dynamic resource creation across environments. It seems like the core team is acutely aware of the shortcomings and are working to address them. In the meantime, perhaps it makes sense to explore / collaborate on solutions to these problems, where possible, in userspace?

Inspired by this module -- which seemed like a simple, reusable pattern -- I started working on modular, (mostly) pure-terraform solutions to the obstacles I've been hitting. I plagiarized / extended the aforementioned module a hair, then hacked at [nested] map lookups / conditionals a bit over in this module.

I thought I'd share the work, in case it's useful for anyone here (feedback is welcome, too -- this stuff was hacked out in a vacuum on a Sunday). Both patterns seemed relevant to this thread, and could be pretty easily extended to other use cases like checking for the existence of [nested] map keys, nested element support, conditionals that emit maps, nested map lookups for strings / lists, etc.

Is there an existing / open contrib space for these type of utility modules? I googled a bit and came up short, and the registry didn't seem quite appropriate.

Would love to see this fixed. Any plans to do so?

as long as you can manipulate any type to render that type as string you can work with pretty much all types within conditionals. No?

let's assume you have a string you want to add to a lists wrapped in a conditional.
it is possible to "type cast" within the split, wrapped in a join, to convert your string to a list before adding it to another list before doing a join. makes sense?

something like this:

cidr_blocks = ["${var.secure_cidr}","${var.vpc_cidr}","${var.cr_lan_ip}","${split(",", var.mgmt_cidr == var.vpc_cidr ? join(",", var.cr_internal_servers) : join(",", concat(var.cr_internal_servers, list(var.mgmt_cidr))))}"]

Terraform is a great product and they have no obligation to implement our feature requests. But let’s not pretend using string manipulation is an appropriate substitute for proper types. Refusing to use a shoe to hammer a nail does not make me “lazy.”

I'm facing the same issue - I cannot conditionally assign tags to EC2 instances based on index count because neither lists nor maps are currently supported in conditions.

locals {
  empty_list            = []
  tag_Custom_Tag = ["Master", "True"]
}

...

  tags = "${merge(map(
    "Name", "...",
    ...
    "${count.index == 0 ? "${local.tag_Custom_Tag}" : "${local.empty_list}" }",
    ), var.extra_tags)}"

getting

At column 8, line 5: conditional operator cannot be used with list values in:

or

At column 11, line 5: conditional operator cannot be used with map values in:

It would be nice to have support for this rather than using quite hacky workarounds which decrease the readability of code.

Having same issue here. Does somebody know a workaround with non-string lists until this is fixed?

I'd love this feature as well. One use case I have is I've got some IAM groups and policies and their names include an appended variable for environment ("dev" or "prod"). This way, and using terraform workspaces, I could deploy the same group and its attached policies but each with a different name, and have my test users attached to the dev ones in order to test out new IAM permissions before I deploy to prod.

I wish the following code would work:
```
resource "aws_iam_group" "basic_allows_group" {
name = "basic_allows__${var.env}"
path = "/"
}

resource "aws_iam_group_membership" "basic_allows_group_membership" {
name = "basic_allows_group_membership"

# tf variable interpolation with conditional ternary
users = ["${var.env == "prod" ? var.basic_allows_users_prod : var.dev-test-users}"]

group = "${aws_iam_group.basic_allows_group.name}"
}```

As this was blocking me from moving forward on a project, I just knocked up a quick and dirty conditional provider, inspired by the module linked by @davidquarles. The module was insufficient for me, as I need count iterators, which are not supported for modules (#953).

The provider is not at all thoroughly tested, and as such I've not tagged a release or provided any binaries yet, but feel free to build and try it out. From some brief testing, it's doing what I need it to for now.

Workaround for 'conditional tags' (rather conditional map, where an empty map() is returned or a full map. This was tested on terraform 0.11.2; thanks to @nanoz for the inspiration and I hope this saves people some time.

locals {
  o = "" #"hack"
  # add a value to 'o' for the map to populate
}

output  { value = "${   zipmap(
                    compact(split(",",  local.o == "" ? "" :  "key,value,propagate_at_launch" )),
                    compact(split(",",  local.o == "" ? "" :  "thakey,${local.o},true" ))
         ) }" }

We're encountering an issue with this as well, with a similar use case as the OP described.

Rather than cluttering up our module(s) with dozens of additional extraneous inputs, or building permutations of the module, we'd like to just use conditionals with lists to try and keep the modules lean.

In our case, we're trying to configure explicit egress rules for network traffic to maintain compliance with GDPR. If the appliance is in the EU, use the EU CIDR blocks, otherwise, use US CIDR blocks, etc.

Right now the proposed work-around seems to function as expected:
cidr_blocks = ["${split(",", var.appliance_region == "eu" ? join(",", var.eu_cidrs) : join(",", var.us_cidrs))}"]

I'm open to better ways to handle this, but it seems like conditionals with lists are a reasonable approach.

@nanoz I get the following error on your approach:
Error: null_resource.test: triggers (hi): '' expected type 'string', got unconvertible type 'map[string]interface {}'
Did I miss sth? -> no answer needed anymore :)

Update:

  • based on his approach I got my case for conditionally merging 3 maps working.
  • It can also be extended to n maps with n different boolean.
  • Note: the "${join("-", keys( is only for the null_resource because it can only handle strings. If you pass in a map you get the error described above in my question. You can ignore the leading - in the output though like -k1-k2-k3-k8-k9
  • Terraform does not know the bool type hence the condition need to be made on strings, that's why all booleans are in quotes, as well as the comparisons.
  • Tested with Terraform v0.11.3

main.tf

variable "bool1" {
  default = "true"
}

variable "bool2" {
  default = "false"
}

variable "a" {
  type = "map"

  default = {
    "k1" = "v1"
    "k2" = "v2"
    "k3" = "v3"
  }
}

variable "b" {
  type = "map"

  default = {
    "k7" = "v7"
    "k6" = "v6"
    "k5" = "v5"
  }
}

variable "c" {
  type = "map"

  default = {
    "k8" = "v8"
    "k9" = "v9"
  }
}

resource "null_resource" "test" {
  triggers {
    uuid = "${uuid()}"

    hi = "${join("-", keys(
      merge(
        var.a, 
        zipmap(
          concat(
            keys(var.a),
            split(",", var.bool1 == "false" ? "" : join(",", keys(var.b)))
          ),
          concat(
            values(var.a),
            split(",", var.bool1 == "false" ? "" : join(",", values(var.b)))
          )
        ),
        zipmap(
          concat(
            keys(var.a),
            split(",", var.bool2 == "false" ? "" : join(",", keys(var.c)))
          ),
          concat(
            values(var.a),
            split(",", var.bool2 == "false" ? "" : join(",", values(var.c)))
          )
        )
      )
    ))}"
  }
}

to see the merge, run

$ terraform init
$ terraform plan

I hope this will some of you folks help to save some time 😃

Another workaround, usable for situation when you need either list_a or list_a + list_b

locals {
  all  = true
  list = "${list("1", "2", "3")}"
}

output "test" {
  # returns full list if all==true, otherwise returns only first element of list
  value = "${slice(local.list, 0, local.all ? length(local.list) : 1 )}"
}

Another workaround that worked for me:

locals {
  bucket_website_config = {
    enabled = [{
      index_document = "${var.index_document}"
    }]
    disabled = "${list()}"
  }
}
resource "aws_s3_bucket" "bucket" {
  ...
  website = "${local.bucket_website_config["${var.website == "true" ? "enabled" : "disabled"}"]}"
}

thank you @llibicpep! your approach was the easiest workaround for me to follow!

It definitely requires some attention, because currently it takes more time to find some workarounds in terraform than to develop the infrastructure itself.

Exactly this! Hope TF 0.12 and HCL2 get released soon.

When I saw this, it made me think it was safe to invest my time working on a module that makes use of lists within conditionals. Am I wrong?

The condition can be any valid interpolation syntax, such as variable access, a function call, or even another conditional. The true and false value can also be any valid interpolation syntax. The returned types by the true and false side must be the same

Conditionals

Hi everyone, just in case anyone hasn't yet heard, HCL2 and Terraform 0.12 have been announced with a preview available https://www.hashicorp.com/blog/terraform-0-1-2-preview

@cam-fulcrum Where do you see a preview available?

@gaui In absence of an officially packaged preview you could probably make your own - see instructions, I'm guessing would need to be from the v0.12-dev branch but you might need to confirm that.

Since I smell some confusion here: that article is _itself_ the preview. In other words, it's a preview of _the announcement text_, not of the running code itself. There will be pre-releases of this once it is feature-complete, the first of which I expect will be called an "alpha", since we'll be building it mainly for our own testing and testing of module- and provider-authoring partners so we can get some early warning of any misses in the automatic upgrade tool and other compatibility problems.

As @tdmalone mentioned, there is development going on in v0.12-dev which you are welcome to build yourself if you like, but I will warn that at the time of writing it is not feature-complete and has a number of known missing features. You should expect it to fail to apply any non-trivial configuration, not least because most of the stable provider releases are not updated to support it yet. (We are using local builds of the providers for development, too.)

There is, however, enough implemented that we can see the situation relating to _this_ issue:

$ terraform console
> true ? ["a"] : ["b"]
[
  "a",
]

Judging from the history this is going to come out soon.

I just wanted to add that I stumbled into a use case when defining security group rules and network ACLs

Noting that conditionals will work just fine with lists and maps in 0.12 and we blogged a preview of it: https://www.hashicorp.com/blog/terraform-0-12-conditional-operator-improvements

Another workaround to use in Terraform 0.11.*:

I needed to add a key conditionally to an existing map

  t1 = {
    Name        = "${local.label}"
    Client      = "${local.client}"
  }

  t2 = "${merge(
    local.t1,
    zipmap(
      compact(list(var.ignore-environment ? "" : "Environment")),
      compact(list(var.ignore-environment ? "" : local.environment))
    )
  )}"

@llibicpep 's workaround seems to be working.
It would be better to have native support for lists in a contitional. Please bring that feature to terraform, a great product to manage your cloud infrastructure.

Hi all!

In v0.12.0-alpha1 I've verified that the root problem here is now fixed:

variable "whether" {
  default = false
}

output "example" {
  value = var.whether ? list("a") : list("b", "c")
}
$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

example = [
  "b",
  "c",
]

There is a remaining problem #19180 which required me to use the old-style list(...) constructor function here rather than the intuitive [ ... ] syntax, but that is a problem at a lower level than the conditional operator and so the conditional operator will work with both syntaxes once that issue is addressed.

Since the remaining problem is covered by that other issue, I'm going to close this one now. Thanks for sharing the use-cases here, and thanks for the patience while we got the groundwork in place to generalize the conditional operator.

another workaround (also silly) would be jsonencode() / jsondecode()
edit: jk jsondecode() is not a thing 😞

None of the workarounds are working for me. Can anyone suggest?

resource "aws_alb" "default" {
  name     = "${var.name}-${var.env_name}-${var.internal == "false" ? "public" : "private" }"
  internal = "${var.internal == "false" ? "false" : "true" }"

  #security_groups = ["${var.internal == "false" ? var.sg80 : var.instance_in_all }"]
  #security_groups = ["${split(",", var.internal == "false" ? join(",", var.sg80) : join(",", var.instance_in_all))}"]
  #subnets = ["${split(",", var.internal == "false" ? join(",", var.public_subnets) : join(",", var.private_subnets))}"]
  security_groups = ["${compact(split(",", var.internal == "false" ? join("", var.sg80) : join("", var.instance_in_all)))}"]
}

Error:

* module.public-alb.aws_alb.default: 1 error(s) occurred:

* module.public-alb.aws_alb.default: At column 48, line 1: join: argument 1 should be type list, got type string in:

${compact(split(",", var.internal == "false" ? join("", var.sg80) : join("", var.instance_in_all)))}
* module.private-alb.aws_alb.default: 1 error(s) occurred:

* module.private-alb.aws_alb.default: At column 48, line 1: join: argument 1 should be type list, got type string in:

${compact(split(",", var.internal == "false" ? join("", var.sg80) : join("", var.instance_in_all)))}

@ethicalmohit Solution would be a map of maps or map of lists, I wrote a short article how to do that: https://medium.com/@business_99069/terraform-conditional-assignment-of-a-map-to-attribute-9bb8471771cb

Here is a workaround to use conditionals with maps

variable "bool" {
  default = false
}

variable "a" {
  type = "map"

  default = {
    "k1" = "v1"
    "k2" = "v2"
    "k3" = "v3"
  }
}

variable "b" {
  type = "map"

  default = {
    "k9" = "v9"
    "k8" = "v8"
    "k7" = "v7"
  }
}

resource "null_resource" "test" {
  triggers {
    uuid = "${uuid()}"

    hi = "${
      zipmap(
        split(",",
          element(
            split("=",
              var.bool
                ? format("%s=%s", join(",", keys(var.a)), join(",", values(var.a)))
                : format("%s=%s", join(",", keys(var.b)), join(",", values(var.b)))
            ),
            0
          )
        ),
        split(",",
          element(
            split("=",
              var.bool
                ? format("%s=%s", join(",", keys(var.a)), join(",", values(var.a)))
                : format("%s=%s", join(",", keys(var.b)), join(",", values(var.b)))
            ),
            1
          )
        )
      )
    }"
  }
}

😓

what is format("%s=%s" doing here? and why do you join them both twice? curious how this works

This allows me to separate all the keys (element 0) from the values (element 1)

I found a workaround to extend @brikis98 's solution to maps. It uses zipmap to create a map from lists. This is how i defer the problem to lists.

  single_list1 = ["sse_algorithm"]
  single_list2 = ["AES256"]

  multi_list1 = ["sse_algorithm", "kms_master_key_id"]
  multi_list2 = ["aws:kms", "${var.key_arn}"]

  option1 = "${split(",", var.isMulti ? join(",", local.multi_list1) : join(",", local.single_list1))}"
  option2 = "${split(",", var.isMulti ? join(",", local.multi_list2) : join(",", local.single_list2))}"

  encryptionParams = "${zipmap(local.option1, local.option2)}"

I know this is closed, but I had issues finding examples of it.
For those of you that arrive here from a search engine, this is how I accomplished filtering a map that had a key with an empty string:

  tags_filtered = {
    for key,value in local.tags:
    key => value
    if value != ""
  }

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shanmugakarna picture shanmugakarna  ·  3Comments

franklinwise picture franklinwise  ·  3Comments

rjinski picture rjinski  ·  3Comments

ketzacoatl picture ketzacoatl  ·  3Comments

zeninfinity picture zeninfinity  ·  3Comments