2

I am trying to build a cloud infrastructure in AWS using Terraform. I want to add a policy for a S3 bucket which uses attribute based authorization (ABAC) via the templatefile function of terraform. My problem is that the variable syntax used by terraform and AWS is the same (${...}).

Here is the policy template:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowReadRole1",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::${bucketName}/*",
            "Effect": "Allow",
            "Principal": "*",
            "Condition": {
                "s3:ExistingObjectTag/myid": "${aws:PrincipalTag/myid}"
            }
        }
    ]
}

The relevant part of the terrafrom file is:

resource "aws_s3_bucket_policy" "mybuckets-policy" {
  bucket = aws_s3_bucket.mybuckets[count.index].bucket
  policy = templatefile("${path.module}/bucket-policy.json", {
    bucketName = aws_s3_bucket.mybuckets[count.index].bucket
  })
  count = 2
}

So what I want is that the ${bucketName} part of the template gets replaced by terraform, while keeping the AWS expression ${aws:PrincipalTag/user-id} in place. But running terraform on the configuration above causes the error message

Call to function "templatefile" failed: ./bucket-policy.json:14,49-50: Extra characters after interpolation expression; Expected a closing brace to end the interpolation expression, but found extra characters..

If I put another item ${foobar} in my template without a specifiying a value for it the error message is

Invalid value for "vars" parameter: vars map does not contain key "foobar", referenced at ./bucket-policy.json:11,30-36.

How can I make terraform do a partial evaluation of a template file while leaving all other items intact?

mat
  • 1,645
  • 15
  • 36
  • 1
    Have you considered just putting your policy in a HEREDOC instead? It's normally much simpler in these cases. Also your current example has stripped the `aws:PrincipalTag` part so it's not a meaningful example right now (there's nothing AWS specific using the same interpolation syntax). You can also escape $ interpolation from Terraform by doubling $ so use `$${...}` – ydaetskcoR Apr 28 '20 at 11:19
  • I fixed the policy example. – mat Apr 28 '20 at 11:50
  • Did you try escaping the `${aws:PrincipalTag/myid}` with `$${aws:PrincipalTag/myid}`? – ydaetskcoR Apr 28 '20 at 11:55
  • Yes, that works well. Thank you. If you post it as an answer, I will gladly accept it. – mat Apr 28 '20 at 12:07
  • If that was the issue then it's a duplicate. I'll see if I can find the duplicate to close it as a target tot hat for others who stumble across this question and not the other. – ydaetskcoR Apr 28 '20 at 12:22
  • I think https://stackoverflow.com/a/51971726/2291321 is a duplicate. If you agree let me know and I'll mark it as a duplicate. – ydaetskcoR Apr 28 '20 at 12:24
  • I agree @ydaetskcoR – mat Apr 28 '20 at 12:55
  • 2
    @ydaetskcoR next time just change the dupe target like this; https://meta.stackoverflow.com/questions/355666/what-should-i-do-as-a-gold-badge-holder-if-i-feel-the-duplicate-target-isnt-the/355667?r=SearchResults&s=3|0.0000#355667 – Jean-François Fabre Apr 29 '20 at 18:49

1 Answers1

5

In the example above the syntax ${} will cause Terraform to try and evaluate the field as an interpolation function. Since u want to use this value literally and not as an interpolation function this will need to be double-escaped by using two $ symbols.

$${aws:PrincipalTag/user-id}

Abhinaya
  • 949
  • 1
  • 5
  • 12
  • While this solution works fine, I would still prefer one where I could prevent those field from beeing interpreted within terraform, without having to alter the policy file. – mat Apr 28 '20 at 12:53