• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Infrastructure as Code: Provisioning Secure WooCommerce Clusters on AWS Using Terraform

Infrastructure as Code: Provisioning Secure WooCommerce Clusters on AWS Using Terraform

Terraform Project Structure and Provider Configuration

We’ll begin by establishing a robust Terraform project structure. This organization is crucial for managing complexity, especially as our infrastructure grows. Our root module will house the main configuration files, including the provider block and backend configuration.

The AWS provider configuration specifies the region and any necessary authentication details. For production environments, it’s highly recommended to use IAM roles or environment variables for credentials rather than hardcoding them. We’ll also configure the Terraform backend to store our state file remotely, typically in an S3 bucket, which is essential for collaboration and state locking.

`main.tf`

# main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  backend "s3" {
    bucket         = "your-terraform-state-bucket-name" # Replace with your S3 bucket name
    key            = "woocommerce/prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "your-terraform-lock-table" # Replace with your DynamoDB table name for state locking
    encrypt        = true
  }
}

provider "aws" {
  region = "us-east-1" # Or your preferred AWS region
  # For production, consider using IAM roles or environment variables for credentials
  # profile = "your-aws-profile"
}

# Variables will be defined in variables.tf
# Resources will be defined in respective .tf files (e.g., network.tf, compute.tf, rds.tf)

Networking Foundation: VPC, Subnets, and Security Groups

A secure and well-architected network is paramount for any production application. We’ll define a Virtual Private Cloud (VPC) with public and private subnets across multiple Availability Zones for high availability. Essential security groups will be created to control inbound and outbound traffic, ensuring only necessary ports are open.

`network.tf`

# network.tf

# VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "woocommerce-vpc"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "woocommerce-igw"
  }
}

# Public Subnets (for NAT Gateways and potentially load balancers)
resource "aws_subnet" "public" {
  count             = 2 # Number of Availability Zones
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.${count.index * 64}/26"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true # For simplicity, though often managed by NAT Gateway

  tags = {
    Name = "woocommerce-public-subnet-${count.index}"
  }
}

# Private Subnets (for application servers and RDS)
resource "aws_subnet" "private" {
  count             = 2 # Number of Availability Zones
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.${count.index * 64}/26"
  availability_zone = data.aws_availability_zones.available.names[count.index]

  tags = {
    Name = "woocommerce-private-subnet-${count.index}"
  }
}

# NAT Gateways (for outbound internet access from private subnets)
resource "aws_eip" "nat" {
  count = length(aws_subnet.public)
  domain = "vpc"
}

resource "aws_nat_gateway" "nat" {
  count         = length(aws_subnet.public)
  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id
  depends_on    = [aws_internet_gateway.gw]

  tags = {
    Name = "woocommerce-nat-gw-${count.index}"
  }
}

# Public Route Table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "woocommerce-public-rt"
  }
}

resource "aws_route_table_association" "public" {
  count          = length(aws_subnet.public)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

# Private Route Table
resource "aws_route_table" "private" {
  count  = length(aws_subnet.private)
  vpc_id = aws_vpc.main.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat[count.index].id
  }

  tags = {
    Name = "woocommerce-private-rt-${count.index}"
  }
}

resource "aws_route_table_association" "private" {
  count          = length(aws_subnet.private)
  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[count.index].id
}

# Data source for Availability Zones
data "aws_availability_zones" "available" {
  state = "available"
}

# Security Group for Load Balancer
resource "aws_security_group" "lb" {
  name        = "woocommerce-lb-sg"
  description = "Allow HTTP and HTTPS inbound traffic"
  vpc_id      = aws_vpc.main.id

  ingress {
    description = "HTTP from anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTPS from anywhere"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "woocommerce-lb-sg"
  }
}

# Security Group for Application Servers (EC2 instances)
resource "aws_security_group" "app" {
  name        = "woocommerce-app-sg"
  description = "Allow traffic from Load Balancer and SSH from trusted IPs"
  vpc_id      = aws_vpc.main.id

  ingress {
    description     = "HTTP from LB"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.lb.id]
  }

  ingress {
    description     = "HTTPS from LB"
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    security_groups = [aws_security_group.lb.id]
  }

  # SSH access from a specific trusted IP range (e.g., your office or bastion host)
  ingress {
    description = "SSH from trusted IP"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["YOUR_TRUSTED_IP_ADDRESS/32"] # IMPORTANT: Replace with your actual trusted IP
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "woocommerce-app-sg"
  }
}

