Skip to content

szabodorka/terraform-wordpress-aws

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 

Repository files navigation

WordPress on AWS with Terraform

This project deploys a production-ready WordPress application on AWS using Terraform. It provisions a WordPress EC2 instance in a private subnet, accessible publicly via an Application Load Balancer (ALB), with an RDS MySQL database and an S3 bucket for media offloading. A bastion host (toggleable via a boolean variable) secures SSH access to the EC2 instance and RDS database administration. Terraform state is managed remotely in an S3 bucket with DynamoDB locking for team collaboration.

Tech Stack

  • Terraform: Infrastructure as code
  • AWS: EC2, RDS, S3, ALB, IAM, VPC
  • Docker: Runs WordPress on the EC2 instance
  • Ubuntu: EC2 operating system
  • MySQL: Database via RDS

Prerequisites

  • Terraform CLI installed (terraform command available).
  • AWS CLI installed and configured with valid credentials (~/.aws/credentials).
  • An AWS account with permissions to create S3 buckets, DynamoDB tables, EC2 instances, ALB, RDS instances and IAM roles/policies.
  • SSH key pair uploaded to AWS

Project Structure

  • backend-setup/: Terraform configuration to create the S3 bucket and DynamoDB table for remote state storage and locking.

    • providers.tf: AWS provider configuration.
    • main.tf: Defines the S3 bucket (with versioning and encryption), DynamoDB table.
  • main-project/: Terraform configuration for the WordPress application.

    • providers.tf: AWS provider configuration.
    • versions.tf: Terraform and provider version configuration.
    • backend.tf: Configures the remote S3 backend (added after backend setup).
    • main.tf: Orchestrates VPC, EC2, RDS, S3, and ALB modules.
    • variables.tf: (Optional) Defines variables (e.g., enable_bastion, ips, db_username).
    • terraform.tfvars: (Optional) Sets variable values.
    • modules/vpc/: Custom VPC with public and private subnets.
    • modules/ec2/: EC2 instance for WordPress (private subnet) and optional bastion host (public subnet).
    • modules/rds/: MySQL RDS instance in private subnet.
    • modules/s3/: S3 bucket for media offloading with IAM roles.
    • modules/alb/: Application Load Balancer for public WordPress access.

Setup Instructions

Step 1: Create the Backend Infrastructure

The backend infrastructure (S3 bucket and DynamoDB table) must be created first and managed with a local state.

Navigate to the backend-setup/ directory:

cd backend-setup/

Initialize Terraform:

terraform init

Review the plan:

terraform plan

Apply the configuration to create the S3 bucket and DynamoDB table:

terraform apply

Confirm by typing yes. This creates the S3 bucket (sd-tfstate-bucket) and DynamoDB table (sd-terraform-state-lock). The state is stored locally in terraform.tfstate.

Step 2: Initialize the Main Project (Locally First)

The main project will be initialized locally before configuring the remote backend.

Navigate to the main-project/ directory:

cd main-project/

Ensure there is no backend.tf file yet to avoid remote backend errors.

Initialize Terraform:

terraform init

(Optional) Review the plan (do not apply yet):

terraform plan

Step 3: Migrate State to Remote Backend

After creating the backend infrastructure, configure the main project to use the remote S3 backend with DynamoDB locking.

Add the backend.tf file to main-project/ with the following content:

terraform {
backend "s3" {
bucket = "sd-tfstate-bucket"
key = "main-project/terraform.tfstate"
region = "eu-central-1" # Replace with your AWS region
dynamodb_table = "sd-terraform-state-lock"
encrypt = true
}
}

Re-initialize Terraform to migrate the local state to the S3 bucket:

terraform init -migrate-state

Confirm the migration by typing yes. This moves the local state to the S3 bucket and enables DynamoDB locking.

Step 4: Deploy the Main Project

With the remote backend configured, deploy the WordPress infrastructure.

Review the plan:

terraform plan

Apply the configuration to create the EC2 instance, RDS MySQL database, S3 media bucket, and IAM roles:

terraform apply

Confirm by typing yes. The state is now stored in the S3 bucket, with locking managed by DynamoDB.

Access WordPress via the ALB URL (http://<lb_dns_name>) from the output lb_url.

Step 5: Access Resources as Administrator

  1. SSH to WordPress EC2:
  • If enable_bastion = true (default):
ssh -A -i <private-key>.pem ubuntu@<bastion_public_ip> -J ubuntu@<wordpress_private_ip>
  • If enable_bastion = false (set in terraform.tfvars): Requires NAT gateway or public IP for WordPress EC2 (not recommended for production).
ssh -i <private-key>.pem ubuntu@<wordpress_private_ip>

Find <bastion_public_ip> in module.ec2.bastion_public_ip and <wordpress_private_ip> via AWS console.

  1. RDS Admin Access:

Create SSH tunnel via bastion:

ssh -i dorka_key.pem -L 3306:<rds_endpoint>:3306 ubuntu@<bastion_public_ip> -N

Connect to RDS:

mysql -h 127.0.0.1 -u <db_username> -p<db_password> <db_name>

<rds_endpoint> is from module.rds.rds_endpoint.

Step 6: Configure WordPress

Access the WordPress site using ALB URL.

Complete the WordPress setup wizard in the browser, using the RDS database credentials (db_username, db_password, db_name) and the RDS endpoint (available in the AWS console or Terraform outputs).

Configure an S3-compatible WordPress plugin (e.g., WP Offload Media) to use the sd-wordpress-media-bucket for media storage. Use the EC2 instance profile for authentication.

Step 7: Cleanup

To destroy the infrastructure:

In the main-project/ directory, destroy the WordPress resources:

terraform destroy

Confirm by typing yes. This uses the remote state and DynamoDB locking.

In the backend-setup/ directory, destroy the backend infrastructure:

cd ../backend-setup/
terraform destroy

Confirm by typing yes.

Note: If you encounter a BucketNotEmpty error, manually empty the bucket using the AWS CLI:

aws s3api delete-objects \
 --bucket sd-tfstate-bucket \
 --delete "$(aws s3api list-object-versions --bucket sd-tfstate-bucket --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}')"
aws s3api delete-objects \
  --bucket sd-tfstate-bucket \
  --delete "$(aws s3api list-object-versions --bucket sd-tfstate-bucket --query='{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}')"

Then retry terraform destroy.

Notes

  • Security: The provided configurations are for demonstration. For production:
    • Avoid hardcoded credentials in user_data or Terraform variables; use AWS Secrets Manager.
    • Restrict the EC2 security group ingress (e.g., limit SSH to your IP).
    • Enable HTTPS with a certificate (e.g., AWS Certificate Manager or Let's Encrypt).
    • Use a custom VPC and private subnets for RDS instead of publicly_accessible = false.
  • RDS: The RDS instance is configured with skip_final_snapshot = true for simplicity. In production, enable snapshots.
  • S3: The sd-wordpress-media-bucket is used for media offloading. Ensure the WordPress plugin is configured with the correct bucket name and IAM role.
  • Region: Replace eu-central-1 with your desired AWS region in all configurations.

For issues, check the AWS credentials, region consistency, or Terraform error messages. Refer to the Terraform and AWS documentation for advanced configurations.

About

WordPress application on AWS using Terraform. The project provisions a VPC with public/private subnets, bastion host, NAT gateway, and ALB; an EC2 instance running WordPress in a Docker container; an RDS MySQL database and an S3 bucket for media offloading, with IAM roles for secure access. Remote state managed in S3 with DynamoDB locking.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages