terraform
Terraform AWS S3 Bucket
Terraform configuration for an AWS S3 bucket with versioning, encryption, lifecycle rules, and access policies.
Overview
A Terraform configuration for provisioning an AWS S3 bucket with production-ready defaults. Includes server-side encryption, versioning, lifecycle policies for cost management, public access blocking, and logging.
Configuration
# main.tf
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Use latest 5.x release
}
}
# Remote state storage (recommended for teams)
backend "s3" {
bucket = "my-terraform-state"
key = "s3-bucket/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = var.project_name
}
}
}
# ── S3 Bucket ──
resource "aws_s3_bucket" "main" {
bucket = "${var.project_name}-${var.environment}-${var.bucket_suffix}"
# Prevent accidental deletion of bucket with data
force_destroy = false
tags = {
Name = "${var.project_name}-${var.environment}"
}
}
# ── Versioning ──
resource "aws_s3_bucket_versioning" "main" {
bucket = aws_s3_bucket.main.id
versioning_configuration {
status = "Enabled" # Track all object versions
}
}
# ── Server-Side Encryption ──
resource "aws_s3_bucket_server_side_encryption_configuration" "main" {
bucket = aws_s3_bucket.main.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms" # KMS encryption (or "AES256" for S3-managed)
}
bucket_key_enabled = true # Reduce KMS API calls and cost
}
}
# ── Block All Public Access ──
resource "aws_s3_bucket_public_access_block" "main" {
bucket = aws_s3_bucket.main.id
block_public_acls = true # Block public ACLs
block_public_policy = true # Block public bucket policies
ignore_public_acls = true # Ignore existing public ACLs
restrict_public_buckets = true # Restrict public bucket policies
}
# ── Lifecycle Rules ──
resource "aws_s3_bucket_lifecycle_configuration" "main" {
bucket = aws_s3_bucket.main.id
rule {
id = "transition-to-cheaper-storage"
status = "Enabled"
transition {
days = 30 # Move to IA after 30 days
storage_class = "STANDARD_IA"
}
transition {
days = 90 # Move to Glacier after 90 days
storage_class = "GLACIER"
}
noncurrent_version_expiration {
noncurrent_days = 90 # Delete old versions after 90 days
}
}
rule {
id = "abort-incomplete-uploads"
status = "Enabled"
abort_incomplete_multipart_upload {
days_after_initiation = 7 # Clean up failed uploads after 7 days
}
}
}
# variables.tf
variable "aws_region" {
description = "AWS region for resources"
type = string
default = "us-east-1"
}
variable "environment" {
description = "Environment name (dev, staging, prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "project_name" {
description = "Project name used in resource naming"
type = string
}
variable "bucket_suffix" {
description = "Unique suffix for the bucket name"
type = string
default = "assets"
}
# outputs.tf
output "bucket_id" {
description = "The name of the bucket"
value = aws_s3_bucket.main.id
}
output "bucket_arn" {
description = "The ARN of the bucket"
value = aws_s3_bucket.main.arn
}
output "bucket_domain_name" {
description = "The domain name of the bucket"
value = aws_s3_bucket.main.bucket_domain_name
}
Key Options Explained
force_destroy = false— Preventsterraform destroyfrom deleting a bucket that still contains objects. Set totrueonly for ephemeral/dev buckets.aws:kmsencryption — Uses AWS KMS for server-side encryption, providing audit trails via CloudTrail.AES256is simpler but has no key management.bucket_key_enabled— Reduces the number of KMS API calls by using a bucket-level key, cutting KMS costs by up to 99%.- Public access block — All four flags set to
trueensures no path to public access, even if a misconfigured policy is applied later. - Lifecycle transitions — Automatically moves data to cheaper storage classes as it ages: Standard to IA (30 days) to Glacier (90 days).
noncurrent_version_expiration— Cleans up old object versions created by versioning, preventing unbounded storage growth.
Common Modifications
- Static website hosting: Add
aws_s3_bucket_website_configurationwithindex_documentanderror_documentfor static site hosting. - CloudFront distribution: Pair with a CloudFront distribution using an Origin Access Control (OAC) for CDN delivery.
- CORS: Add
aws_s3_bucket_cors_configurationfor cross-origin access from web applications. - Replication: Add
aws_s3_bucket_replication_configurationfor cross-region disaster recovery. - Notifications: Add
aws_s3_bucket_notificationto trigger Lambda functions or SQS queues on object events.