# Task 109


Set up an AWS S3 bucket and DynamoDB table for Terraform state management. Ensure the S3 bucket has preventive measures against accidental deletion and is configured with versioning enabled, server-side encryption using AES256, and public access restrictions. Create a DynamoDB table with a hash key named 'LockID' using 'PAY_PER_REQUEST' billing mode for Terraform locking.

## Intent

has one aws_s3_bucket_versioning resource
with bucket    
with status

has one aws_dynamodb_table resource
with name
with hash_key

has one aws_s3_bucket_lifecycle_configuration resource
with bucket 
with rule
    with id
    with status
    with action
    with transition
        with days
        with storage_class

has one aws_s3_bucket_server_side_encryption_configuration resource
with bucket
with rule
    with apply_server_side_encryption_by_default
    with sse_algorithm

has one aws_s3_bucket_public_access_block resource
with bucket
with block_public_acls (boolean)
with block_public_policy (boolean)
with ignore_public_acls (boolean)
with restrict_public_buckets(boolean)

has one aws_dynamodb_table resource
with name  
with hash_key  
with attribute
    with name
    with type

## Reference Terraform solution

terraform {
  required_version = ">= 1.15.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

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

resource "aws_s3_bucket" "terraform_state" {
  bucket = "iac-misc-terraform-state"
}

resource "aws_s3_bucket_versioning" "enabled" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
  bucket = aws_s3_bucket.terraform_state.id

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

resource "aws_s3_bucket_public_access_block" "public_access" {
  bucket                  = aws_s3_bucket.terraform_state.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "iac-misc-terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}


## OPA Rego validation policy

package terraform.policy

import rego.v1

default has_s3_bucket_versioning = false
default has_dynamodb_table = false
default has_s3_bucket_server_side_encryption_configuration = false
default has_s3_bucket_public_access_block = false
default valid_configuration = false


# Check for aws_s3_bucket_versioning resource with 'bucket' and 'status'
has_s3_bucket_versioning if {
    versioning := input.planned_values.root_module.resources[_]
    versioning.type == "aws_s3_bucket_versioning"
    versioning.values.versioning_configuration[_].status == "Enabled"
}

# Check for aws_dynamodb_table resource with 'name' and 'hash_key'
has_dynamodb_table if {
    some i
    table := input.planned_values.root_module.resources[i]
    table.type == "aws_dynamodb_table"
    table.values.name != null
    table.values.hash_key != null
}

# Check for aws_s3_bucket_server_side_encryption_configuration resource with 'bucket', 'rule', and 'sse_algorithm'
has_s3_bucket_server_side_encryption_configuration if {
    some i
    encryption := input.planned_values.root_module.resources[i]
    encryption.type == "aws_s3_bucket_server_side_encryption_configuration"
    encryption.values.rule != null
    encryption.values.rule[0].apply_server_side_encryption_by_default != null
    encryption.values.rule[0].apply_server_side_encryption_by_default[0].sse_algorithm != null
}

# Check for aws_s3_bucket_public_access_block resource with 'bucket' and boolean flags
has_s3_bucket_public_access_block if {
    some i
    access_block := input.planned_values.root_module.resources[i]
    access_block.type == "aws_s3_bucket_public_access_block"
    access_block.values.block_public_acls == true
    access_block.values.block_public_policy == true
    access_block.values.ignore_public_acls == true
    access_block.values.restrict_public_buckets == true
}

# Combined validation rule
valid_configuration if {
    has_s3_bucket_versioning
    has_dynamodb_table
    has_s3_bucket_server_side_encryption_configuration
    has_s3_bucket_public_access_block
}

main if {
    valid_configuration
}