The new Terraform 0.12 conditional operator does not seem to work correctly with lists yet.
terraform -v
Terraform v0.12.0-alpha1
+ provider.aws v1.40.0-5-g4f24bc8b7
I've provided the entire configuration, but the part that matters is the output at the end.
provider "aws" {
region = "us-west-2"
}
variable "vpc_name" {
description = "name of the VPC"
default = "tf-0.12-example"
}
resource "aws_vpc" "my_vpc" {
cidr_block = "172.16.0.0/16"
tags = {
Name = var.vpc_name
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "172.16.10.0/24"
availability_zone = "us-west-2a"
tags = {
Name = "tf-0.12-example"
}
}
resource "aws_network_interface" "foo" {
subnet_id = aws_subnet.my_subnet.id
private_ips = ["172.16.10.100"]
tags = {
Name = "tf-0.12-primary_network_interface"
}
}
resource "aws_instance" "foo" {
ami = "ami-22b9a343" # us-west-2
instance_type = "t2.micro"
tags = {
Name = "tf-0.12-ec2-instance"
}
network_interface {
network_interface_id = aws_network_interface.foo.id
device_index = 0
}
}
output "private_dns" {
value = aws_instance.foo.private_dns
}
output "addresses" {
value = (aws_instance.foo.public_dns != "" ? [aws_instance.foo.private_dns, aws_instance.foo.public_dns] : [aws_instance.foo.private_dns])
}
Error: Inconsistent conditional result types
on /Users/roger/Documents/HashiCorp/GitHub/rberlind/terraform-0.12-examples/aws-instance-with-network/main.tf line 53, in output "addresses":
53: value = (aws_instance.foo.public_dns != "" ? [aws_instance.foo.private_dns, aws_instance.foo.public_dns] : [aws_instance.foo.private_dns])
The true and false result expressions must have consistent types. The given
expressions are tuple and tuple, respectively.
None
I believe what I gave in the output should work in Terraform 0.12 since the conditional operator is now supposed to support lists. Both the true and false expressions are lists of attributes, one having 2 of them and the other having just one.
Terraform gave an error.
terraform initterraform applyHi Roger!
This looks like the same root cause as #19141, but for the moment I will leave both open since they look different from a user perspective so we will want to retest both situations before considering this resolved.
Thanks again!
Thanks @apparentlymart. Is there any workaround for my situation? I don't see how I would declare the two lists in the conditional operator as being of type list(string).
Hi @rberlind,
For this particular case the automatic detection is the only possibility and so the workaround I proposed for variable defaults would not apply, as you've seen.
Although I've not been able to verify this workaround, I _think_ it may work to construct the two lists using a list(...) function call, which'll give the language a hint that a list is needed here and force that conversion to happen.
(Please note that the list(vals...) function is not the same as the list(string) type declaration in variable blocks; the list(vals...) function is actually a legacy thing needed to construct lists in interpolation expressions in older releases which we've retained for compatibility, but hopefully it can also serve a temporary duty as a workaround for this other bug.)
Thanks. I will test.
Trying this with alpha-2 gives the same error as alpha-1
I then tried this variant:
value = (aws_instance.ubuntu.public_dns != "" ? list(aws_instance.ubuntu.private_dns, aws_instance.ubuntu.public_dns) : list(aws_instance.ubuntu.private_dns))
but got error:
on /Users/roger/Documents/HashiCorp/GitHub/rberlind/terraform-0.12-examples/for-expressions/main.tf line 45, in output "addresses":
45: value = (aws_instance.ubuntu.public_dns != "" ? list(aws_instance.ubuntu.private_dns, aws_instance.ubuntu.public_dns) : list(aws_instance.ubuntu.private_dns))
|----------------
| aws_instance.ubuntu is tuple with 3 elements
This value does not have any attributes.
Hi @rberlind,
Thanks for checking! Indeed this issue hasn't been fixed yet so it being still present in alpha2 is expected.
For the new error you saw here, it seems like your configuration is now a little different than the example you originally shared but I assume you've got count = 3 on that aws_instance.ubuntu, which means that aws_instance.ubuntu is now the sequence of three instances rather than a single instance object as before. With count set you'll now need to use the splat syntax to access the attributes across all of the different instances. Your existing example returns both the private and public addresses so I suppose a way to do that with the new multiple instances would be something like this:
output "addresses" {
value = {for inst in aws_instance.ubuntu: inst.id => {
private = inst.private_dns,
public = inst.public_dns,
}}
}
That would produce a value like the following:
{
"i-abc123": {
"public": "blahblah.amazonaws.com",
"private": "blahblah.amazonaws.com",
},
"i-def456": {
"public": "blahblah.amazonaws.com",
"private": "blahblah.amazonaws.com",
},
"i-ghi789": {
"public": "blahblah.amazonaws.com",
"private": "blahblah.amazonaws.com",
},
}
... though of course at that point you'd no longer be using a conditional list result, and so that wouldn't be verifying this issue anymore. :grinning:
Actually, I don't have count at all, @apparentlymart. You seem to be thinking about the generalized splat operator. All I'm trying to use here is the conditional operator in the final addresses output with lists, following https://www.hashicorp.com/blog/terraform-0-12-conditional-operator-improvements
Sorry, @apparentlymart : I do have count=3 on the aws_instance, and that was causing issues since I was trying to process the aws_instance as if only 1 existed.
I was able to get this to work with the following:
output "addresses" {
value = (aws_instance.ubuntu[0].public_dns != "" ? list(aws_instance.ubuntu[0].private_dns, aws_instance.ubuntu[0].public_dns) : list(aws_instance.ubuntu[0].private_dns))
}
Unfortunately, output "addresses" {
value = (aws_instance.ubuntu[0].public_dns != "" ? [aws_instance.ubuntu[0].private_dns, aws_instance.ubuntu[0].public_dns] : [aws_instance.ubuntu[0].private_dns])
} still says: "The true and false result expressions must have consistent types. The given
expressions are tuple and tuple, respectively."
The following conditional operator also works: output "conditional" {
value = (var.aws_region == "us-east-1" ? list("1", "2") : list("1", "2", "3"))
}
I decided to generate the list of all private and public addresses for all 3 aws_instances, but decided to make it more interesting by using a conditional on the associate_public_ip_address attribute so that only one of the 3 instances has a public IP. However, while doing this, I discovered that associate_public_ip_address is ignored when provisioning into a default VPC and that all EC2 instances get a public DNS address. So, I changed my example to use provision into a non-standard subnet and used IPs instead or DNS addresses.
In my aws_instance with count = 3, I now include associate_public_ip_address = ( count.index == 1 ? true : false) so that only the second of the three instances has a public IP. I then defined an ips output like this:
output "ips" {
value = [
for instance in aws_instance.ubuntu:
(instance.public_ip != "" ? list(instance.private_ip, instance.public_ip) : list(instance.private_ip))
]
}
And this is the output:
ips = [
[
"172.16.10.218",
],
[
"172.16.10.199",
"34.201.169.46",
],
[
"172.16.10.250",
],
]
So, the situation with regard to the conditional operator using lists is better than I originally thought in that things work if I construct the lists with the list() function. But it would be nice if we could get this to work with the [...] notation.
The upstream library changes in #19690 include a fix for this.
In terraform console in v0.12.0-alpha4 the bug was still present:
> true ? ["a"] : []
Error: Inconsistent conditional result types
on <console-input> line 1:
(source code not available)
The true and false result expressions must have consistent types. The given
expressions are tuple and tuple, respectively.
With the code from #19690 it works as expected:
> true ? ["a"] : []
[
"a",
]
This is still a problem for me using the latest master code as of March 6.
Let's say I have some locals like this.
locals {
foo = {
bar1=[],
bar2=[]
}
foo2 = {
bar1=[],
bar2={}
}
}
Then this works as expected
terraform console
> true ? {} : local.foo
{}
But this does not work
> true ? {} : local.foo2
>
Error: Inconsistent conditional result types
on <console-input> line 1:
(source code not available)
|----------------
| local.foo2 is object with 2 attributes
The true and false result expressions must have consistent types. The given
expressions are object and object, respectively.
Looks like the problems happens whenever bar1 and bar2 are either {} and [] but works whenever they are the same. This is a major problem for me.
I found that referencing lists in Terraform 0.12 beta 1 works as in this example:
output "ips_with_list_in_brackets" {
value = [
for instance in aws_instance.ubuntu:
(instance.public_ip != "" ? [instance.private_ip, instance.public_ip] : [instance.private_ip])
]
}
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.
Most helpful comment
This is still a problem for me using the latest master code as of March 6.
Let's say I have some locals like this.
Then this works as expected
But this does not work
Looks like the problems happens whenever bar1 and bar2 are either {} and [] but works whenever they are the same. This is a major problem for me.