├── .gitignore ├── README.md ├── main.tf └── modules └── latest ├── aws_ami.tf ├── main_script.tf ├── user_data.tpl ├── userdata_ubuntu.tpl ├── variables.tf └── versions.tf /.gitignore: -------------------------------------------------------------------------------- 1 | mykey-pair 2 | mykey-pair.pub 3 | .terraform 4 | .terraform.lock.hcl 5 | terraform.tfstate 6 | terraform.tfstate.backup 7 | user_data(exec).tpl 8 | banner.txt 9 | wordpress_tf.sh 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This terraform projects creates 2 | RDS databse with mysql 5.7 3 | #EC2 instance with wordpress pre installed and configured 4 | ---------------------------------------------------------------------- 5 | AWS FREE TRIAL friendly 6 | ---------------------------------------------------------------------- 7 | Change database entries ,regions and other variable in terraform.tfvars file 8 | And Database password in user.tfvars file 9 | ------------------------------------------------------------------------- 10 | This script is default for Ubuntu 11 | If you want to configure wordpress in AWS LINUX 2 then change IsUbuntu value to false in terraform.tfvars 12 | 13 | Make sure you have configured aws CLI in your local machine 14 | 15 | user_data.tf is script for LINUX 2 and userdata_ubuntu.tpl is for Ubuntu 16 | ---------------------------------------------------------------------------------------- 17 | ami-id will be imported using data.aws_ami 18 | -------------------------------------------------------------------------------- 19 |

Security:

20 |

EC2 will be launched in public subnet and RDS will be launched in private subnet

21 |

Only EC2 with defined security group can access RDS and RDS wont have internet access

22 |

Password for RDS will be used to create the resource, later it should be changed manually for security purposes. Terraform will ignore any changes in password 23 | 24 | 25 | <-----------------------------------------------------------------------------------------------------------------------> 26 | 27 |

Prerequisite

28 |

Before launching Terraform template, aws cli should be installed and configured with proper access key and secret key

29 |

Terraform should be installed in your local machine

30 |

Configure AWS CLI with aws configure if you havent configured already

31 | 32 | <------------------------------------------------------------------------------------------------------------------------> 33 | 34 |

STEPS:

35 | 36 |

Clone this repo using command git clone https://github.com/devbhusal/terraform-ec2-RDS-wordpress.git

37 |

Go to project folder cd terraform-ec2-RDS-wordpress

38 |

Initialize terraform terraform init

39 |

Change database and aws setting in terraform.tfvars file

40 |

Generate Key pair using ssh-keygen -f mykey-pair

41 |

View Plan using terraform plan

42 |

Apply the plan using terraform apply

43 | 44 |

After successfull provisioning of AWS Resources,Using remote-exec and private key, EC2 instance will be connected via SSH. Tail command will used to check prgress of Wordpress Installation. Once Installation is done ,You will be provided with Public Ip address of WebServer.

45 |

everything is Automatic. This will provision all needed aws resources and also build and start webserver using USERDATA

46 | 47 |

Destroy the resources terraform destroy

