1variable "bucket_name" {
2description = "The globally unique name for the S3 bucket."
3type = string
4
5validation {
6# S3 bucket naming rules: https:
7condition = can(regex("^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$", var.bucket_name))
8error_message = "Bucket name must be globally unique, between 3 and 63 characters long, and contain only lowercase letters, numbers, dots (.), and hyphens (-). It must start and end with a letter or number."
9}
10}
11
12variable "enable_versioning" {
13description = "Set to true to enable versioning for the S3 bucket."
14type = bool
15default = true
16}
17
18variable "acl" {
19description = "The canned ACL to apply to the bucket. Valid values: private, public-read, public-read-write, authenticated-read, aws-exec-read, bucket-owner-read, bucket-owner-full-control."
20type = string
21default = "private"
22
23validation {
24condition = contains([
25"private",
26"public-read",
27"public-read-write",
28"authenticated-read",
29"aws-exec-read",
30"bucket-owner-read",
31"bucket-owner-full-control",
32], var.acl)
33error_message = "Invalid ACL value. Must be one of the allowed canned ACLs."
34}
35}
36
37variable "lifecycle_rules" {
38description = "A list of lifecycle rule configurations for the S3 bucket."
39type = list(object({
40id = string
41enabled = bool
42prefix = optional(string, "")
43tags = optional(map(string), {})
44
45# Transition rules define when objects are moved to different storage classes.
46transitions = optional(list(object({
47days = number
48storage_class = string
49})), [])
50
51# Expiration rules define when objects are permanently deleted.
52expiration = optional(object({
53days = number
54}), null)
55}))
56default = []
57
58validation {
59condition = alltrue([for rule in var.lifecycle_rules : rule.id != "" && rule.enabled])
60error_message = "Each lifecycle rule must have a non-empty ID and be enabled."
61}
62
63validation {
64condition = alltrue([for rule in var.lifecycle_rules : rule.expiration == null || rule.expiration.days > 0])
65error_message = "Lifecycle expiration days must be a positive number."
66}
67
68validation {
69condition = alltrue([for rule in var.lifecycle_rules : alltrue([for transition in rule.transitions : transition.days > 0 && contains(["GLACIER", "DEEP_ARCHIVE", "INTELLIGENT_TIERING", "STANDARD_IA", "ONEZONE_IA"], transition.storage_class)])])
70error_message = "Lifecycle transition days must be positive, and storage class must be a valid AWS S3 storage class."
71}
72}
73
74resource "aws_s3_bucket" "this" {
75bucket = var.bucket_name
76acl = var.acl
77
78tags = {
79ManagedBy = "Terraform"
80BucketName = var.bucket_name
81Environment = "Module"
82}
83}
84
85resource "aws_s3_bucket_versioning" "this" {
86bucket = aws_s3_bucket.this.id
87versioning_configuration {
88status = var.enable_versioning ? "Enabled" : "Disabled"
89}
90}
91
92resource "aws_s3_bucket_lifecycle_configuration" "this" {
93bucket = aws_s3_bucket.this.id
94
95rule = [for rule in var.lifecycle_rules : {
96id = rule.id
97status = rule.enabled ? "Enabled" : "Disabled"
98prefix = rule.prefix
99tags = rule.tags
100
101transition = [
102for transition in rule.transitions : {
103days = transition.days
104storage_class = transition.storage_class
105}
106]
107
108expiration = rule.expiration != null ? {
109days = rule.expiration.days
110} : null
111}]
112}
113
114output "bucket_id" {
115description = "The ID of the created S3 bucket."
116value = aws_s3_bucket.this.id
117}
118
119output "bucket_arn" {
120description = "The ARN of the created S3 bucket."
121value = aws_s3_bucket.this.arn
122}