3

I have a module that contains the following code.

resource "aws_s3_bucket" "main" {
  bucket = var.bucket_name
  acl    = "private"
  tags   = var.tags

  versioning {
    enabled = var.versioning_enabled
  }
}

resource "aws_s3_bucket_policy" "mod" {
  depends_on = [aws_s3_bucket.main]
  count  = length(var.bucket_policy) > 0 ? 1 : 0
  bucket = aws_s3_bucket.main.id
  policy = var.bucket_policy
}

variable "bucket_policy" {
  default = ""
}

I call the module using the code below, which i've redacted for security.

module "xxxx-api-s3-firehose" {
  source      = "git::ssh://git@github.com/xxxx/infra-terraform-modules-s3?ref=v1.0.0"
  bucket_name = "reporting-xxxxxx-api-${var.env_suffix}-${var.region}"

  bucket_policy = <<EOF
{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Sid": "xxx Bucket Permissions",
         "Effect": "Allow",
         "Principal": {
            "AWS": "${aws_iam_role.xxxxx-api-firehose-role.arn}"
         },
         "Action": [
            "s3:Get*",
            "s3:List*",
            "s3:Put*"
         ],
         "Resource": [
            "arn:aws:s3:::${module.xxxx-api-s3-firehose.bucket_id}",
            "arn:aws:s3:::${module.xxxxx-api-s3-firehose.bucket_id}/*"
         ]
      },
      {
         "Sid": "xx Bucket Permissions",
         "Effect": "Allow",
         "Principal": {
            "AWS": "arn:aws:iam::${var.account_id}:role/${var.xxxxx}"
         },
         "Action": [
            "s3:Get*",
            "s3:List*",
            "s3:Put*"
         ],
         "Resource": [
            "arn:aws:s3:::${module.xxx-api-s3-firehose.bucket_id}",
            "arn:aws:s3:::${module.xxx-api-s3-firehose.bucket_id}/*"
         ]
      }
   ]
}
EOF

I receive the below error after running terraform apply.

 Error: Invalid count argument
│ 
│   on xxxxx-backend-dev.xxxx-api-s3-firehose/main.tf line 39, in resource "aws_s3_bucket_policy" "mod":
│   39:   count  = length(var.bucket_policy) > 0 ? 1 : 0
│ 
│ The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.
╵

I receive the error in multiple versions of terraform including the latest 1.0.6.

I'm not sure what the issue is. Can someone advise?

Marcin
  • 215,873
  • 14
  • 235
  • 294
gurpsone
  • 463
  • 7
  • 17

2 Answers2

1

The key problem here is that you're trying to use the length of the string as part of the count condition, but the string template includes values that won't be known until the apply step and so the length of the string can't be known until the apply step either.

To make this work, you need to find some way to separate the indication of whether to declare the object from the exact policy string it'll use.

One way to achieve that is to wrap the specific string value inside an object that can be wholly null to represent not declaring a policy at all. For example:

variable "bucket_policy" {
  type = object({
    json = string
  })
  default = null
}

resource "aws_s3_bucket_policy" "mod" {
  count = var.bucket_policy != null ? 1 : 0

  bucket = aws_s3_bucket.main.id
  policy = var.bucket_policy.json
}

When calling the module:

  bucket_policy = {
    json = jsonencode({
      # (the policy document content, as before)
    })
  }

With this structure, var.bucket_policy can be known even if var.bucket_policy.json isn't known, and thus we've separated the decision about whether to declare the policy at all from the specific policy document value.

If you set bucket_policy to null (whether explicitly or just by omitting its definition inside the module block) then the policy resource will have count = 0. If you set it to a non-null object as I showed above then the policy resource will have count = 1 and the policy document will be passed in as an argument.

Martin Atkins
  • 62,420
  • 8
  • 120
  • 138
  • I still get the same error using the `count = var.xyz !=null ? 1 : 0` method. using tf 0.14 Are there any other ways to base a count based on a null value or not? – AG6HQ Jul 06 '22 at 15:00
0

As the error msg suggest, you can't do this. Your bucket_policy length is not constant. You should be able to overcome this using lists:

variable "bucket_policy" {
  default = []
}

resource "aws_s3_bucket_policy" "mod" {
  depends_on = [aws_s3_bucket.main]
  count  = length(var.bucket_policy) > 0 ? 1 : 0
  bucket = aws_s3_bucket.main.id
  policy = var.bucket_policy[0]
}

and

module "xxxx-api-s3-firehose" {
  source      = "git::ssh://git@github.com/xxxx/infra-terraform-modules-s3?ref=v1.0.0"
  bucket_name = "reporting-xxxxxx-api-${var.env_suffix}-${var.region}"

  bucket_policy = [<<EOF
{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Sid": "xxx Bucket Permissions",
         "Effect": "Allow",
         "Principal": {
            "AWS": "${aws_iam_role.xxxxx-api-firehose-role.arn}"
         },
         "Action": [
            "s3:Get*",
            "s3:List*",
            "s3:Put*"
         ],
         "Resource": [
            "arn:aws:s3:::${module.xxxx-api-s3-firehose.bucket_id}",
            "arn:aws:s3:::${module.xxxxx-api-s3-firehose.bucket_id}/*"
         ]
      },
      {
         "Sid": "xx Bucket Permissions",
         "Effect": "Allow",
         "Principal": {
            "AWS": "arn:aws:iam::${var.account_id}:role/${var.xxxxx}"
         },
         "Action": [
            "s3:Get*",
            "s3:List*",
            "s3:Put*"
         ],
         "Resource": [
            "arn:aws:s3:::${module.xxx-api-s3-firehose.bucket_id}",
            "arn:aws:s3:::${module.xxx-api-s3-firehose.bucket_id}/*"
         ]
      }
   ]
}
EOF
]
Marcin
  • 215,873
  • 14
  • 235
  • 294