Task: Create an AWS S3 bucket named 'test-bucket' with preventive destruction measures, private ACLs, AES256 server-side encryption, and a public access block restricting public access.

Specification:
has one "aws_s3_bucket" resource
    with one "bucket"

has one "aws_s3_bucket_acl"
    with one "bucket"
    with one "acl" or "access_control_policy"

has one "aws_s3_bucket_public_access_block"
    with one "bucket"

has one "aws_s3_bucket_server_side_encryption_configuration"
    with one "bucket"
    with one "rule"

Terraform configuration (main.tf):
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.75"
    }
  }

  required_version = ">= 1.9.0"
}

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "backstage" {
  bucket_prefix = "test-bucket-"
}

resource "aws_s3_bucket_ownership_controls" "example" {
    bucket = aws_s3_bucket.backstage.id
    rule {
        object_ownership = "BucketOwnerPreferred"
    }
}

resource "aws_s3_bucket_acl" "backstage_acl" {
  depends_on = [ aws_s3_bucket_ownership_controls.example ]
  bucket = aws_s3_bucket.backstage.id
  acl    = "private"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "backstage_server_side_encription" {
  bucket = aws_s3_bucket.backstage.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "backstage" {
  bucket = aws_s3_bucket.backstage.id

  block_public_acls   = true
  block_public_policy = true
  ignore_public_acls = true
  restrict_public_buckets = true
}

Validation policy (checks.rego):
package terraform.validation


default valid_configuration = false
default has_s3_bucket = false
default has_aws_s3_bucket_acl = false
default has_aws_s3_bucket_public_access_block = false
default has_aws_s3_bucket_server_side_encryption_configuration = false

has_s3_bucket {
    resource := input.configuration.root_module.resources[_]
    resource.type == "aws_s3_bucket"    
    # resource.name == "backstage"
    # some i
    # resource.expressions.bucket.references[i] == "var.bucket_name"
}


has_aws_s3_bucket_acl{
        resource := input.configuration.root_module.resources[_]
    resource.type == "aws_s3_bucket_acl"    
    # resource.name == "backstage"
    resource.expressions.acl.constant_value != null
    
}

has_aws_s3_bucket_public_access_block{
    resource := input.configuration.root_module.resources[_]
    resource.type == "aws_s3_bucket_public_access_block"
    resource.expressions.bucket.references != null
}

has_aws_s3_bucket_server_side_encryption_configuration{
    resource := input.configuration.root_module.resources[_]
    resource.type == "aws_s3_bucket_server_side_encryption_configuration"    
    # resource.name == "backstage"
    # some i, j, k
    # resource.expressions.server_side_encryption_configuration[i].rule[j].apply_server_side_encryption_by_default[k].sse_algorithm.constant_value == "AES256" != null
    some i, j
    resource.expressions.rule[i].apply_server_side_encryption_by_default[j].sse_algorithm.constant_value == "AES256"
}

valid_configuration{
    has_s3_bucket
    has_aws_s3_bucket_acl
    has_aws_s3_bucket_public_access_block
    has_aws_s3_bucket_server_side_encryption_configuration
}