# Security Group for RDS Database
resource "aws_security_group" "rds" {
  name        = "woocommerce-rds-sg"
  description = "Allow MySQL traffic from Application Servers"
  vpc_id      = aws_vpc.main.id

  ingress {
    description     = "MySQL from App Servers"
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "woocommerce-rds-sg"
  }
}

Database Tier: RDS for MySQL

A managed relational database service like AWS RDS is crucial for reliability and scalability. We’ll provision an RDS instance for MySQL, placing it in private subnets for enhanced security. Parameter groups and option groups can be customized for performance tuning and specific MySQL features.

`rds.tf`

# rds.tf

# RDS Subnet Group
resource "aws_db_subnet_group" "woocommerce" {
  name       = "woocommerce-db-subnet-group"
  subnet_ids = [for subnet in aws_subnet.private : subnet.id] # Use private subnets

  tags = {
    Name = "woocommerce-db-subnet-group"
  }
}

# RDS Instance
resource "aws_db_instance" "woocommerce" {
  identifier           = "woocommerce-db"
  engine               = "mysql"
  engine_version       = "8.0" # Specify your desired MySQL version
  instance_class       = "db.t3.medium" # Choose an appropriate instance class
  allocated_storage    = 50 # GB
  storage_type         = "gp2" # General Purpose SSD

  db_name              = "woocommerce_db"
  username             = var.db_username # Defined in variables.tf
  password             = var.db_password # Defined in variables.tf
  parameter_group_name = "default.mysql8.0" # Or a custom parameter group
  # option_group_name    = "default:mysql-8-0" # Or a custom option group

  db_subnet_group_name   = aws_db_subnet_group.woocommerce.name
  vpc_security_group_ids = [aws_security_group.rds.id]

  skip_final_snapshot      = true # Set to false for production backups
  publicly_accessible      = false # Crucial for security
  multi_az                 = true # For high availability
  storage_encrypted        = true # Enable encryption at rest
  backup_retention_period  = 7 # Days
  # kms_key_id             = "arn:aws:kms:..." # Specify if using a custom KMS key

  tags = {
    Name = "woocommerce-db-instance"
  }
}

Application Tier: EC2 Instances and Auto Scaling

We’ll deploy EC2 instances to host our WooCommerce application. An Auto Scaling Group (ASG) will manage these instances, ensuring that the application scales automatically based on demand and maintains a desired capacity. A Launch Template defines the configuration for new instances, including the AMI, instance type, user data for bootstrapping, and IAM instance profile.

`compute.tf`

# compute.tf

# IAM Role for EC2 Instances
resource "aws_iam_role" "ec2_role" {
  name = "woocommerce-ec2-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
}

# IAM Instance Profile
resource "aws_iam_instance_profile" "ec2_profile" {
  name = "woocommerce-ec2-profile"
  role = aws_iam_role.ec2_role.name
}

