Iteration over non-iterable value; A value of type string cannot be used as the │ collection in a 'for' expression

Hi I have below template.tpl file which I’m trying to enforce S3 policy using aws_s3_bucket_policy resource

%{ for bucketname in bucketnames ~}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EnforceSSL",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::${bucketname}/*",
        "arn:aws:s3:::${bucketname}"
      ],
      "Principal": "*",
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}
%{ endfor ~}

Terraform code is as below

variable "bucket_names" {
  type        = list
  description = "list of bucket names to enforce the policies"
  default     = ["test-1223kjjdnfks23", "test-1223kjjdnfks234"]
}

data "aws_s3_bucket" "s3_bucket" {

  count  = length(var.bucket_names)
  bucket = var.bucket_names[count.index]
}

resource "aws_s3_bucket_policy" "s3_bucket_common_policy_enforce" {
  count    = length(var.bucket_names)
  bucket = data.aws_s3_bucket.s3_bucket[count.index].bucket
  policy = templatefile("${path.module}/template.tpl", {
    bucketnames = var.bucket_names[count.index]
  })
}

Generates error Iteration over non-iterable value; A value of type string cannot be used as the │ collection in a 'for' expression..

Please help me :pray:

Hi @nkrishnakishor,

I’m afraid I’m a little confused about what your goal is here, and so I’m not sure what to suggest.

The template you have written seems to be designed to generate a result consisting of multiple bucket policies concatenated together into a single string, with one JSON document after another. But that isn’t a valid value for the S3 bucket policy argument: it expects just one JSON object describing a single policy for the bucket.

The error is arising here because you’ve assigned a single bucket name to the variable bucketnames inside the template, but the template expects to receive a collection of bucket names.

If you intend to produce only one policy document per bucket, then what you need to pass into the template is just a single bucket name, and then remove the for directive from the template, since there’s no need for repetition in there.


Separately, I suggest that you read and consider the advice in Generating JSON or YAML from a template. Although it’s not causing the problem you are currently observing, generating JSON by string concatenation risks generating invalid JSON syntax, whereas if you use Terraform’s jsonencode function as the entire template then the result is guaranteed to be valid JSON syntax, regardless of what characters appear in the bucket name.

The error you’re encountering, “Iteration over non-iterable value; A value of type string cannot be used as the collection in a ‘for’ expression,” is occurring because the templatefile function expects bucketnames to be a list (or another iterable collection), but you’re passing a single string from var.bucket_names[count.index] . When the template tries to iterate over bucketnames , it fails because it’s attempting to iterate over a string, not a list.

To resolve this issue, you need to modify how you pass bucketnames to the templatefile function within your aws_s3_bucket_policy resource. Instead of passing a single string, you should pass a list containing just that single string. This will make bucketnames iterable, allowing the for loop in your template to execute correctly.