DEV Community

LaTerral Williams
LaTerral Williams

Posted on

πŸ”’ Building a Secure AWS Environment with Terraform + AWS CloudShell

⭐ Why I Built This Project

(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:

  • 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

Each project is designed to reflect real-world responsibilities, not just theoretical learning.


πŸ“Œ Project Sequence

πŸ‘‰ 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


🧱 Why This Project Matters

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:

  • Secure by default
  • Consistent across deployments
  • Auditable and reviewable
  • Resistant to configuration drift

This project focuses on using Terraform to define and deploy a secure AWS foundation, including:

  • A baseline VPC configuration
  • A secure S3 bucket with encryption, versioning, and public access blocked
  • CloudTrail logging enforced through code

Instead of manually clicking through the AWS console, this project demonstrates how security controls can be:

  • Version-controlled
  • Peer-reviewed
  • Re-deployed on demand
  • Automatically restored if misconfigured

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.


πŸ“š Table of Contents

  1. Introduction
  2. Why This Project Matters
  3. Prerequisites
  4. Using VS Code vs CloudShell
  5. Setting Up AWS CloudShell
  6. Creating Your Terraform Project Structure
  7. Writing Terraform Configuration Files
  8. Initializing Terraform
  9. Planning and Applying
  10. Verifying the Deployment
  11. Cleaning Up Resources
  12. Troubleshooting Tips
  13. Final Thoughts

🌟 Introduction

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.


πŸ” Why This Project Matters

You will create three essential security components using Terraform:

  • A VPC (Virtual Private Cloud)
  • A Secure S3 Bucket for CloudTrail logs
  • A CloudTrail Trail for auditing AWS activity

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


🧰 Prerequisites

  • AWS account
  • Basic familiarity with AWS Console
  • A browser (for CloudShell)
  • Optional: VS Code for code editing

No Terraform account required.

No installations on your laptop required.


πŸ’» Using VS Code vs AWS CloudShell

You can write Terraform locally in VS Code, but ARM64 Windows devices don’t have native Terraform binaries.

So the recommended approach is:

πŸ₯‡ Use VS Code for editing

πŸ₯‡ Use AWS CloudShell for running Terraform

CloudShell gives you:

  • Linux environment
  • Pre-installed Terraform (or installable)
  • Preconfigured IAM authentication
  • Safe sandbox

This combo gives you real-world DevSecOps workflow.


☁️ Setting Up AWS CloudShell

  1. Log in to the AWS Console
  2. Click the CloudShell terminal icon in the upper-right corner
  3. CloudShell opens a terminal inside AWS
  4. Check if Terraform is installed:
terraform version
Enter fullscreen mode Exit fullscreen mode

If Terraform is missing:

  • Run uname -m to detect architecture
  • Install Terraform using the latest ARM64 or AMD64 Linux binary
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

πŸ“ Creating Your Terraform Project Structure

Inside CloudShell:

mkdir terraform-security-module
cd terraform-security-module
Enter fullscreen mode Exit fullscreen mode

Recommended real-world folder structure:

terraform-security-module/
β”‚
β”œβ”€β”€ main.tf
β”œβ”€β”€ variables.tf
β”œβ”€β”€ outputs.tf
β”œβ”€β”€ versions.tf
└── .gitignore
Enter fullscreen mode Exit fullscreen mode

Add a .gitignore:

.terraform/
terraform.tfstate
terraform.tfstate.backup
*.backup
Enter fullscreen mode Exit fullscreen mode

πŸ›  Writing Terraform Configuration Files

Below is the full configuration needed to deploy a secure AWS baseline.


πŸ”Ή versions.tf

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

πŸ”Ή variables.tf

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

πŸ”Ή main.tf

Contains:

  • VPC
  • S3 bucket
  • Public access block
  • Versioning
  • Encryption
  • Bucket policy
  • CloudTrail

Note: I added notes to describe what each section should complete.

//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

πŸ”Ή outputs.tf

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

βš™οΈ Initializing Terraform in CloudShell

Run:

terraform init
terraform validate
Enter fullscreen mode Exit fullscreen mode

Your environment is now ready.


πŸš€ Planning and Applying

Preview what Terraform will create:

terraform plan -out tfplan
Enter fullscreen mode Exit fullscreen mode

Apply the infrastructure:

terraform apply tfplan
Enter fullscreen mode Exit fullscreen mode

Terraform will deploy:

  • A new secure VPC
  • A CloudTrail-ready S3 bucket
  • Encryption + versioning + public access blocks
  • A CloudTrail trail

πŸ” Verifying the Deployment

Check VPC

AWS Console β†’ VPC β†’ Your VPCs β†’ Look for the name tf-security-demo-vpc

Check S3

Look for:

βœ” Versioning enabled

βœ” AES-256 encryption

βœ” Public Access Block = ON

Check CloudTrail

AWS Console β†’ CloudTrail β†’ Trails β†’ Your trail should be active


🧹 Cleaning Up Resources

Always run this to avoid costs:

terraform destroy
Enter fullscreen mode Exit fullscreen mode

Confirm with yes when prompted.

CloudShell removes:

  • CloudTrail
  • S3 bucket
  • VPC

πŸ›  Troubleshooting Tips

❗ Terraform not found

Install Terraform manually inside CloudShell after running:

uname -m
Enter fullscreen mode Exit fullscreen mode

❗ Permission denied

Ensure your IAM user has:

  • S3 bucket creation permissions
  • CloudTrail permissions
  • VPC permissions

❗ S3 bucket name already exists

Use random suffix:

resource "random_id" "suffix" {
  byte_length = 4
}
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ Final Thoughts

You just built:

  • A secure AWS logging architecture
  • Using Terraform
  • Inside AWS CloudShell
  • Without installing anything locally

This is professional-grade IaC experienceβ€”perfect for:

  • Cloud Security
  • DevOps
  • SOC roles
  • Portfolio projects

Top comments (0)