Managing Multiple S3 Buckets with Fine-Grained Access Policies Using Terraform

Hello Folks,

Terraform Level-3 Task-7 is getting failed- “due to policy missing for ‘datacenter-dev-bucket-26876’”. However, terraform show commands display policy is created for dev bucket.

main.tf

######################################

Create S3 Buckets (Dev, Staging, Prod)

######################################

resource “aws_s3_bucket” “bucket_creation_using_for_loop” {
for_each = var.KKE_ENV_TAGS
bucket = each.value.bucket_name

tags = merge(
{
Name = each.value.bucket_name
Environment = each.key

},
each.value.owner != "" ? { Owner = each.value.owner } : {}

)

lifecycle {
ignore_changes = [tags]
}

}

######################################

Lifecycle Rules (for buckets with backup = true)

######################################

resource “aws_s3_bucket_lifecycle_configuration” “kke_lifecycle” {
for_each = {
for env, config in var.KKE_ENV_TAGS :
env => config if lookup(config, “backup”, false)
}

bucket = aws_s3_bucket.bucket_creation_using_for_loop[each.key].id

rule {
id = “MoveToGlacier”
status = “Enabled”

filter {
  prefix = ""
}

transition {
  days          = 30
  storage_class = "GLACIER"
}

}
}

######################################

Public Read Bucket Policy

######################################