48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | 2 | 3 | module aws_wordpress { 4 | source = "./modules/latest" 5 | database_name = "wordpress_db" // database name 6 | database_user = "wordpress_user" //database username 7 | // Password here will be used to create master db user.It should be chnaged later 8 | database_password = "PassWord4-user" //password for user database 9 | shared_credentials_file = "~/.aws/credentials" //Access key and Secret key file location 10 | region = "ap-southeast-2" //sydney region 11 | IsUbuntu = true // true for ubuntu,false for linux 2 //boolean type 12 | // avaibility zone and their CIDR 13 | AZ1 = "ap-southeast-2a" // for EC2 14 | AZ2 = "ap-southeast-2b" //for RDS 15 | AZ3 = "ap-southeast-2c" //for RDS 16 | VPC_cidr = "10.0.0.0/16" // VPC CIDR 17 | subnet1_cidr = "10.0.1.0/24" // Public Subnet for EC2 18 | subnet2_cidr = "10.0.2.0/24" //Private Subnet for RDS 19 | subnet3_cidr = "10.0.3.0/24" //Private subnet for RDS 20 | PUBLIC_KEY_PATH = "./mykey-pair.pub" // key name for ec2, make sure it is created before terrafomr apply 21 | PRIV_KEY_PATH = "./mykey-pair" 22 | instance_type = "t2.micro" //type of instance 23 | instance_class = "db.t2.micro" //type of RDS Instance 24 | root_volume_size = 22 25 | } -------------------------------------------------------------------------------- /modules/latest/aws_ami.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "linux2" { 2 | most_recent = true 3 | owners = ["amazon"] 4 | 5 | filter { 6 | name = "name" 7 | values = ["amzn2-ami-hvm-*-x86_64-gp2"] 8 | } 9 | 10 | 11 | filter { 12 | name = "virtualization-type" 13 | values = ["hvm"] 14 | } 15 | } 16 | 17 | 18 | data "aws_ami" "ubuntu" { 19 | 20 | most_recent = true 21 | 22 | filter { 23 | name = "name" 24 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 25 | } 26 | 27 | filter { 28 | name = "virtualization-type" 29 | values = ["hvm"] 30 | } 31 | 32 | owners = ["099720109477"] 33 | } -------------------------------------------------------------------------------- /modules/latest/main_script.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | 3 | region = var.region 4 | shared_credentials_files =[var.shared_credentials_file] 5 | profile = "default" 6 | } 7 | 8 | 9 | # Create VPC 10 | resource "aws_vpc" "prod-vpc" { 11 | cidr_block = var.VPC_cidr 12 | enable_dns_support = "true" #gives you an internal domain name 13 | enable_dns_hostnames = "true" #gives you an internal host name 14 | instance_tenancy = "default" 15 | 16 | 17 | } 18 | 19 | # Create Public Subnet for EC2 20 | resource "aws_subnet" "prod-subnet-public-1" { 21 | vpc_id = aws_vpc.prod-vpc.id 22 | cidr_block = var.subnet1_cidr 23 | map_public_ip_on_launch = "true" //it makes this a public subnet 24 | availability_zone = var.AZ1 25 | 26 | } 27 | 28 | # Create Private subnet for RDS 29 | resource "aws_subnet" "prod-subnet-private-1" { 30 | vpc_id = aws_vpc.prod-vpc.id 31 | cidr_block = var.subnet2_cidr 32 | map_public_ip_on_launch = "false" //it makes private subnet 33 | availability_zone = var.AZ2 34 | 35 | } 36 | 37 | # Create second Private subnet for RDS 38 | resource "aws_subnet" "prod-subnet-private-2" { 39 | vpc_id = aws_vpc.prod-vpc.id 40 | cidr_block = var.subnet3_cidr 41 | map_public_ip_on_launch = "false" //it makes private subnet 42 | availability_zone = var.AZ3 43 | 44 | } 45 | 46 | 47 | 48 | # Create IGW for internet connection 49 | resource "aws_internet_gateway" "prod-igw" { 50 | vpc_id = aws_vpc.prod-vpc.id 51 | 52 | } 53 | 54 | # Creating Route table 55 | resource "aws_route_table" "prod-public-crt" { 56 | vpc_id = aws_vpc.prod-vpc.id 57 | 58 | route { 59 | //associated subnet can reach everywhere 60 | cidr_block = "0.0.0.0/0" 61 | //CRT uses this IGW to reach internet 62 | gateway_id = aws_internet_gateway.prod-igw.id 63 | } 64 | 65 | 66 | } 67 | 68 | 69 | # Associating route tabe to public subnet 70 | resource "aws_route_table_association" "prod-crta-public-subnet-1" { 71 | subnet_id = aws_subnet.prod-subnet-public-1.id 72 | route_table_id = aws_route_table.prod-public-crt.id 73 | } 74 | 75 | 76 | 77 | //security group for EC2 78 | 79 | resource "aws_security_group" "ec2_allow_rule" { 80 | 81 | 82 | ingress { 83 | description = "HTTPS" 84 | from_port = 443 85 | to_port = 443 86 | protocol = "tcp" 87 | cidr_blocks = ["0.0.0.0/0"] 88 | } 89 | 90 | ingress { 91 | description = "HTTP" 92 | from_port = 80 93 | to_port = 80 94 | protocol = "tcp" 95 | cidr_blocks = ["0.0.0.0/0"] 96 | } 97 | 98 | ingress { 99 | description = "MYSQL" 100 | from_port = 3306 101 | to_port = 3306 102 | protocol = "tcp" 103 | cidr_blocks = ["0.0.0.0/0"] 104 | } 105 | 106 | ingress { 107 | description = "SSH" 108 | from_port = 22 109 | to_port = 22 110 | protocol = "tcp" 111 | cidr_blocks = ["0.0.0.0/0"] 112 | } 113 | 114 | egress { 115 | from_port = 0 116 | to_port = 0 117 | protocol = "-1" 118 | cidr_blocks = ["0.0.0.0/0"] 119 | } 120 | vpc_id = aws_vpc.prod-vpc.id 121 | tags = { 122 | Name = "allow ssh,http,https" 123 | } 124 | } 125 | 126 | 127 | # Security group for RDS 128 | resource "aws_security_group" "RDS_allow_rule" { 129 | vpc_id = aws_vpc.prod-vpc.id 130 | ingress { 131 | from_port = 3306 132 | to_port = 3306 133 | protocol = "tcp" 134 | security_groups = ["${aws_security_group.ec2_allow_rule.id}"] 135 | } 136 | # Allow all outbound traffic. 137 | egress { 138 | from_port = 0 139 | to_port = 0 140 | protocol = "-1" 141 | cidr_blocks = ["0.0.0.0/0"] 142 | } 143 | tags = { 144 | Name = "allow ec2" 145 | } 146 | 147 | } 148 | 149 | # Create RDS Subnet group 150 | resource "aws_db_subnet_group" "RDS_subnet_grp" { 151 | subnet_ids = ["${aws_subnet.prod-subnet-private-1.id}", "${aws_subnet.prod-subnet-private-2.id}"] 152 | } 153 | 154 | # Create RDS instance 155 | resource "aws_db_instance" "wordpressdb" { 156 | allocated_storage = 10 157 | engine = "mysql" 158 | engine_version = "5.7" 159 | instance_class = var.instance_class 160 | db_subnet_group_name = aws_db_subnet_group.RDS_subnet_grp.id 161 | vpc_security_group_ids = ["${aws_security_group.RDS_allow_rule.id}"] 162 | db_name = var.database_name 163 | username = var.database_user 164 | password = var.database_password 165 | skip_final_snapshot = true 166 | 167 | # make sure rds manual password chnages is ignored 168 | lifecycle { 169 | ignore_changes = [password] 170 | } 171 | } 172 | 173 | # change USERDATA varible value after grabbing RDS endpoint info 174 | data "template_file" "user_data" { 175 | template = var.IsUbuntu ? file("${path.module}/userdata_ubuntu.tpl") : file("${path.module}/user_data.tpl") 176 | vars = { 177 | db_username = var.database_user 178 | db_user_password = var.database_password 179 | db_name = var.database_name 180 | db_RDS = aws_db_instance.wordpressdb.endpoint 181 | } 182 | } 183 | 184 | 185 | # Create EC2 ( only after RDS is provisioned) 186 | resource "aws_instance" "wordpressec2" { 187 | ami = var.IsUbuntu ? data.aws_ami.ubuntu.id : data.aws_ami.linux2.id 188 | instance_type = var.instance_type 189 | subnet_id = aws_subnet.prod-subnet-public-1.id 190 | vpc_security_group_ids = ["${aws_security_group.ec2_allow_rule.id}"] 191 | user_data = data.template_file.user_data.rendered 192 | key_name = aws_key_pair.mykey-pair.id 193 | tags = { 194 | Name = "Wordpress.web" 195 | } 196 | 197 | root_block_device { 198 | volume_size = var.root_volume_size # in GB 199 | 200 | } 201 | 202 | # this will stop creating EC2 before RDS is provisioned 203 | depends_on = [aws_db_instance.wordpressdb] 204 | } 205 | 206 | // Sends your public key to the instance 207 | resource "aws_key_pair" "mykey-pair" { 208 | key_name = "mykey-pair" 209 | public_key = file(var.PUBLIC_KEY_PATH) 210 | } 211 | 212 | # creating Elastic IP for EC2 213 | resource "aws_eip" "eip" { 214 | instance = aws_instance.wordpressec2.id 215 | 216 | } 217 | 218 | output "IP" { 219 | value = aws_eip.eip.public_ip 220 | } 221 | output "RDS-Endpoint" { 222 | value = aws_db_instance.wordpressdb.endpoint 223 | } 224 | 225 | output "INFO" { 226 | value = "AWS Resources and Wordpress has been provisioned. Go to http://${aws_eip.eip.public_ip}" 227 | } 228 | 229 | resource "null_resource" "Wordpress_Installation_Waiting" { 230 | # trigger will create new null-resource if ec2 id or rds is chnaged 231 | triggers={ 232 | ec2_id=aws_instance.wordpressec2.id, 233 | rds_endpoint=aws_db_instance.wordpressdb.endpoint 234 | 235 | } 236 | connection { 237 | type = "ssh" 238 | user = var.IsUbuntu ? "ubuntu" : "ec2-user" 239 | private_key = file(var.PRIV_KEY_PATH) 240 | host = aws_eip.eip.public_ip 241 | } 242 | 243 | 244 | provisioner "remote-exec" { 245 | inline = ["sudo tail -f -n0 /var/log/cloud-init-output.log| grep -q 'WordPress Installed'"] 246 | 247 | } 248 | } 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /modules/latest/user_data.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # variable will be populated by terraform template 3 | db_username=${db_username} 4 | db_user_password=${db_user_password} 5 | db_name=${db_name} 6 | db_RDS=${db_RDS} 7 | # install LAMP Server 8 | yum update -y 9 | #install apache server and mysql client 10 | yum install -y httpd 11 | yum install -y mysql 12 | 13 | 14 | #first enable php7.xx from amazon-linux-extra and install it 15 | 16 | amazon-linux-extras enable php7.4 17 | yum clean metadata 18 | yum install -y php php-{pear,cgi,common,curl,mbstring,gd,mysqlnd,gettext,bcmath,json,xml,fpm,intl,zip,imap,devel} 19 | #install imagick extension 20 | yum -y install gcc ImageMagick ImageMagick-devel ImageMagick-perl 21 | pecl install imagick 22 | chmod 755 /usr/lib64/php/modules/imagick.so 23 | cat <>/etc/php.d/20-imagick.ini 24 | 25 | extension=imagick 26 | 27 | EOF 28 | 29 | systemctl restart php-fpm.service 30 | 31 | 32 | 33 | 34 | systemctl start httpd 35 | 36 | 37 | # Change OWNER and permission of directory /var/www 38 | usermod -a -G apache ec2-user 39 | chown -R ec2-user:apache /var/www 40 | find /var/www -type d -exec chmod 2775 {} \; 41 | find /var/www -type f -exec chmod 0664 {} \; 42 | 43 | 44 | # #**********************Installing Wordpress manually********************************* 45 | # # Download wordpress package and extract 46 | # wget https://wordpress.org/latest.tar.gz 47 | # tar -xzf latest.tar.gz 48 | # cp -r wordpress/* /var/www/html/ 49 | # # Create wordpress configuration file and update database value 50 | # cd /var/www/html 51 | # cp wp-config-sample.php wp-config.php 52 | # sed -i "s/database_name_here/$db_name/g" wp-config.php 53 | # sed -i "s/username_here/$db_username/g" wp-config.php 54 | # sed -i "s/password_here/$db_user_password/g" wp-config.php 55 | # sed -i "s/localhost/$db_RDS/g" wp-config.php 56 | # cat <>/var/www/html/wp-config.php 57 | # define( 'FS_METHOD', 'direct' ); 58 | # define('WP_MEMORY_LIMIT', '128M'); 59 | # EOF 60 | 61 | #**********************Installing Wordpress using WP CLI********************************* 62 | curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar 63 | chmod +x wp-cli.phar 64 | mv wp-cli.phar /usr/local/bin/wp 65 | wp core download --path=/var/www/html --allow-root 66 | wp config create --dbname=$db_name --dbuser=$db_username --dbpass=$db_user_password --dbhost=$db_RDS --path=/var/www/html --allow-root --extra-php </,/<\/Directory>/ s/AllowOverride None/AllowOverride all/' /etc/httpd/conf/httpd.conf 80 | 81 | #Make apache autostart and restart apache 82 | systemctl enable httpd.service 83 | systemctl restart httpd.service 84 | echo WordPress Installed 85 | 86 | -------------------------------------------------------------------------------- /modules/latest/userdata_ubuntu.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # AUTOMATIC WORDPRESS INSTALLER IN AWS Ubuntu Server 20.04 LTS (HVM) 3 | 4 | # varaible will be populated by terraform template 5 | db_username=${db_username} 6 | db_user_password=${db_user_password} 7 | db_name=${db_name} 8 | db_RDS=${db_RDS} 9 | 10 | # install LAMP Server 11 | apt update -y 12 | apt upgrade -y 13 | apt update -y 14 | apt upgrade -y 15 | #install apache server 16 | apt install -y apache2 17 | 18 | 19 | 20 | apt install -y php 21 | apt install -y php php-{pear,cgi,common,curl,mbstring,gd,mysqlnd,bcmath,json,xml,intl,zip,imap,imagick} 22 | 23 | 24 | 25 | #and download mysql package to yum and install mysql client from yum 26 | apt install -y mysql-client-core-8.0 27 | 28 | # starting apache and register them to startup 29 | 30 | systemctl enable --now apache2 31 | 32 | 33 | # Change OWNER and permission of directory /var/www 34 | usermod -a -G www-data ubuntu 35 | chown -R ubuntu:www-data /var/www 36 | find /var/www -type d -exec chmod 2775 {} \; 37 | find /var/www -type f -exec chmod 0664 {} \; 38 | 39 | # #**********************Installing Wordpress manually********************************* 40 | # # Download wordpress package and extract 41 | # wget https://wordpress.org/latest.tar.gz 42 | # tar -xzf latest.tar.gz 43 | # cp -r wordpress/* /var/www/html/ 44 | # # Create wordpress configuration file and update database value 45 | # cd /var/www/html 46 | # cp wp-config-sample.php wp-config.php 47 | # sed -i "s/database_name_here/$db_name/g" wp-config.php 48 | # sed -i "s/username_here/$db_username/g" wp-config.php 49 | # sed -i "s/password_here/$db_user_password/g" wp-config.php 50 | # sed -i "s/localhost/$db_RDS/g" wp-config.php 51 | # cat <>/var/www/html/wp-config.php 52 | # define( 'FS_METHOD', 'direct' ); 53 | # define('WP_MEMORY_LIMIT', '128M'); 54 | # EOF 55 | 56 | #**********************Installing Wordpress using WP CLI********************************* 57 | curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar 58 | chmod +x wp-cli.phar 59 | mv wp-cli.phar /usr/local/bin/wp 60 | wp core download --path=/var/www/html --allow-root 61 | wp config create --dbname=$db_name --dbuser=$db_username --dbpass=$db_user_password --dbhost=$db_RDS --path=/var/www/html --allow-root --extra-php </,/<\/Directory>/ s/AllowOverride None/AllowOverride all/' /etc/apache2/apache2.conf 75 | a2enmod rewrite 76 | 77 | # restart apache 78 | 79 | systemctl restart apache2 80 | echo WordPress Installed 81 | 82 | -------------------------------------------------------------------------------- /modules/latest/variables.tf: -------------------------------------------------------------------------------- 1 | variable "database_name" {} 2 | variable "database_password" {} 3 | variable "database_user" {} 4 | 5 | variable "region" {} 6 | variable "shared_credentials_file" {} 7 | variable "IsUbuntu" { 8 | type = bool 9 | default = true 10 | 11 | } 12 | variable "AZ1" {} 13 | variable "AZ2" {} 14 | variable "AZ3" {} 15 | variable "VPC_cidr" {} 16 | variable "subnet1_cidr" {} 17 | variable "subnet2_cidr" {} 18 | variable "subnet3_cidr" {} 19 | variable "instance_type" {} 20 | variable "instance_class" {} 21 | variable "PUBLIC_KEY_PATH" {} 22 | variable "PRIV_KEY_PATH" {} 23 | variable "root_volume_size" {} 24 | 25 | -------------------------------------------------------------------------------- /modules/latest/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | version = "~>4.30.0" 5 | } 6 | null = { 7 | version = "~> 3.1.1" 8 | } 9 | template ={ 10 | version= "~> 2.2.0" 11 | 12 | } 13 | } 14 | 15 | required_version = "~> 1.1.3" 16 | } --------------------------------------------------------------------------------