This project provisions AWS S3 buckets and EC2 instances using Terraform in a production-ready setup:
-
Remote backend with S3 + DynamoDB state locking
-
Reusable Terraform modules (
s3
,ec2
) -
Workspaces for dev, stage, prod
-
HashiCorp Vault for secret management
-
GitHub-friendly repo (no secrets checked in)
terraform-aws-project/
├── backend.tf # Remote backend config (S3 + DynamoDB)
├── main.tf # Root module calling s3 & ec2
├── providers.tf # AWS & Vault providers
├── variables.tf # Input variables
├── outputs.tf # Root outputs
├── modules/ # Reusable modules
│ ├── ec2/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── s3/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── .gitignore # Ignore secrets & tfstate files
Note: The envs/
folder is intentionally not included. You will create it locally with environment .tfvars
files.
Terraform uses an S3 bucket + DynamoDB for state management.
Workspaces (dev
, stage
, prod
) create isolated state files inside the bucket:
deva-terraform-remote-s3/
├── env:/dev/global/s3-ec2/terraform.tfstate
├── env:/stage/global/s3-ec2/terraform.tfstate
└── env:/prod/global/s3-ec2/terraform.tfstate
-
Each environment has a separate state file → no conflicts.
-
State is stored remotely in S3 → accessible to the whole team.
-
DynamoDB locking prevents simultaneous changes.
-
Encrypted (SSE) in S3 for security.
You can create it via AWS CLI or Terraform.
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--region us-east-1
Explanation:
LockID
→ Primary key for DynamoDB table.- Used by Terraform to acquire locks during
apply
to prevent concurrent changes.
Note: In backend.tf file we will have S3 and DynamoDB configurations
✅ Important Notes
-
Always use S3 + DynamoDB in production for safe remote state + locking.
-
Use versioning on the S3 bucket to avoid accidental state loss.
-
Terraform ≥ 1.5
-
AWS CLI configured
-
HashiCorp Vault running
-
AWS IAM user/role with permissions:
-
AmazonS3FullAccess
-
AmazonEC2FullAccess
-
AmazonDynamoDBFullAccess
-
git clone https://github.com/<your-username>/terraform-aws-project.git
cd terraform-aws-project
Create a folder:
mkdir envs
Inside it, add environment files:
envs/dev.tfvars
aws_region = "us-east-2"
vault_addr = "<vault-host>:8200"
vault_token = "VAULT_TOKEN"
environment = "dev"
bucket_name = "devaterraform-dev-bucket-12345"
ami_id = "ami-0cfde0ea8edd312d4"
instance_type = "t3.micro"
envs/stage.tfvars
aws_region = "us-east-2"
vault_addr = "<vault-host>:8200"
vault_token = "VAULT_TOKEN"
environment = "dev"
bucket_name = "devaterraform-dev-bucket-12345"
ami_id = "ami-0cfde0ea8edd312d4"
instance_type = "t3.micro"
envs/prod.tfvars
aws_region = "us-east-2"
vault_addr = "<vault-host>:8200"
vault_token = "VAULT_TOKEN"
environment = "dev"
bucket_name = "devaterraform-dev-bucket-12345"
ami_id = "ami-0cfde0ea8edd312d4"
instance_type = "t3.micro"
terraform init
terraform workspace new dev
terraform workspace new stage
terraform workspace new prod
terraform workspace select dev
export VAULT_TOKEN="s.xxxxxx" # from Vault
terraform apply -var-file=envs/dev.tfvars
terraform destroy -var-file=envs/dev.tfvars
When applied, Terraform will output:
-
S3 Bucket Name
-
EC2 Instance ID
-
EC2 Public IP
Challenge | Solution |
---|---|
GitHub rejected pushes due to Vault token in .tfvars |
Cleaned repo, re-initialized Git, added .gitignore , excluded .tfvars |
Terraform module errors (missing vars/outputs) | Fixed with proper variables.tf and outputs.tf in modules |
Backend locking issues | Added DynamoDB table for state locking |
Multiple environments | Used workspaces and per-environment .tfvars files |
-
Terraform AWS Provider
https://registry.terraform.io/providers/hashicorp/aws/latest/docs -
Terraform Workspaces
https://developer.hashicorp.com/terraform/language/state/workspaces -
HashiCorp Vault
https://developer.hashicorp.com/vault/docs -
Terraform Backends
https://developer.hashicorp.com/terraform/language/settings/backends/s3