Unable to render json policy with list of resources

Hi,

New to the forum, thanks first of all.

I am struggling to render a template with templatefile by passing a list of resources as variable.

Given the below json template:

{
"Version": "2012-10-17",
"Statement": [
    {
        "Action": [
            "s3:ListBucket*",
            "s3:GetObject*",
            "s3:PutObject*"
        ],
        "Resource": "${bucket_arns}",
        "Condition": {
            "Bool": {
                "aws:MultiFactorAuthPresent": [
                    "true"
                ]
            }
        }
    }
]

}

And having the following policy document:

data "aws_iam_policy_document" "analyst_s3_list_get_put_to_archive" {
source_json =  templatefile("${path.module}/rendering/generic/s3_list_get_put_to_bucket.json", {
  bucket_arns = [ 
      "arn:aws:s3:::${var.bucket_prefix}-${var.environment}-archive",
      "arn:aws:s3:::${var.bucket_prefix}-${var.environment}-archive/*"
  ]
})
}

The rendering fails as follows in the plan phase:

Call to function "templatefile" failed:
../../modules/policy_definitions/rendering/generic/s3_list_get_put_to_bucket.json:10,28-39:
Invalid template interpolation value; Cannot include the given value in a
string template: string required..

What am I missing and how could I make this to work?

Thanks!

Alberto.

The surface problem here is that you are passing an HCL collection value into your template and rendering it directly into what looks like a JSON string value.

However, the AWS provider supports defining your policy statements using Terraform code, which is then rendered as JSON for you. For example, I think your policy document could be written as (untested):

data "aws_iam_policy_document" "analyst_s3_list_get_put_to_archive" {
  statement {
    actions = [
      "s3:ListBucket*",
      "s3:GetObject*",
      "s3:PutObject*"
    ]

    resources = [
      "arn:aws:s3:::${var.bucket_prefix}-${var.environment}-archive",
      "arn:aws:s3:::${var.bucket_prefix}-${var.environment}-archive/*"
    ]

    condition {
      test = "Bool"
      variable = "aws:MultiFactorAuthPresent"
      values = [ true ]
    }
  }
}

Would that work for your use case?

1 Like

Thanks for your reply @alisdair!!

That definitely works and was my previous iteration, however I’m trying to achieve reusability of common s3 json policies without the need to repeat statements blocks in aws_iam_policy_document. For example, to list and to get from a bucket, depending on a list of buckets, I can give one role access to some buckets while giving another role access to a different bucket without filling the code with aws_iam_policy_document (which is fine, they’re human readable)

In that case, you might want to use the override functionality, pointing to a generated policy’s source JSON and adding rules to override. See this example from the provider docs.

If you’ve already evaluated that idea and rejected it, then I think the solution to your original problem is likely to call jsonencode(…) on your bucket_arns collection value when you pass it to templatefile, then remove the " quotes in your template.

1 Like

Thanks again @alisdair!

Removing the quotes from the json template did the trick. Thank you very much!

Alberto.