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

Passing List as Template Variables #9368

Closed
yasin-amadmia-mck opened this issue Oct 14, 2016 · 14 comments
Closed

Passing List as Template Variables #9368

yasin-amadmia-mck opened this issue Oct 14, 2016 · 14 comments

Comments

@yasin-amadmia-mck
Copy link

I want to render a template as below (effectively create an AWS Policy)

{
  "Version": "2012-10-17",
  "Statement": [
    {
       ...
       ...
      },
      "Action": "ec2:*",
      "Resource": "xxxx*"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "ec2:*",
      "Resource": "xxx/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "x.x.x.x/24",
            ...
            ...
            "x.x.x.x/16"
          ]
        }
      }
    }
  ]
}

My Template file test.tpl is

{
  "Version": "2012-10-17",
  "Statement": [
    {
       ...
       ...
      },
      "Action": "ec2:*",
      "Resource": "xxxx*"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "ec2:*",
      "Resource": "xxx/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [${whitelist_ips}]
        }
      }
    }
  ]
}

How do I pass ${whitelist_ips} as list to template when I render it ?

I tried the usual way as below but it gives me template parse error

data "template_file" "x" {
  template = "${file("test.tpl")}"

  vars {
    whitelist_ips = [ "${split(",", var.allow_list}" ]
  }
}

My use case is that I don't want to hard-code the IPs within access policy and want to provide a way to pass it as a variable like

variable "allow_list" { default = "x.x.x.x/20,y.y.y/10" }
@llarsson
Copy link

Try pushing the allow_list into your template as-is, and use a combo of split and formatlist (link) in the template to get your results.

I didn't know that the interpolation functions are available even within templates, but they are (I could use one to include another file that way... however, a file included that way is included verbatim, so it can't have interpolation functions in it).

@jen20
Copy link
Contributor

jen20 commented Oct 14, 2016

Hi @geek876! I haven't read this fully but to give a quick drive-by answer that might get you further quicker than there is an answer: you might want to look at the aws_iam_policy_document data source which can be used to produce all kinds of policies without requiring template rendering. Despite the name, it can also be used for S3, SNS and SQS policies (and likely others too).

@yasin-amadmia-mck
Copy link
Author

Thanks @llarsson and @jen20
I tried few things but none of them seem to work. In the end I scrapped the whole template thing and now trying to write the policy in-line but it is failing too. I may have same issue @jen20 if I use aws_iam_policy_document I believe.

Now I have my module as

variable "ip_whitelist" {}
...
...
resource "aws_elasticsearch_domain" "x" {
...
...
  access_policies = <<EOF
{
"Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "xxx"
      },
      "Action": "es:*",
      "Resource": "$xxxx"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Resource": "xxx",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "${var.ip_whitelist}"
        }
      }
    }
  ]
}
EOF

Then I call this module

module "xxx" {
   source = "<path>"
   ...
   ...
  ip_whitelist = [ "${split(",", var.ip_whitelist)}" ]

}

I have

variable "ip_whitelist" { default = "10.0.0.1,10.0.0.2" }

This complained that variable ip_whitelist in module x should be type string, got list`

Then in module, I changed

variable "ip_whitelist" { type = "list" }

But then started to get error
At column 1, line 1: output of an HIL expression must be a string, or a single list (argument 8 is TypeList) in:

@mrwacky42
Copy link
Contributor

Try also jsonencode. eg:

data "template_file" "foo" {
    template = "${file("foo_policy.json.tftemplate")}"
    vars {
        whitelist_ips = "${jsonencode(split(",", var.allow_list))}"    
    }
}

And a template that looks like so...

[SNIP]
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": ${whitelist_ips}
        }
      }
[SNIP]

@yasin-amadmia-mck
Copy link
Author

Thanks @mrwacky42. The jsonencode works well.

@gambol99
Copy link

I'm assuming this is the official stance of terraform ... i.e. map and lists are not going to be supported? ..

@jen20
Copy link
Contributor

jen20 commented Jul 17, 2017

aws_iam_policy_document does not have these issues - you can interpolate variables at will since the policy document is written in HCL.

@gambol99
Copy link

still ... it would be nice though to support more than what the DSL supports ... A primary example being userdata for cloudinit, terraform is a pain when it comes to conditionals and customization

@apparentlymart
Copy link
Contributor

As of right now, template_file is limited by the fact that the Terraform resource schema requires the type of everything to be concretely specified, so we can't have an attribute of type "map of anything".

This is very likely to change eventually, but there's a lot of internal work to do before we can get there to teach Terraform core how to deal with things that are dynamically-typed. For the time being, workarounds (such as those discussed above) are required for handling lists and maps.

The most general workaround is to use the external data source to run an external program to produce the string you need. This data source is subject to the same limitation as template_file, since it all goes through the same core code, but at least with an external program it becomes possible to decode any temporary forms used to "smuggle" lists and maps out to the external program and reproduce the original list or map data to use in the external program's logic.

I'm sorry things aren't smoother in this area. I can only ask for patience as we design and implement the necessary changes to enable this to be improved, which is something we have already started to investigate.

@jen20
Copy link
Contributor

jen20 commented Jul 17, 2017

@kevinchabreck
Copy link

I was able to get this to work with an in-line policy by wrapping my terraform list variable with jsonencode:

variable "whitelist" {
  default = [
    "10.0.0.1/32",
    "10.0.0.2/32"
  ]
}

resource "aws_s3_bucket_policy" "b" {
  bucket = "${aws_s3_bucket.b.id}"
  policy =<<POLICY
{
  "Version": "2012-10-17",
  "Id": "${aws_s3_bucket.b.id}-bucket-access-policy",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::${aws_s3_bucket.b.id}/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": ${jsonencode(var.whitelist)}}
      }
    }
  ]
}
POLICY
}

You can also get extra explicit and define your whitelist as a map where the keys are the IP(s) and the values are their canonical descriptions (this is nice for creating well-named aws_security_group_rule resources).

variable "whitelist" {
  default = {
    "10.0.0.1/32" = "office"
    "10.0.0.2/32" = "home"
  }
}

resource "aws_s3_bucket_policy" "b" {
  bucket = "${aws_s3_bucket.b.id}"
  policy =<<POLICY
{
  "Version": "2012-10-17",
  "Id": "${aws_s3_bucket.b.id}-bucket-access-policy",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::${aws_s3_bucket.b.id}/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": ${jsonencode(keys(var.whitelist))}}
      }
    }
  ]
}
POLICY
}

@osterman
Copy link

osterman commented Oct 2, 2018

In addition to the suggestions above, I recommend to use iam_policy_document to generate the JSON using native HCL types. It provides a json output and you can chain multiple documents together by passing source_json.

@ghost
Copy link

ghost commented Nov 7, 2018

This issue has been automatically migrated to hashicorp/terraform-provider-template#40 because it looks like an issue with that provider. If you believe this is not an issue with the provider, please reply to hashicorp/terraform-provider-template#40.

@ghost
Copy link

ghost commented Mar 31, 2020

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 Mar 31, 2020
This issue was closed.
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

9 participants