resource “aws_s3_bucket_policy” “kke_bucket_policy” {
for_each = var.KKE_ENV_TAGS

bucket = aws_s3_bucket.bucket_creation_using_for_loop[each.key].id

policy = jsonencode({
Version = “2012-10-17”
Statement = [
{
Sid = “PublicReadAccess”
Effect = “Allow”
Principal = “"
Action = [
"s3:Get
”,
“s3:List*”,
“s3:Describe*”
]
Resource = [
aws_s3_bucket.bucket_creation_using_for_loop[each.key].arn,
“${aws_s3_bucket.bucket_creation_using_for_loop[each.key].arn}/*”
]
}
]
})

depends_on = [aws_s3_bucket.bucket_creation_using_for_loop]
}

############################################

variables.tf

############################################

variable “KKE_ENV_TAGS” {
description = “Environment specific metadata”
type = map(object({
bucket_name = string
owner = string
backup = optional(bool, false)
}))
default = {
Dev = {
bucket_name = “nautilus-dev-bucket-31091”
owner = “Alice”
backup = false
}
Staging = {
bucket_name = “nautilus-staging-bucket-31091”
owner = “Bob”
backup = true
}
Prod = {
bucket_name = “nautilus-prod-bucket-31091”
owner = “Carol”
backup = true
}
}
}

############################################

outputs.tf

############################################

output “kke_bucket_names” {
description = “Names of the created S3 buckets”
value = [for b in aws_s3_bucket.bucket_creation_using_for_loop : b.bucket]
}

Can someone what went wrong here?

Thanks,
Sohel Khan.

Hi @Sohelk

I’ve just checked it, and it’s working properly on my end, so this issue seems to be user-specific. Could you please share your code again using the code block feature in this forum? I’ll review it and provide feedback.

image

Please find below .tf files,

######################################
#main.tf
# Create S3 Buckets (Dev, Staging, Prod)
######################################

resource “aws_s3_bucket” “bucket_creation_using_for_loop” {
for_each = var.KKE_ENV_TAGS
bucket = each.value.bucket_name

tags = merge(
{
Name = each.value.bucket_name
Environment = each.key



},
each.value.owner != “” ? { Owner = each.value.owner } : {}

)

lifecycle {
ignore_changes = [tags]
}

}

######################################

# Lifecycle Rules (for buckets with backup = true)

######################################

resource “aws_s3_bucket_lifecycle_configuration” “kke_lifecycle” {
for_each = {
for env, config in var.KKE_ENV_TAGS :
env => config if lookup(config, “backup”, false)
}

bucket = aws_s3_bucket.bucket_creation_using_for_loop[each.key].id

rule {
id = “MoveToGlacier”
status = “Enabled”

filter {
prefix = “”
}

transition {
days = 30
storage_class = “GLACIER”
}

}
}

######################################

# Public Read Bucket Policy

######################################

resource “aws_s3_bucket_policy” “kke_bucket_policy” {
for_each = var.KKE_ENV_TAGS

bucket = aws_s3_bucket.bucket_creation_using_for_loop[each.key].id

policy = jsonencode({
Version = “2012-10-17”
Statement = [
{
Sid = “PublicReadAccess”
Effect = “Allow”
Principal = “"
Action = [
"s3:Get
”,
“s3:List*”,
“s3:Describe*”
]
Resource = [
aws_s3_bucket.bucket_creation_using_for_loop[each.key].arn,
“${aws_s3_bucket.bucket_creation_using_for_loop[each.key].arn}/*”
]
}
]
})

depends_on = [aws_s3_bucket.bucket_creation_using_for_loop]
}

############################################

# variables.tf

############################################

variable “KKE_ENV_TAGS” {
description = “Environment specific metadata”
type = map(object({
bucket_name = string
owner = string
backup = optional(bool, false)
}))
default = {
Dev = {
bucket_name = “nautilus-dev-bucket-31091”
owner = “Alice”
backup = false
}
Staging = {
bucket_name = “nautilus-staging-bucket-31091”
owner = “Bob”
backup = true
}
Prod = {
bucket_name = “nautilus-prod-bucket-31091”
owner = “Carol”
backup = true
}
}
}

############################################

# outputs.tf

############################################

output “kke_bucket_names” {
description = “Names of the created S3 buckets”
value = [for b in aws_s3_bucket.bucket_creation_using_for_loop : b.bucket]
}

Hi @Sohelk

The task asks to use the lifecycle block with ignore_changes to protect the tags, not aws_s3_bucket_lifecycle_configuration. Please try again.

Hi @raymond.baoly, please review my code once if you get a chance. I am also facing similar issue. I can see the lifecycle policy in the state file for dev bucket.

resource “aws_s3_bucket” “buckets” {
for_each = var.KKE_ENV_TAGS
bucket = each.value.Name
tags = {
Name = each.value.Name
Environment = each.value.Environment
Owner = each.value.Owner
Backup = each.value.Backup ? “true” : “false”
}
lifecycle {
ignore_changes = [
tags
]
}
}

resource “aws_s3_bucket_lifecycle_configuration” “lifecycle” {
for_each = { for k, v in var.KKE_ENV_TAGS : k => v if v.Backup }
bucket = aws_s3_bucket.buckets[each.key].id
rule {
id = “MoveToGlacier”
status = “Enabled”
transition {
days = 30
storage_class = “GLACIER”
}
}
}

resource “aws_s3_bucket_policy” “policy” {
for_each = var.KKE_ENV_TAGS
bucket = aws_s3_bucket.buckets[each.key].id
policy = jsonencode({
Version = “2012-10-17”
Statement = [
{
Effect = “Allow”
Principal = “"
Action = “S3:GetObject”
Resource = "${aws_s3_bucket.buckets[each.key].arn}/

}
]
})
depends_on = [aws_s3_bucket.buckets]
}

variable “KKE_ENV_TAGS” {
type = map(object({
Name = string
Environment = string
Owner = string
Backup = bool
}))
default = {
dev = {
Name = “devops-dev-bucket-31190”
Environment = “Dev”
Owner = “Alice”
Backup = false
}
staging = {
Name = “devops-staging-bucket-31190”
Environment = “Staging”
Owner = “Bob”
Backup = true
}
Prod = {
Name = “devops-prod-bucket-31190”
Environment = “Prod”
Owner = “Carol”
Backup = true
}
}
}

output “kke_bucket_name” {
value = [for b in aws_s3_bucket.buckets : b.bucket]
}