Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Allow using lists and maps with conditionals #12453

Closed
brikis98 opened this issue Mar 5, 2017 · 47 comments
Closed

Feature request: Allow using lists and maps with conditionals #12453

brikis98 opened this issue Mar 5, 2017 · 47 comments

Comments

@brikis98
Copy link
Contributor

brikis98 commented Mar 5, 2017

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.

@brikis98
Copy link
Contributor Author

brikis98 commented Mar 5, 2017

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...

@apparentlymart
Copy link
Contributor

This is addressed by hashicorp/hil#42.

@jsalbright
Copy link

@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.

@apparentlymart
Copy link
Contributor

@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.

@igor-pinchuk
Copy link

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

@modax
Copy link
Contributor

modax commented Jun 28, 2017

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))

@JackKora
Copy link

JackKora commented Jul 6, 2017

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

@Sytten
Copy link

Sytten commented Jul 13, 2017

Same issue here, please fix

@geekifier
Copy link

geekifier commented Aug 1, 2017

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.

@nanoz
Copy link

nanoz commented Sep 5, 2017

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
          )
        )
      )
    }"
  }
}

😓

bzub added a commit to cloudnativelabs/terraform-packet-kubernetes that referenced this issue Oct 9, 2017
For use in modules that use a lot of conditionals + list variables.
This module is a little more convenient than performing the workarounds
needed to use conditionals with list values.

See: hashicorp/terraform#12453
@non7top
Copy link

non7top commented Oct 17, 2017

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

@Niksko
Copy link

Niksko commented Oct 25, 2017

@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

@ahailes
Copy link

ahailes commented Oct 31, 2017

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

@tejasmanohar
Copy link

This would be super useful

@davidquarles
Copy link

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.

@hermes-pimentel
Copy link

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

@aaomoware
Copy link

aaomoware commented Nov 24, 2017

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))))}"]

@steveh
Copy link
Contributor

steveh commented Nov 24, 2017

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.”

@Constantin07
Copy link

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.

@rarguelloF
Copy link

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

@davidgibney
Copy link

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}"
}```

@pdf
Copy link

pdf commented Jan 23, 2018

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.

@olenm
Copy link

olenm commented Feb 3, 2018

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" ))
         ) }" }

@Ray-B
Copy link

Ray-B commented Feb 6, 2018

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.

@gaui
Copy link

gaui commented Jun 21, 2018

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.

charly-vega added a commit to beta-uy/terraform-aws-influxdb that referenced this issue Jul 2, 2018
…able due to conditionals not

being supported for list values
(hashicorp/terraform#12453)
@sundeer
Copy link

sundeer commented Jul 3, 2018

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

@ghost
Copy link

ghost commented Jul 3, 2018

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

@gaui
Copy link

gaui commented Jul 3, 2018

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

@tdmalone
Copy link

tdmalone commented Jul 4, 2018

@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.

@apparentlymart
Copy link
Contributor

apparentlymart commented Jul 4, 2018

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",
]

@tinyzimmer
Copy link

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

@mitchellh
Copy link
Contributor

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

@mrcrch
Copy link

mrcrch commented Aug 21, 2018

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))
    )
  )}"

@Halderian
Copy link

@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.

@apparentlymart
Copy link
Contributor

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.

@atheiman
Copy link

atheiman commented Nov 1, 2018

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

@ethicalmohit
Copy link

ethicalmohit commented Dec 2, 2018

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)))}

@jaceq
Copy link

jaceq commented Dec 11, 2018

@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

@red8888
Copy link

red8888 commented Jan 14, 2019

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

@nanoz
Copy link

nanoz commented Feb 14, 2019

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

afda16 added a commit to alphagov/govuk-aws that referenced this issue May 24, 2019
Update the original `lb_listener_rules` module, that hasn't been used so far,
to support a more specific case, where we want to add a target group and listener
rule for that target based on host headers. Each target group has its own health
check, so we can ensure traffic is only routing when the application is up and
healthy.

We are testing this module with the backend LB. We can choose when to use the
application healthchecks and target groups with the `enable_lb_app_healthchecks`
variable. By default the LBs continue using the default target group. When
we set `enable_lb_app_healthchecks` to true, we are creating the LB forward rules
from the service cnames variable.

This expression:
```
rules_host = ["${compact(split(",", var.enable_lb_app_healthchecks ? join(",", var.backend_public_service_cnames) : ""))}"]
```

is required because we can't use a conditional with list variables:
hashicorp/terraform#12453

This is fixed in Terraform 0.12, until then we need to implement a workaround.
@rahuljain1311
Copy link

rahuljain1311 commented May 30, 2019

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)}"

@sethdmoore
Copy link

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 != ""
  }

@ghost
Copy link

ghost commented Jul 24, 2019

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.

@ghost ghost locked and limited conversation to collaborators Jul 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests