AWS S3 Bucket Module Help

With the release of AWS provider version 5.x, most attributes have been deprecated and new resources have been created regarding s3 buckets.

I have a module where I create an aws_s3_bucket resource and can create multiple aws-s3_bucket_lifecycle_configuration resources.

The issue I’m having is that for the lifecycle configuration itself, you can have multiple scenarios regarding the filter:

  • empty filter without any prefix or tags
  • a filter with a prefix, but no tags
  • a filter with a tag, but no prefix
  • multiple tags, no prefix
  • prefix with multiple tags
  • prefix with one tag

However, I’m having trouble with being able to handle all these different scenarios in my module because based on the above mentioned scenario, there could be a need for additional configuration blocks and based on which scenario you run into, you could have the filter wrapped in different configuration blocks.

Any guidance would be helpful in how to handle this new resource.

EXAMPLE CODE:

main.tf

resource "aws_s3_bucket" "bucket" {
  count  = !var.ignore-lifecycle-rule-changes ? 1 : 0
  bucket = var.bucket-name
  tags   = merge({ "Name" = var.bucket-name }, var.bucket-tags)
}

resource "aws_s3_bucket_lifecycle_configuration" "bucket_lifecycle_config" {
  bucket   = !var.ignore-lifecycle-rule-changes ? aws_s3_bucket.bucket[0].id : aws_s3_bucket.bucket-ignored-lifecycle[0].id
  for_each = { for rule in var.lifecycle-rules : rule.id => rule }

  rule {
    id     = each.value.id
    status = each.value.status
    filter {
      and {
        prefix = each.value.prefix
        tags   = each.value.tags
      }
    }
    abort_incomplete_multipart_upload {
      days_after_initiation = each.value.abort_incomplete_multipart_upload_days
    }

    dynamic "expiration" {
      for_each = each.value.expiration != null ? [each.value.expiration] : []
      iterator = expr
      content {
        date                         = expr.value.date
        days                         = expr.value.days
        expired_object_delete_marker = expr.value.expired_object_delete_marker
      }
    }

    dynamic "noncurrent_version_expiration" {
      for_each = each.value.noncurrent_version_expiration != null ? [each.value.noncurrent_version_expiration] : []
      iterator = transition
      content {
        noncurrent_days           = transition.value.days
        newer_noncurrent_versions = transition.value.versions
      }
    }

    dynamic "transition" {
      for_each = each.value.transition != null ? [each.value.transition] : []
      iterator = transition
      content {
        date          = transition.value.date
        days          = transition.value.days
        storage_class = transition.value.storage_class
      }
    }

    dynamic "noncurrent_version_transition" {
      for_each = each.value.noncurrent_version_transition != null ? [each.value.noncurrent_version_transition] : []
      iterator = transition
      content {
        noncurrent_days           = transition.value.days
        newer_noncurrent_versions = transition.value.versions
        storage_class             = transition.value.storage_class
      }
    }
  }
}

variables.tf

variable "bucket-name" {
  description = ""
  type        = string
}
variable "ignore-lifecycle-rule-changes" {
  description = "Used when lifecycle rules are managed outside of Terraform"
  type        = bool
  default     = false
}

variable "lifecycle-rules" {
  description = ""
  default     = []
  type = list(object({
    id                                     = string
    prefix                                 = string
    tags                                   = map(string)
    status                                 = string
    abort_incomplete_multipart_upload_days = number
    expiration = object({
      date                         = string
      days                         = number
      expired_object_delete_marker = bool
    })
    noncurrent_version_expiration = object({
      days     = number
      versions = number
    })
    transition = object({
      date          = string
      days          = number
      storage_class = string
    })
    noncurrent_version_transition = object({
      versions      = number
      days          = number
      storage_class = string
    })
  }))
}

variable "bucket-tags" {
  description = ""
  type        = map(string)
  default     = {}
}