Tools
π Building a Secure AWS Environment with Terraform + AWS CloudShell
2025-12-29
0 views
admin
β Why I Built This Project ## π Project Sequence ## π§± Why This Project Matters ## π Table of Contents ## π Introduction ## π Why This Project Matters ## π§° Prerequisites ## π» Using VS Code vs AWS CloudShell ## π₯ Use VS Code for editing ## π₯ Use AWS CloudShell for running Terraform ## βοΈ Setting Up AWS CloudShell ## π Creating Your Terraform Project Structure ## π Writing Terraform Configuration Files ## πΉ versions.tf ## πΉ variables.tf ## πΉ main.tf ## πΉ outputs.tf ## βοΈ Initializing Terraform in CloudShell ## π Planning and Applying ## π Verifying the Deployment ## Check VPC ## Check S3 ## Check CloudTrail ## π§Ή Cleaning Up Resources ## π Troubleshooting Tips ## β Terraform not found ## β Permission denied ## β S3 bucket name already exists ## π Final Thoughts (Project 6 of 6 β Terraform Security Module: Secure AWS Baseline with Infrastructure as Code) Instead of studying cloud security concepts in isolation, Iβm using real job descriptions as a roadmap and building hands-on projects that map directly to cloud security, cloud operations, and security engineering roles. This 6-part series focuses on practical, cloud security skills, including: Each project is designed to reflect real-world responsibilities, not just theoretical learning. π Part 1: AWS IAM Hardening β strengthening identity boundaries and improving authentication hygiene
π Part 2: Cloud Security Posture Management (CSPM) using Security Hub + AWS Config
π Part 3: CASB-Like Monitoring with GuardDuty + CloudTrail, focusing on anomalies, delegated admin, and safe threat simulation
π Part 4: Drift Detection with AWS Config, using managed rules, EventBridge routing, tags, and optional remediation
π Part 5: Log Analysis & Dashboards with Athena + QuickSight, turning raw CloudTrail logs into actionable security insights
π Part 6: (this project) β Terraform Security Module, building a secure AWS baseline using Infrastructure as Code In real-world cloud environments, security doesnβt start in the console, it starts in code. Modern cloud security teams rely on Infrastructure as Code (IaC) tools like Terraform to ensure environments are: This project focuses on using Terraform to define and deploy a secure AWS foundation, including: Instead of manually clicking through the AWS console, this project demonstrates how security controls can be: Youβll also see how Terraform helps detect and prevent drift, a critical requirement in regulated and enterprise cloud environments. To keep the project accessible and low-cost, Terraform is executed using AWS CloudShell, eliminating local installation challenges (especially on Windows ARM systems) while still following real-world DevSecOps workflows. By the end of this project, youβll have a repeatable, secure AWS baseline defined entirely in code, a strong capstone that ties together identity, monitoring, logging, and governance concepts from the entire series and aligns directly with expectations for cloud security and cloud operations roles. Beginner-Friendly | Fun | Technical | Real-World Cloud Security Project Welcome to Project 6 - Terraform Security Module, where youβll learn how to build a secure AWS baseline using Terraform, AWS CloudShell, and a workflow that mirrors real cloud security engineering. This guide is fun, practical, and perfect for beginners who want hands-on cloud security experience without breaking the bank. Terraform is one of the most powerful Infrastructure-as-Code (IaC) tools in the cloud ecosystem.
But installing Terraform locally, especially on Windows ARM devices, can get complicated. So instead, we take the fun, beginner-friendly, zero-hassle route:
π Run Terraform directly inside AWS CloudShell, which comes preconfigured with AWS credentials and a Linux environment, exactly like real DevOps teams use. You will create three essential security components using Terraform: These are foundational in cloud security operations, compliance, and threat detection. This entire environment is: β Free or extremely low-cost
β Fully repeatable using IaC
β Destroyable in minutes
β Perfect for portfolios No Terraform account required.
No installations on your laptop required. You can write Terraform locally in VS Code, but ARM64 Windows devices donβt have native Terraform binaries. So the recommended approach is: CloudShell gives you: This combo gives you real-world DevSecOps workflow. If Terraform is missing: Recommended real-world folder structure: Below is the full configuration needed to deploy a secure AWS baseline. Note: I added notes to describe what each section should complete. Your environment is now ready. Preview what Terraform will create: Apply the infrastructure: Terraform will deploy: AWS Console β VPC β Your VPCs β Look for the name tf-security-demo-vpc β Versioning enabled
β AES-256 encryption
β Public Access Block = ON AWS Console β CloudTrail β Trails β Your trail should be active Always run this to avoid costs: Confirm with yes when prompted. Install Terraform manually inside CloudShell after running: Ensure your IAM user has: This is professional-grade IaC experienceβperfect for: Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
terraform version Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
terraform version CODE_BLOCK:
terraform version COMMAND_BLOCK:
sudo yum install -y wget unzip TERRAFORM_VERSION="1.14.2" //At the time of this project **1.14.2** was the most recent version of terraform. wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip sudo mv terraform /usr/local/bin/ terraform version Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
sudo yum install -y wget unzip TERRAFORM_VERSION="1.14.2" //At the time of this project **1.14.2** was the most recent version of terraform. wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip sudo mv terraform /usr/local/bin/ terraform version COMMAND_BLOCK:
sudo yum install -y wget unzip TERRAFORM_VERSION="1.14.2" //At the time of this project **1.14.2** was the most recent version of terraform. wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip sudo mv terraform /usr/local/bin/ terraform version CODE_BLOCK:
mkdir terraform-security-module
cd terraform-security-module Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
mkdir terraform-security-module
cd terraform-security-module CODE_BLOCK:
mkdir terraform-security-module
cd terraform-security-module CODE_BLOCK:
terraform-security-module/
β
βββ main.tf
βββ variables.tf
βββ outputs.tf
βββ versions.tf
βββ .gitignore Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
terraform-security-module/
β
βββ main.tf
βββ variables.tf
βββ outputs.tf
βββ versions.tf
βββ .gitignore CODE_BLOCK:
terraform-security-module/
β
βββ main.tf
βββ variables.tf
βββ outputs.tf
βββ versions.tf
βββ .gitignore CODE_BLOCK:
.terraform/
terraform.tfstate
terraform.tfstate.backup
*.backup Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
.terraform/
terraform.tfstate
terraform.tfstate.backup
*.backup CODE_BLOCK:
.terraform/
terraform.tfstate
terraform.tfstate.backup
*.backup COMMAND_BLOCK:
terraform { required_version = ">= 1.5.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }
} provider "aws" { region = var.aws_region
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
terraform { required_version = ">= 1.5.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }
} provider "aws" { region = var.aws_region
} COMMAND_BLOCK:
terraform { required_version = ">= 1.5.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }
} provider "aws" { region = var.aws_region
} CODE_BLOCK:
variable "aws_region" { description = "AWS region to deploy into" type = string default = "us-east-1"
} variable "project_name" { description = "Prefix for all resource names" type = string default = "tf-security-demo"
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
variable "aws_region" { description = "AWS region to deploy into" type = string default = "us-east-1"
} variable "project_name" { description = "Prefix for all resource names" type = string default = "tf-security-demo"
} CODE_BLOCK:
variable "aws_region" { description = "AWS region to deploy into" type = string default = "us-east-1"
} variable "project_name" { description = "Prefix for all resource names" type = string default = "tf-security-demo"
} CODE_BLOCK:
//Create a basic VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Name = "${var.project_name}-vpc" Environment = "lab" ManagedBy = "terraform" }
} //Generate Unique Suffix to Avoid Bucket Name Conflicts resource "random_id" "suffix" { byte_length = 4
} //Create the Bucket resource "aws_s3_bucket" "cloudtrail_logs" { bucket = "${var.project_name}-cloudtrail-logs-${random_id.suffix.hex}" tags = { Name = "${var.project_name}-cloudtrail-logs" Environment = "lab" ManagedBy = "terraform" }
} //Block All Public Access resource "aws_s3_bucket_public_access_block" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true
} //Enable Versioning resource "aws_s3_bucket_versioning" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id versioning_configuration { status = "Enabled" }
} //Enable Encryption (SSE-S3) resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } }
} //Bucket Policy for CloudTrail data "aws_caller_identity" "current" {} resource "aws_s3_bucket_policy" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AWSCloudTrailAclCheck" Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "s3:GetBucketAcl" Resource = aws_s3_bucket.cloudtrail_logs.arn }, { Sid = "AWSCloudTrailWrite" Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "s3:PutObject" Resource = "${aws_s3_bucket.cloudtrail_logs.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*" Condition = { StringEquals = { "s3:x-amz-acl" = "bucket-owner-full-control" } } } ] })
} //Create a CloudTrail Trail resource "aws_cloudtrail" "main" { name = "${var.project_name}-trail" s3_bucket_name = aws_s3_bucket.cloudtrail_logs.id include_global_service_events = true is_multi_region_trail = true enable_logging = true event_selector { read_write_type = "All" include_management_events = true } depends_on = [ aws_s3_bucket_policy.cloudtrail_logs, aws_s3_bucket_public_access_block.cloudtrail_logs ] tags = { Name = "${var.project_name}-trail" Environment = "lab" ManagedBy = "terraform" }
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
//Create a basic VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Name = "${var.project_name}-vpc" Environment = "lab" ManagedBy = "terraform" }
} //Generate Unique Suffix to Avoid Bucket Name Conflicts resource "random_id" "suffix" { byte_length = 4
} //Create the Bucket resource "aws_s3_bucket" "cloudtrail_logs" { bucket = "${var.project_name}-cloudtrail-logs-${random_id.suffix.hex}" tags = { Name = "${var.project_name}-cloudtrail-logs" Environment = "lab" ManagedBy = "terraform" }
} //Block All Public Access resource "aws_s3_bucket_public_access_block" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true
} //Enable Versioning resource "aws_s3_bucket_versioning" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id versioning_configuration { status = "Enabled" }
} //Enable Encryption (SSE-S3) resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } }
} //Bucket Policy for CloudTrail data "aws_caller_identity" "current" {} resource "aws_s3_bucket_policy" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AWSCloudTrailAclCheck" Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "s3:GetBucketAcl" Resource = aws_s3_bucket.cloudtrail_logs.arn }, { Sid = "AWSCloudTrailWrite" Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "s3:PutObject" Resource = "${aws_s3_bucket.cloudtrail_logs.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*" Condition = { StringEquals = { "s3:x-amz-acl" = "bucket-owner-full-control" } } } ] })
} //Create a CloudTrail Trail resource "aws_cloudtrail" "main" { name = "${var.project_name}-trail" s3_bucket_name = aws_s3_bucket.cloudtrail_logs.id include_global_service_events = true is_multi_region_trail = true enable_logging = true event_selector { read_write_type = "All" include_management_events = true } depends_on = [ aws_s3_bucket_policy.cloudtrail_logs, aws_s3_bucket_public_access_block.cloudtrail_logs ] tags = { Name = "${var.project_name}-trail" Environment = "lab" ManagedBy = "terraform" }
} CODE_BLOCK:
//Create a basic VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Name = "${var.project_name}-vpc" Environment = "lab" ManagedBy = "terraform" }
} //Generate Unique Suffix to Avoid Bucket Name Conflicts resource "random_id" "suffix" { byte_length = 4
} //Create the Bucket resource "aws_s3_bucket" "cloudtrail_logs" { bucket = "${var.project_name}-cloudtrail-logs-${random_id.suffix.hex}" tags = { Name = "${var.project_name}-cloudtrail-logs" Environment = "lab" ManagedBy = "terraform" }
} //Block All Public Access resource "aws_s3_bucket_public_access_block" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true
} //Enable Versioning resource "aws_s3_bucket_versioning" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id versioning_configuration { status = "Enabled" }
} //Enable Encryption (SSE-S3) resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } }
} //Bucket Policy for CloudTrail data "aws_caller_identity" "current" {} resource "aws_s3_bucket_policy" "cloudtrail_logs" { bucket = aws_s3_bucket.cloudtrail_logs.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AWSCloudTrailAclCheck" Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "s3:GetBucketAcl" Resource = aws_s3_bucket.cloudtrail_logs.arn }, { Sid = "AWSCloudTrailWrite" Effect = "Allow" Principal = { Service = "cloudtrail.amazonaws.com" } Action = "s3:PutObject" Resource = "${aws_s3_bucket.cloudtrail_logs.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*" Condition = { StringEquals = { "s3:x-amz-acl" = "bucket-owner-full-control" } } } ] })
} //Create a CloudTrail Trail resource "aws_cloudtrail" "main" { name = "${var.project_name}-trail" s3_bucket_name = aws_s3_bucket.cloudtrail_logs.id include_global_service_events = true is_multi_region_trail = true enable_logging = true event_selector { read_write_type = "All" include_management_events = true } depends_on = [ aws_s3_bucket_policy.cloudtrail_logs, aws_s3_bucket_public_access_block.cloudtrail_logs ] tags = { Name = "${var.project_name}-trail" Environment = "lab" ManagedBy = "terraform" }
} CODE_BLOCK:
output "vpc_id" { value = aws_vpc.main.id
} output "cloudtrail_logs_bucket" { value = aws_s3_bucket.cloudtrail_logs.bucket
} output "cloudtrail_trail_name" { value = aws_cloudtrail.main.name
} output "region" { value = var.aws_region
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
output "vpc_id" { value = aws_vpc.main.id
} output "cloudtrail_logs_bucket" { value = aws_s3_bucket.cloudtrail_logs.bucket
} output "cloudtrail_trail_name" { value = aws_cloudtrail.main.name
} output "region" { value = var.aws_region
} CODE_BLOCK:
output "vpc_id" { value = aws_vpc.main.id
} output "cloudtrail_logs_bucket" { value = aws_s3_bucket.cloudtrail_logs.bucket
} output "cloudtrail_trail_name" { value = aws_cloudtrail.main.name
} output "region" { value = var.aws_region
} CODE_BLOCK:
terraform init
terraform validate Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
terraform init
terraform validate CODE_BLOCK:
terraform init
terraform validate CODE_BLOCK:
terraform plan -out tfplan Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
terraform plan -out tfplan CODE_BLOCK:
terraform plan -out tfplan CODE_BLOCK:
terraform apply tfplan Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
terraform apply tfplan CODE_BLOCK:
terraform apply tfplan CODE_BLOCK:
terraform destroy Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
terraform destroy CODE_BLOCK:
terraform destroy CODE_BLOCK:
uname -m Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
resource "random_id" "suffix" { byte_length = 4
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
resource "random_id" "suffix" { byte_length = 4
} CODE_BLOCK:
resource "random_id" "suffix" { byte_length = 4
} - Identity hardening and MFA enforcement
- IAM governance and access reviews
- Continuous monitoring of cloud resources
- Misconfiguration detection and drift analysis
- Log analysis, audit readiness, and evidence gathering
- Infrastructure-as-Code (IaC) security baselines and guardrails
- Guard rails at scale using AWS Organizations + Service Control Policies (SCPs)
- Threat detection, anomaly monitoring, and incident triage - Secure by default
- Consistent across deployments
- Auditable and reviewable
- Resistant to configuration drift - A baseline VPC configuration
- A secure S3 bucket with encryption, versioning, and public access blocked
- CloudTrail logging enforced through code - Version-controlled
- Peer-reviewed
- Re-deployed on demand
- Automatically restored if misconfigured - Introduction
- Why This Project Matters
- Prerequisites
- Using VS Code vs CloudShell
- Setting Up AWS CloudShell
- Creating Your Terraform Project Structure
- Writing Terraform Configuration Files
- Initializing Terraform
- Planning and Applying
- Verifying the Deployment
- Cleaning Up Resources
- Troubleshooting Tips
- Final Thoughts - A VPC (Virtual Private Cloud)
- A Secure S3 Bucket for CloudTrail logs
- A CloudTrail Trail for auditing AWS activity - AWS account
- Basic familiarity with AWS Console
- A browser (for CloudShell)
- Optional: VS Code for code editing - Linux environment
- Pre-installed Terraform (or installable)
- Preconfigured IAM authentication
- Safe sandbox - Log in to the AWS Console
- Click the CloudShell terminal icon in the upper-right corner
- CloudShell opens a terminal inside AWS
- Check if Terraform is installed: - Run uname -m to detect architecture
- Install Terraform using the latest ARM64 or AMD64 Linux binary - Public access block
- Bucket policy - A new secure VPC
- A CloudTrail-ready S3 bucket
- Encryption + versioning + public access blocks
- A CloudTrail trail - S3 bucket creation permissions
- CloudTrail permissions
- VPC permissions - A secure AWS logging architecture
- Using Terraform
- Inside AWS CloudShell
- Without installing anything locally - Cloud Security
- Portfolio projects
how-totutorialguidedev.toailinuxservershelldnsroutingsslterraformgit