Infrastructure as code for Amazon S3 begins with a clear declaration of desired state and ends with a consistent, repeatable bucket that can be observed, versioned, and extended without manual console interaction. Terraform allows the S3 bucket to be defined declaratively, ensuring consistency and version control. Automation using Terraform eliminates manual clicking in the AWS Console, reducing human error. State management by Terraform tracks the state of the bucket, making updates and deletions safer and more predictable. Advanced features can be layered like Versioning, Logging, and IAM policies directly within Terraform code.
When working with applications hosted on the cloud, a data storage solution is required. Some of the storage options that can be set up in the cloud include object storage, block storage, and file storage. While they vary depending on the provider, they typically offer scalability, flexibility, and cost-effectiveness and can be easily set up with an IaC tool such as Terraform.
In this blog post, we look at how to create an AWS S3 bucket using Terraform, one of AWS’ popular storage options.
Terraform allows you to define your infrastructure using code, providing an easily manageable way of expressing desired states with versioning, collaboration, and automation. Additionally, Terraform has excellent support for AWS.
Before diving in further, let's understand why Terraform is such an attractive solution for infrastructure provisioning in AWS.
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.
Amazon S3 is a storage service offered by AWS for storing data lakes, websites, mobile applications, backups and restores, archives, and enterprise applications. The S3 stands for Simple Storage Service, which can be scaled based on individual or organization needs. In addition to providing storage, Amazon S3 also offers comprehensive access management, which can help you set up very granular permissions.
Prerequisites And Account Setup
Before diving into the hands-on process, ensure you have these necessary prerequisites in place:
- AWS Account: An active AWS account to access and utilize AWS services, including S3
Specifying desired properties such as name, ACLs, and storage class within a Terraform configuration file allows Terraform to create the buckets in the AWS account.
The benefits that emerge from this setup are repeatable, version-controlled, reusable, and state managed.
Repeatable ensures the S3 buckets will be created consistently every time the Terraform apply command is run.
Version-controlled allows tracking changes made to S3 buckets over time, providing an effective means for rolling back to previous versions of the configuration if necessary.
Reusable means Terraform modules can be created to store common S3 bucket configurations, saving time and effort.
State Management means Terraform maintains a state file that keeps track of the current state of the infrastructure, including AWS S3 buckets. This state file enables Terraform to understand the differences between the declared configuration and the actual infrastructure, facilitating updates and modifications.
Here are some specific benefits of learning how to create an AWS S3 bucket using Terraform:
- Infrastructure as Code: Terraform allows you to define S3 buckets declaratively, ensuring consistency and version control.
- Automation: Using Terraform eliminates manual clicking in the AWS Console, reducing human error.
- State Management: Terraform tracks the state of your bucket, making updates and deletions safer and more predictable.
- Advanced Features: You can easily layer complex configurations like Versioning, Logging, and IAM policies directly within your Terraform code.
Core Terraform Workflow Init Plan Apply
The first command is to initialize the working directory and download the AWS provider.
terraform init
The second command is to run a Terraform plan. This command shows the bucket, public access block, and ownership controls that Terraform is about to create.
terraform plan
Apply the Terraform configuration using the terraform apply command, which will eventually create an S3 bucket in AWS.
terraform apply
Note: These screenshots reflect an earlier version of the example. The updated code in this article uses current Terraform AWS provider patterns, so resource names and plan/apply output may differ slightly.
After the apply completes, you should see a new S3 bucket named spacelift-test1-s3 in your AWS account.
Type yes and press Enter. Terraform will then create the S3 bucket and output information about the resources created.
To see a preview of the resource that will be created, you can run the terraform plan command in the terminal. This will show 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.
The workflow provides a safety net. Plan reveals drift before any change is made. Init guarantees provider versions are pinned. Apply enforces the declared state. Verification follows by visiting the AWS Management Console and navigating to the S3 service. The newly created bucket with the name specified in the Terraform configuration should be visible.
Resource Definition awss3bucket
Using the resource block, a new resource of type awss3bucket is defined. This tells Terraform that we want to create a new S3 bucket in the AWS account.
Inside the awss3bucket block, the name of the bucket is specified using the bucket field. In this case, it is named terraform-experiments, but any name that meets the requirements for S3 bucket names can be chosen.
It is worth noting that while the bucket field is optional, it is considered a best practice to give resources descriptive names to help with organization and management.
A minimal provider configuration is:
provider "aws" {
region = "us-east-1"
}
A minimal bucket definition is:
resource "aws_s3_bucket" "example" {
bucket = "my-unique-bucket-name-12345"
}
Then run terraform init, terraform plan, and terraform apply.
The main Terraform resources used in this guide are:
- awss3bucket
- awss3object
- awss3bucketpublicaccess_block
- awss3bucketownershipcontrols
These resources let you create a bucket, upload objects, block public access, and manage bucket ownership behavior.
Create the bucket with the awss3bucket resource after configuring the AWS provider. In modern Terraform, keep the bucket definition minimal and manage settings like versioning or encryption with separate resources when needed.
Versioning Configuration
Enabling versioning for your S3 bucket helps manage and retain multiple versions of an object.
To enable versioning, modify your main.tf file:
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-unique-bucket-name"
tags = {
Name = "MyS3Bucket"
Environment = "Production"
}
}
resource "aws_s3_bucket_versioning" "versioning_example" {
bucket = aws_s3_bucket.my_bucket.id
versioning_configuration {
status = "Enabled"
}
}
Versioning is managed as a separate resource in current provider patterns. This separation keeps the bucket resource minimal and allows versioning to be toggled without recreating the bucket.
Impact of versioning is protection against accidental deletion and the ability to restore prior object states. Contextually, versioning pairs with lifecycle rules and object locking for retention policies.
Logging And Access Management
You can configure S3 bucket logging to record all requests made to your bucket.
Logging provides an audit trail for access patterns, error rates, and compliance evidence. The log data can be analyzed to detect anomalous access or to satisfy regulatory requirements.
Access management is comprehensive. Amazon S3 also offers comprehensive access management, which can help set up very granular permissions.
Public access control is handled via awss3bucketpublicaccessblock and awss3bucketownership_controls.
Ownership controls determine how object ownership is interpreted in a bucket. Control object ownership and set object_ownership to ObjectWriter enables predictable behavior for cross-account uploads.
Object Upload And Deprecation Notes
In the previous step, an S3 bucket is created with Terraform. In this step, files are uploaded to that bucket using the awss3object resource.
If working from older examples, you may still see awss3bucket_object, but that resource has been deprecated.
The shift from awss3bucketobject to awss3_object reflects provider modernization. Using the current resource avoids deprecation warnings and aligns with current documentation.
Uploading objects as part of Terraform state means the object content becomes part of IaC. This is appropriate for small configuration files and templates, while large binaries are better delivered via CI pipelines.
Public Access Block And Ownership Controls
Modern patterns encourage blocking public access by default and explicitly managing ownership.
The resource set awss3bucketpublicaccessblock and awss3bucketownership_controls lets you create a bucket, upload objects, block public access, and manage bucket ownership behavior.
These controls interact with the bucket policy and ACL. The combination reduces the risk of accidental public exposure while preserving the ability to allow controlled access through IAM policies.
Module Based Provisioning
Terraform module which creates S3 bucket on AWS with all or almost all features provided by Terraform AWS provider.
These features of S3 bucket configurations are supported:
- static web-site hosting
- access logging
- versioning
- CORS
- lifecycle rules
- server-side encryption
- object locking
- Cross-Region Replication (CRR)
- 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
Module usage example:
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
}
}
Log delivery bucket example:
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
}
Additional log delivery configuration:
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
}
WAF log delivery example:
module "s3_bucket_for_waf_logs" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket =
The module encapsulates complexity. Using a module ensures consistent defaults for ownership, public access block, and logging policies. Force_destroy allows deletion of non-empty bucket when required for ephemeral environments.
Feature Matrix And Resource Overview
The following table summarizes key Terraform resources and their purpose.
| Resource | Purpose | Typical Attributes |
| awss3bucket | Create S3 bucket | bucket, tags |
| awss3bucketversioning | Enable versioning | bucket, versioningconfiguration.status |
| awss3object | Upload object | bucket, key, source |
| awss3bucketpublicaccessblock | Block public access | blockpublicacls, ignorepublicacls, blockpublicpolicy, restrictpublicbuckets |
| awss3bucketownershipcontrols | Manage ownership | bucket, rule.objectownership |
The following table summarizes module supported features.
| Feature Category | Supported Capabilities |
| Hosting | static web-site hosting |
| Logging | access logging, ELB log delivery bucket policy, ALB/NLB log delivery bucket policy, WAF log delivery bucket policy |
| Data Protection | versioning, server-side encryption, object locking, lifecycle rules |
| Access | CORS, Account-level Public Access Block, access management |
| Replication | Cross-Region Replication |
| Advanced Buckets | S3 Directory Bucket, S3 Table Bucket, S3 Vectors |
Best Practices And Benefits
Infrastructure as Code provides declarative definition, consistency, and version control.
Automation eliminates manual console steps and reduces human error.
State Management tracks current state, making updates and deletions safer.
Advanced Features allow layering of Versioning, Logging, and IAM policies directly within code.
Repeatable configurations ensure buckets are created consistently each apply.
Version-controlled configurations allow rollback to previous versions.
Reusable modules store common configurations, saving time and effort.
State files enable Terraform to understand differences between declared configuration and actual infrastructure, facilitating updates and modifications.
When working with applications hosted on the cloud, object storage can be set up with an IaC tool such as Terraform.
Why Terraform remains attractive is the ability to define infrastructure using code, providing an easily manageable way of expressing desired states with versioning, collaboration, and automation. Additionally, Terraform has excellent support for AWS.
Conclusion
Terraform AWS S3 bucket provisioning moves storage definition from manual console clicks to declarative code that can be reviewed, tested, and audited. The core workflow of init, plan, and apply provides a predictable change pipeline. Keeping the awss3bucket resource minimal and delegating versioning, encryption, logging, and public access controls to separate resources aligns with modern provider patterns. The awss3bucketobject deprecation to awss3_object illustrates the need to track provider evolution. Ownership controls and public access blocks harden security posture by default. Module based provisioning via terraform-aws-modules/s3-bucket/aws compresses dozens of settings into reusable blocks for static hosting, access logging, versioning, CORS, lifecycle rules, server-side encryption, object locking, Cross-Region Replication, ELB/ALB/NLB/WAF log delivery policies, account-level public access block, and specialized bucket types such as S3 Directory Bucket, S3 Table Bucket, and S3 Vectors. Verification in the AWS Management Console confirms the declared state materializes. The result is repeatable, version-controlled, and reusable infrastructure that supports both simple storage needs and complex enterprise data lakes.