# User Data for EC2 Instances (Bootstrapping)
# This script will install Apache, PHP, MySQL client, and clone your WooCommerce application.
# It also configures Apache to serve your site and sets up basic security.
locals {
  user_data = <<-EOT
    #!/bin/bash
    exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
    echo "Starting user data script..."

    # Update packages and install necessary software
    sudo apt-get update -y
    sudo apt-get upgrade -y
    sudo apt-get install -y apache2 php libapache2-mod-php php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip unzip wget

    # Configure Apache
    sudo a2enmod rewrite
    sudo systemctl restart apache2

    # Download and extract WooCommerce (replace with your actual deployment method)
    # For production, consider using a CI/CD pipeline to deploy code.
    # This is a placeholder for demonstration.
    cd /var/www/html
    sudo wget https://wordpress.org/latest.zip -O wordpress.zip
    sudo unzip wordpress.zip
    sudo mv wordpress/* .
    sudo rm wordpress.zip
    sudo chown -R www-data:www-data /var/www/html

    # Configure WordPress/WooCommerce (replace with your actual configuration)
    # This would typically involve wp-cli or manual configuration files.
    # For simplicity, we'll assume a basic setup.
    # You'll need to create wp-config.php with your database credentials.
    # Example:
    # sudo cp wp-config-sample.php wp-config.php
    # sudo sed -i "s/database_name_here/woocommerce_db/" wp-config.php
    # sudo sed -i "s/username_here/var.db_username/" wp-config.php
    # sudo sed -i "s/password_here/var.db_password/" wp-config.php
    # sudo sed -i "s/localhost/var.db_endpoint/" wp-config.php

    echo "User data script finished."
  EOT
}

# Launch Template
resource "aws_launch_template" "woocommerce" {
  name_prefix   = "woocommerce-lt-"
  image_id      = "ami-0abcdef1234567890" # Replace with a suitable Amazon Linux 2 or Ubuntu AMI ID
  instance_type = "t3.medium" # Choose an appropriate instance type

  iam_instance_profile {
    arn = aws_iam_instance_profile.ec2_profile.arn
  }

  network_interfaces {
    associate_public_ip_address = false # Instances will be in private subnets
    security_groups             = [aws_security_group.app.id]
  }

  user_data = base64encode(local.user_data)

  # Add SSH key for manual access if needed (consider bastion host for production)
  # key_name = "your-ssh-key-pair-name"

  tags {
    Name = "woocommerce-app-instance"
  }

  lifecycle {
    create_before_destroy = true
  }
}

# Auto Scaling Group
resource "aws_autoscaling_group" "woocommerce" {
  name                      = "woocommerce-asg"
  desired_capacity          = 2
  min_size                  = 1
  max_size                  = 5
  vpc_zone_identifier       = [for subnet in aws_subnet.private : subnet.id] # Deploy in private subnets
  launch_template {
    id      = aws_launch_template.woocommerce.id
    version = "$Latest"
  }

  # Health check configuration
  health_check_type         = "ELB" # Or "EC2" if not using ELB
  health_check_grace_period = 300

  # Attach to target group (defined in load_balancer.tf)
  target_group_arns = [aws_lb_target_group.woocommerce.arn]

  # Scaling policies (optional, but recommended)
  # resource "aws_autoscaling_policy" "scale_up" {
  #   name                   = "woocommerce-scale-up"
  #   scaling_adjustment     = 1
  #   autoscaling_group_name = aws_autoscaling_group.woocommerce.name
  #   adjustment_type        = "ChangeInCapacity"
  #   cooldown               = 300
  # }

  # resource "aws_autoscaling_policy" "scale_down" {
  #   name                   = "woocommerce-scale-down"
  #   scaling_adjustment     = -1
  #   autoscaling_group_name = aws_autoscaling_group.woocommerce.name
  #   adjustment_type        = "ChangeInCapacity"
  #   cooldown               = 300
  # }

  # Example CloudWatch alarm for scaling up based on CPU utilization
  # resource "aws_cloudwatch_metric_alarm" "cpu_high" {
  #   alarm_name          = "woocommerce-cpu-high"
  #   comparison_operator = "GreaterThanOrEqualToThreshold"
  #   evaluation_periods  = "2"
  #   metric_name         = "CPUUtilization"
  #   namespace           = "AWS/EC2"
  #   period              = "120"
  #   statistic           = "Average"
  #   threshold           = "70"
  #   alarm_description   = "This metric monitors cpu high for woocommerce ASG"
  #   alarm_actions       = [aws_autoscaling_policy.scale_up.id]
  #   dimensions = {
  #     AutoScalingGroupName = aws_autoscaling_group.woocommerce.name
  #   }
  # }

  tags = [
    {
      key                 = "Name"
      value               = "woocommerce-app-instance"
      propagate_at_launch = true
    }
  ]
}

Load Balancing and SSL Termination

An Application Load Balancer (ALB) distributes incoming traffic across the EC2 instances in the Auto Scaling Group. We’ll configure listeners for HTTP and HTTPS, with SSL termination handled at the ALB. This offloads SSL processing from the application servers and simplifies certificate management.

`load_balancer.tf`

# load_balancer.tf

# Application Load Balancer
resource "aws_lb" "woocommerce" {
  name               = "woocommerce-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.lb.id]
  subnets            = [for subnet in aws_subnet.public : subnet.id] # Public subnets for ALB

  enable_deletion_protection = false # Set to true for production

  tags = {
    Name = "woocommerce-alb"
  }
}

# ALB Target Group
resource "aws_lb_target_group" "woocommerce" {
  name     = "woocommerce-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id
  target_type = "instance"

  health_check {
    enabled             = true
    interval            = 30
    path                = "/" # Or a specific health check endpoint
    port                = "traffic-port"
    protocol            = "HTTP"
    matcher             = "200" # Expect HTTP 200 OK
    timeout             = 5
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }

  tags = {
    Name = "woocommerce-tg"
  }
}

# Listener for HTTP (redirect to HTTPS)
resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.woocommerce.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type = "redirect"
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

# Listener for HTTPS
resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.woocommerce.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08" # Choose an appropriate SSL policy
  certificate_arn   = "arn:aws:acm:us-east-1:123456789012:certificate/your-certificate-id" # Replace with your ACM certificate ARN

  default_action {
    target_group_arn = aws_lb_target_group.woocommerce.arn
  }
}

Variables and Outputs

Centralizing configuration values in variables makes the Terraform code reusable and easier to manage. Outputs provide useful information about the deployed infrastructure, such as the ALB DNS name.

`variables.tf`

# variables.tf

variable "db_username" {
  description = "Username for the RDS database"
  type        = string
  sensitive   = true # Mark as sensitive to prevent exposure in logs
}

variable "db_password" {
  description = "Password for the RDS database"
  type        = string
  sensitive   = true
}

variable "aws_region" {
  description = "AWS region to deploy resources"
  type        = string
  default     = "us-east-1"
}

variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "public_subnet_cidrs" {
  description = "CIDR blocks for public subnets"
  type        = list(string)
  default     = ["10.0.1.0/26", "10.0.1.64/26"]
}

variable "private_subnet_cidrs" {
  description = "CIDR blocks for private subnets"
  type        = list(string)
  default     = ["10.0.2.0/26", "10.0.2.64/26"]
}

`outputs.tf`

# outputs.tf

output "alb_dns_name" {
  description = "The DNS name of the Application Load Balancer"
  value       = aws_lb.woocommerce.dns_name
}

output "alb_zone_id" {
  description = "The Zone ID of the Application Load Balancer"
  value       = aws_lb.woocommerce.zone_id
}

output "rds_endpoint" {
  description = "The endpoint of the RDS database instance"
  value       = aws_db_instance.woocommerce.endpoint
}

output "rds_port" {
  description = "The port of the RDS database instance"
  value       = aws_db_instance.woocommerce.port
}

Deployment and Management Workflow

With the Terraform configuration in place, the deployment process is straightforward:

  • Initialize Terraform: Run terraform init to download provider plugins and configure the backend.
  • Plan the Infrastructure: Execute terraform plan to review the changes Terraform will make to your AWS environment. This is a critical step to catch any unintended modifications.
  • Apply the Configuration: Run terraform apply to provision the resources. You will be prompted to enter sensitive variables like the database username and password.
  • Destroy Resources: When no longer needed, use terraform destroy to tear down all provisioned resources, preventing unnecessary costs.

For managing sensitive variables like database credentials, consider using environment variables (e.g., TF_VAR_db_username, TF_VAR_db_password) or a secrets management system like AWS Secrets Manager integrated with Terraform.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Step-by-Step Guide to building a custom dynamic lead collector block for Gutenberg using Alpine.js lightweight states
  • WordPress Development Recipe: Implementing a secure lock mechanism for multi-worker Cron tasks with Metadata API (add_post_meta)
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Shopify headless API handlers
  • Debugging and Resolving complex WooCommerce hook execution loops issues during heavy concurrent database traffic
  • WordPress Development Recipe: Real-time custom event triggers using WebSockets and Shortcode API

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (47)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (143)
  • WordPress Plugin Development (158)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Step-by-Step Guide to building a custom dynamic lead collector block for Gutenberg using Alpine.js lightweight states
  • WordPress Development Recipe: Implementing a secure lock mechanism for multi-worker Cron tasks with Metadata API (add_post_meta)
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Shopify headless API handlers

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala