Amazon S3 is an object storage service that allows you to store and retrieve large amounts of unstructured data from anywhere on the web. Launched by AWS in 2006, S3 is highly scalable and offers a cost-effective solution for data storage needs. With S3 you can store various types of unstructured data, such as videos, images, audio files, and any other type of data that does not fit neatly into a database. In this article we discuss how to use S3 with Terraform, a popular Infrastructure as Code tool. The discussion covers the basics of creating an S3 bucket and applying IAM policies to it to control access. Additionally the advanced features that Terraform offers for S3 are explored, such as lifecycle rules, versioning, and object-level encryption. Best practices for using S3 with Terraform include resource tagging, version control, and testing environments. By the end you will have a good grasp on the basics of interacting with S3 using Terraform.
Terraform is not limited to a specific cloud; rather it uses provider plugins to facilitate infrastructure management across all major cloud providers. Additionally there are community plugins available for other popular services, such as Cloudflare. To see a preview of the resource that will be created, you can run the terraform plan command in your terminal. This will show you what changes Terraform will make to your infrastructure based on the configuration in your main.tf file. Before we can apply our configuration and create the S3 bucket, we need to initialize the Terraform module by running terraform init in our terminal. This command downloads the necessary provider plugins and sets up the backend for storing the state file.
Module Landscape and Source Provenance
The terraform-aws-modules/terraform-aws-s3-bucket module is a comprehensive Terraform solution for creating and managing AWS S3 buckets with an extensive range of configurations and features. The module abstracts the complexity of S3 bucket management while providing fine-grained control over bucket properties, security settings, and integration with other AWS services. The module is designed to be comprehensive, handling nearly all S3 bucket configurations supported by the AWS provider for Terraform.
The module is organized around several core components that work together to provision and configure S3 buckets. The module uses local variables and conditional logic to determine which resources should be created based on the provided configuration. This conditional creation pattern means that unused resources are not provisioned, reducing cost and state size.
A comprehensive Terraform module for creating and managing AWS S3 buckets with advanced security, lifecycle management, and access control features is also offered via jcalles/terraform-s3-module. This module has been designed with enterprise-grade security and achieves 100 percent compliance with industry standards. Perfect Security Score 227 out of 227 Checkov security checks passed. Zero Vulnerabilities with no failed security checks. Enterprise Ready with CIS Benchmarks, PCI-DSS, HIPAA compliant. Encryption by Default with AES256 or KMS encryption for all buckets. Secure Access Controls with least privilege IAM policies, no hardcoded credentials. Automatic Cleanup with 7-day default for incomplete multipart uploads. TLS Enforcement with secure transport required for all operations.
The real world consequence of these design choices is reduced operational risk and faster audit cycles. Teams deploying the module can reference a documented security score to accelerate procurement reviews. The conditional resource creation reduces unexpected costs from orphaned AWS resources. The integration with cloudposse/terraform-null-label for standardized resource naming ensures consistent naming across environments, which lowers confusion in large organizations and simplifies cost allocation.
Core Feature Coverage
The Terraform module which creates S3 bucket on AWS with all or almost all features provided by Terraform AWS provider supports the following features of S3 bucket configurations.
- static web-site hosting
- access logging
- versioning
- CORS
- lifecycle rules
- server-side encryption
- object locking
- Cross-Region Replication
- ELB log delivery bucket policy
- ALB/NLB log delivery bucket policy
- WAF log delivery bucket policy
- Account-level Public Access Block
- S3 Directory Bucket
- S3 Table Bucket
- S3 Vectors
Static web-site hosting enables a bucket to serve content directly to browsers without additional compute. Access logging provides an audit trail of requests to objects, which is critical for compliance investigations. Versioning protects against accidental overwrites and enables point-in-time recovery. CORS configuration allows browsers to safely request objects from the bucket across origins, which is essential for web applications. Lifecycle rules automate transitions to cheaper storage classes and eventual deletion, directly impacting storage cost over time. Server-side encryption ensures data is encrypted at rest by default, reducing the likelihood of data exposure. Object locking provides WORM compliance for regulated data. Cross-Region Replication ensures durability and low-latency access across regions. ELB, ALB/NLB, and WAF log delivery bucket policies allow centralised log collection from AWS load balancers and web application firewall, simplifying security monitoring.
The module supports a comprehensive set of S3 bucket features organized by category.
| Feature Category | Supported Features | Configuration Variable |
|---|---|---|
| Basic Configuration | Bucket creation, naming, force destroy | bucket, bucketprefix, forcedestroy |
| Access Control | ACLs, Object Ownership, Public Access Block | acl, grant, objectownership, blockpublic_acls |
| Encryption | SSE-S3, SSE-KMS, Encryption policies | serversideencryption_configuration |
| Storage Management | Versioning, Lifecycle rules, Intelligent tiering | versioning, lifecyclerule, intelligenttiering |
| Website Hosting | Index/error docs, redirects, routing rules | website |
| Integrations | CORS, Logging, Replication, Notifications | corsrule, logging, replicationconfiguration |
| Monitoring | Metrics, Inventory, Analytics | metricconfiguration, inventoryconfiguration, analytics_configuration |
| Security Policies | TLS |
This table reflects the module’s coverage of the AWS provider surface. Basic Configuration variables control naming and lifecycle of the bucket itself, which impacts naming collisions and safe deletion. Access Control variables determine who can read or write objects and whether public access is blocked at the account level. Encryption variables control SSE-S3 and SSE-KMS, which determines key management responsibility. Storage Management variables influence cost and performance over the object lifecycle. Website Hosting variables allow the bucket to serve as a static site. Integrations variables couple the bucket to other AWS services. Monitoring variables enable observability.
Security First Architecture and Compliance Posture
Security First is a stated design principle. Encryption by default, secure bucket policies, and IAM integration are highlighted. The module achieves 100 percent compliance with industry standards. Perfect Security Score 227 out of 227 Checkov security checks passed. Zero Vulnerabilities with no failed security checks. Enterprise Ready with CIS Benchmarks, PCI-DSS, HIPAA compliant. Encryption by Default with AES256 or KMS encryption for all buckets. Secure Access Controls with least privilege IAM policies, no hardcoded credentials. Automatic Cleanup with 7-day default for incomplete multipart uploads. TLS Enforcement with secure transport required for all operations.
The practical impact for users is that security reviews can be completed faster. Teams can run compliance validation locally.
checkov -d
checkov -d --framework terraform
Expected result: ✅ 227 passed, ❌ 0 failed
The 7-day default for incomplete multipart uploads prevents abandoned parts from accumulating cost. TLS Enforcement ensures that all operations occur over secure transport, reducing risk of man-in-the-middle exposure. Least privilege IAM policies reduce blast radius if credentials are compromised. No hardcoded credentials eliminates secrets leakage in state files or repositories.
Multi-Bucket Support and Naming Consistency
Multi-Bucket Support allows creation and management of multiple S3 buckets with different configurations from a single module invocation. Consistent Naming is achieved via integration with cloudposse/terraform-null-label for standardized resource naming. Monitoring Ready capabilities include CloudTrail and CloudWatch integration capabilities. Highly Configurable options allow extensive customization for different use cases.
Lifecycle Management provides automated object transitions and deletion policies. Access Control supports bucket policies, access points, and CORS configuration. The combination of these capabilities means a single Terraform configuration can express a data lake with raw, processed, and archived tiers, each with distinct retention and encryption settings, while maintaining consistent naming and tagging.
The module jcalles/s3/aws demonstrates multi-bucket composition.
module "s3_buckets" {
source = "jcalles/s3/aws"
version = "~> 1.0"
namespace = "yourcompany"
stage = "prod"
aws_region = "us-west-2"
aws_account_id = "YOUR_ACCOUNT_ID"
buckets = {
"data" = {
versioning = {
enabled = true
}
server_side_encryption_rule = {
sse_algorithm = "AES256"
}
}
"logs" = {
lifecycle_rules = [
{
id = "log_retention"
enabled = true
expiration = {
days = 90
}
}
]
}
}
tags = {
Environment = "production"
Project = "web-app"
}
}
The data bucket enables versioning and AES256 server side encryption. The logs bucket defines a lifecycle rule with 90 day expiration. Tags propagate environment and project metadata for cost allocation and governance.
Additional patterns are illustrated for website buckets and datalake buckets.
module "website_buckets" {
source = "jcalles/s3/aws"
buckets = {
"website" = {
versioning_enabled = false
encryption_enabled = true
cors_enabled = true
publicly_accessible = true
cors_rule = [{
allowed_methods = ["GET", "HEAD"]
allowed_origins = ["https://example.com"]
allowed_headers = ["*"]
}]
}
}
}
module "datalake_buckets" {
source = "jcalles/s3/aws"
buckets = {
"raw-data" = {
versioning_enabled = true
encryption_enabled = true
lifecycle_rules_enabled = true
storage_class = "STANDARD"
}
"processed-data" = {
versioning_enabled = true
encryption_enabled = true
lifecycle_rules_enabled = true
storage_class = "STANDARD_IA"
}
"archived-data" = {
versioning_enabled = true
encryption_enabled = true
lifecycle_rules_enabled = true
storage_class = "GLACIER"
}
}
}
The raw-data bucket uses STANDARD storage class for immediate access. Processed-data uses STANDARD_IA for infrequent access cost savings. Archived-data uses GLACIER for long term archival. Versioning and encryption remain enabled across all tiers, ensuring consistent protection.
Configuration Examples from Reference Implementations
The terraform-aws-modules/s3-bucket/aws module is used with object ownership controls and versioning.
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = "my-s3-bucket"
acl = "private"
control_object_ownership = true
object_ownership = "ObjectWriter"
versioning = {
enabled = true
}
}
Control of object ownership with ObjectWriter ensures that the bucket owner has full control over objects even when ACLs are used. Versioning enabled protects against accidental deletion.
Log delivery buckets require specific ACLs and policies.
module "s3_bucket_for_logs" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = "my-s3-bucket-for-logs"
acl = "log-delivery-write"
force_destroy = true
control_object_ownership = true
object_ownership = "ObjectWriter"
attach_elb_log_delivery_policy = true
}
The log-delivery-write ACL allows ELB to write logs. Force destroy allows deletion of non-empty bucket during teardown. Attach ELB log delivery policy grants ELB permission to write.
A variant adds ALB and NLB log delivery.
module "s3_bucket_for_logs" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = "my-s3-bucket-for-logs"
force_destroy = true
control_object_ownership = true
object_ownership = "ObjectWriter"
attach_elb_log_delivery_policy = true
attach_lb_log_delivery_policy = true
}
Attach LB log delivery policy is required for ALB/NLB logs. The module abstracts the complex bucket policy required for load balancer log delivery, reducing manual policy errors.
WAF log delivery is also supported.
module "s3_bucket_for_waf_logs" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket =
The partial example indicates a dedicated bucket for WAF logs, which centralises web application firewall telemetry.
Operational Impact and Real-World Consequences
Using the resource block we have defined a new resource of type awss3bucket. This tells Terraform that we want to create a new S3 bucket in our AWS account. Inside the awss3bucket block we have specified the name of our bucket using the bucket field. In this case we have named it terraform-experiments, but you can choose any name that meets the requirements for S3 bucket names. It is worth noting that while the bucket field is optional, it is considered a best practice to give your resources descriptive names to help with organization and management.
The impact of descriptive naming is reduced operational toil during incident response. Teams can identify purpose from name without inspecting tags. The use of terraform init to download provider plugins and set up the backend ensures state is tracked consistently. Running terraform plan provides a preview of changes before apply, preventing unintended resource drift.
S3 on One Page highlights store data efficiently. The module patterns translate this efficiency into IaC by codifying lifecycle rules and storage class transitions. The result is predictable cost and reduced manual intervention.
Integration Points and AWS Service Coupling
Integrations covered include CORS, Logging, Replication, Notifications. Monitoring includes Metrics, Inventory, Analytics. Security Policies include TLS. These integration points couple S3 buckets to CloudFront, Lambda notifications, SNS, SQS, and other services. The module variable cors_rule allows fine-grained control over cross origin requests. Logging integration enables access logging to a separate bucket, creating an audit trail. Replication configuration enables Cross-Region Replication for business continuity.
The Account-level Public Access Block feature prevents accidental public exposure at the account level, which is a common root cause of data leaks. S3 Directory Bucket and S3 Table Bucket support newer S3 features for high performance and tabular data. S3 Vectors support vector embedding storage for AI workloads.
Conclusion
The Terraform AWS S3 bucket modules provide a comprehensive, security-first approach to provisioning S3 infrastructure. The terraform-aws-modules/s3-bucket/aws module offers broad feature coverage including static web site hosting, access logging, versioning, CORS, lifecycle rules, server side encryption, object locking, Cross-Region Replication, and log delivery policies for ELB, ALB/NLB, and WAF. The jcalles/s3/aws module emphasizes enterprise compliance with 227 out of 227 Checkov checks passed, encryption by default, TLS enforcement, and automatic cleanup of incomplete multipart uploads. Multi-bucket support, consistent naming via cloudposse/terraform-null-label, and lifecycle management allow teams to express complex data lake patterns in declarative code. Conditional resource creation, object ownership controls, and public access blocking reduce operational risk while maintaining fine-grained control. The combination of these modules with Terraform’s plan and init workflows enables safe, auditable, and repeatable S3 provisioning at scale.