├── .github └── workflows │ ├── terraform-destroy.yml │ └── terraform.yml ├── .gitignore ├── Jenkinsfile ├── README.md ├── databases.tf ├── ec2.tf ├── internetgateway.tf ├── mysql.txt ├── output.tf ├── provider.tf ├── routetables.tf ├── securitygroup.tf ├── subnets.tf ├── user_data.tpl ├── variable.tf └── vpc.tf /.github/workflows/terraform-destroy.yml: -------------------------------------------------------------------------------- 1 | name: 'Terraform Destroy' 2 | 3 | on: 4 | workflow_dispatch: # This allows manual triggering only 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | terraform: 11 | name: 'Terraform' 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | 18 | - name: Configure AWS Credentials 19 | uses: aws-actions/configure-aws-credentials@v2 20 | with: 21 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 22 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 23 | aws-region: eu-west-2 24 | 25 | - name: Setup Terraform 26 | uses: hashicorp/setup-terraform@v2 27 | with: 28 | terraform_version: 1.0.0 29 | 30 | - name: Terraform Init 31 | run: terraform init 32 | 33 | - name: Terraform Destroy 34 | run: terraform destroy -auto-approve -------------------------------------------------------------------------------- /.github/workflows/terraform.yml: -------------------------------------------------------------------------------- 1 | name: 'Terraform Deploy' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | terraform: 13 | name: 'Terraform' 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Configure AWS Credentials 21 | uses: aws-actions/configure-aws-credentials@v2 22 | with: 23 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 24 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 25 | aws-region: eu-west-2 26 | 27 | - name: Setup Terraform 28 | uses: hashicorp/setup-terraform@v2 29 | with: 30 | terraform_version: 1.0.0 31 | 32 | - name: Terraform Init 33 | run: terraform init 34 | 35 | - name: Terraform Format 36 | run: terraform fmt -check 37 | 38 | - name: Terraform Plan 39 | run: terraform plan -no-color 40 | 41 | - name: Terraform Apply 42 | run: terraform apply -auto-approve -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pem 2 | *.tfstate* 3 | .terraform 4 | .terraform.lock.hcl 5 | credentials 6 | id_rsa.pub 7 | .txt -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any // Specifies that the pipeline can run on any available agent (agent refers to the Jenkins worker node). 3 | 4 | stages { 5 | stage('Checkout') { // This is the first stage in the pipeline and is named "Checkout." 6 | steps { 7 | // This step allows us to use the GitHub token securely for authentication during the checkout process. 8 | withCredentials([string(credentialsId: 'GitHub_Token', variable: 'github_token')]) { 9 | // This step checks out the source code repository from GitHub using Git. 10 | checkout([ 11 | $class: 'GitSCM', 12 | branches: [[name: '*/main']], // The pipeline will only consider the 'main' branch. 13 | extensions: [[$class: 'CleanCheckout']], // Ensures a clean checkout without any leftovers from previous builds. 14 | userRemoteConfigs: [[url: 'https://' + env.github_token + '@' + 'github.com/ab12345/12345xyz.git']] // The repository URL with the GitHub token for authentication. 15 | ]) 16 | } 17 | } 18 | } 19 | 20 | stage('Initialize Working Directory') { // This is the second stage, which initializes the Terraform working directory. 21 | steps { 22 | script { 23 | // This step runs the 'terraform init' command to initialize the Terraform working directory. 24 | sh 'terraform init' 25 | } 26 | } 27 | } 28 | 29 | stage('Terraform Plan') { // This is the third stage, which performs a Terraform plan. 30 | steps { 31 | script { 32 | // This step runs the 'terraform plan' command to generate an execution plan for Terraform. 33 | sh 'terraform plan' 34 | } 35 | } 36 | } 37 | 38 | stage('Terraform Apply') { // This is the fourth stage, which applies the Terraform plan to create or modify resources. 39 | steps { 40 | script { 41 | // This step runs the 'terraform apply' command with '--auto-approve' flag to automatically approve the plan and apply changes without manual confirmation. 42 | sh 'terraform apply --auto-approve' 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | This is the repository containing the files required to automate the creation of the infrastructure of the startup using terraform. 3 | 4 | ## Project Requirements 5 | The infrastructure includes 4 web servers and 2 databases. The requirements are: 6 | - High availability 7 | - Unique naming of each instance 8 | - Installation of apache webserver on the instances 9 | 10 | ## Architecture diagram 11 | To meet the requirements, the 4 web servers (which can be edited to your preference) will be created in 2 public subnets in 2 different 12 | availability zones while the database instances will be created in 2 private subnets in 2 availabilty zones as the web servers. 13 | 14 | Here's the architecture diagram: 15 | 16 | ![Architectural Diagram for Startup)](https://user-images.githubusercontent.com/87014766/220373865-24821c92-9e4d-46a1-b97b-117199038347.png) 17 | -------------------------------------------------------------------------------- /databases.tf: -------------------------------------------------------------------------------- 1 | # This block creates the databases using the "aws_db_instance" resource. 2 | # The MySQL engine is used and the instance class is set to "db.t2.micro", and 5GB of storage is allocated for each instance. 3 | # The instances are deployed in the "eu-west-2a" availability zone, and each instance is named using the "name" parameter with a unique index number. 4 | # A default username and password for the databases is set using the "username" and "password" parameters. 5 | 6 | 7 | resource "aws_db_instance" "databasename" { 8 | count = 1 9 | engine = "mysql" 10 | instance_class = "db.t3.micro" 11 | engine_version = "8.0.40" 12 | allocated_storage = 5 13 | availability_zone = "eu-west-2a" 14 | db_name = "database${count.index + 1}" 15 | username = "admin" 16 | password = "password" 17 | apply_immediately = true 18 | skip_final_snapshot = true 19 | db_subnet_group_name = aws_db_subnet_group.subnet_group.name 20 | vpc_security_group_ids = [aws_security_group.db_security_group.id] 21 | 22 | } 23 | 24 | 25 | resource "aws_db_instance" "databasename_2" { 26 | count = 1 27 | engine = "mysql" 28 | instance_class = "db.t3.micro" 29 | engine_version = "8.0.40" 30 | allocated_storage = 5 31 | availability_zone = "eu-west-2b" 32 | db_name = "database${count.index + 1}" 33 | username = "admin" 34 | password = "password" 35 | apply_immediately = true 36 | skip_final_snapshot = true 37 | db_subnet_group_name = aws_db_subnet_group.subnet_group.name 38 | vpc_security_group_ids = [aws_security_group.db_security_group.id] 39 | 40 | } 41 | -------------------------------------------------------------------------------- /ec2.tf: -------------------------------------------------------------------------------- 1 | # This block creates the web servers using the "aws_instance" resource. 2 | # We're using the AMI ID that we fetched earlier, setting the instance type using the "instance_type" variable, and deploying the instances in the "eu-west-2a" availability zone. 3 | # We're also using a "count" parameter to create 4 instances, and we're naming each instance using the "Name" tag with a unique index number. 4 | 5 | resource "aws_instance" "web_server_az1" { 6 | count = 2 7 | ami = data.aws_ami.ubuntu.id 8 | instance_type = var.instance_type 9 | vpc_security_group_ids = [aws_security_group.default.id] 10 | subnet_id = aws_subnet.web_server_subnet_1.id 11 | associate_public_ip_address = true 12 | user_data = file("${path.module}/user_data.tpl") 13 | 14 | tags = { 15 | Name = "web-server-${count.index + 1}" 16 | } 17 | } 18 | 19 | resource "aws_instance" "web_server_az2" { 20 | count = 2 21 | ami = data.aws_ami.ubuntu.id 22 | instance_type = var.instance_type 23 | vpc_security_group_ids = [aws_security_group.default.id] 24 | subnet_id = aws_subnet.web_server_subnet_2.id 25 | associate_public_ip_address = true 26 | user_data = file("${path.module}/user_data.tpl") 27 | 28 | tags = { 29 | Name = "web-server-${count.index + 3}" 30 | } 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /internetgateway.tf: -------------------------------------------------------------------------------- 1 | # Create the internet gateway 2 | 3 | resource "aws_internet_gateway" "internet_gateway" { 4 | vpc_id = aws_vpc.startup_vpc.id 5 | 6 | tags = { 7 | Name = "Internet Gateway" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /mysql.txt: -------------------------------------------------------------------------------- 1 | /*#a)USE mysql; 2 | b)UPDATE user SET plugin='mysql_native_password' WHERE User='root'; 3 | c)FLUSH PRIVILEGES; 4 | d)ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'admin'; 5 | e)exit 6 | 7 | 5) Then to login again in mysql 8 | sudo mysql -p 9 | Type the Password - admin 10 | 6) Install unzip - sudo apt-get install unzip 11 | 7) To unzip - unzip filename 12 | 8) Change password to 'admin' in your code where database password is asked. 13 | 9) Don't forget to change the host to ('0.0.0.0' and port=80 ) in your main flask file.*/ -------------------------------------------------------------------------------- /output.tf: -------------------------------------------------------------------------------- 1 | # This block outputs the public IP addresses of the EC2 instances using the "aws_instance" resource. 2 | # It uses the "web_server" resource name and the "public_ip" attribute to retrieve the IP addresses 3 | 4 | 5 | output "web_server_az1_public_ips" { 6 | value = aws_instance.web_server_az1.*.public_ip 7 | } 8 | 9 | output "web_server_az2_public_ips" { 10 | value = aws_instance.web_server_az2.*.public_ip 11 | } 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /provider.tf: -------------------------------------------------------------------------------- 1 | # This block sets the AWS provider configuration for Terraform. 2 | # In this case, we're using the "aws" provider and setting the region to "eu-west-2". 3 | 4 | provider "aws" { 5 | region = "eu-west-2" 6 | shared_credentials_files = ["${path.module}/credentials"] 7 | } 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /routetables.tf: -------------------------------------------------------------------------------- 1 | # Create the route tables 2 | 3 | resource "aws_route_table" "web_server_route_table" { 4 | vpc_id = aws_vpc.startup_vpc.id 5 | 6 | route { 7 | cidr_block = "0.0.0.0/0" 8 | gateway_id = aws_internet_gateway.internet_gateway.id 9 | } 10 | 11 | tags = { 12 | Name = "Web Server Route Table" 13 | } 14 | } 15 | 16 | 17 | 18 | 19 | resource "aws_default_route_table" "mtc_private_rt" { 20 | default_route_table_id = aws_vpc.startup_vpc.default_route_table_id 21 | 22 | tags = { 23 | Name = "mtc_private" 24 | } 25 | } 26 | 27 | resource "aws_route_table_association" "mtc_public_assoc1" { 28 | count = 3 29 | subnet_id = aws_subnet.web_server_subnet_1.id 30 | route_table_id = aws_route_table.web_server_route_table.id 31 | } 32 | 33 | resource "aws_route_table_association" "mtc_public_assoc2" { 34 | count = 3 35 | subnet_id = aws_subnet.web_server_subnet_2.id 36 | route_table_id = aws_route_table.web_server_route_table.id 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /securitygroup.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "default" { 2 | vpc_id = aws_vpc.startup_vpc.id 3 | 4 | ingress { 5 | from_port = 80 6 | to_port = 80 7 | protocol = "tcp" 8 | cidr_blocks = ["0.0.0.0/0"] 9 | } 10 | 11 | egress { 12 | from_port = 0 13 | to_port = 0 14 | protocol = "-1" 15 | cidr_blocks = ["0.0.0.0/0"] 16 | } 17 | } 18 | 19 | resource "aws_security_group" "webserver_sg" { 20 | vpc_id = aws_vpc.startup_vpc.id 21 | 22 | ingress { 23 | protocol = "tcp" 24 | self = true 25 | from_port = 80 26 | to_port = 80 27 | cidr_blocks = ["0.0.0.0/0"] 28 | } 29 | 30 | egress { 31 | from_port = 0 32 | to_port = 0 33 | protocol = "-1" 34 | cidr_blocks = ["0.0.0.0/0"] 35 | } 36 | } 37 | 38 | resource "aws_security_group" "db_security_group" { 39 | name_prefix = "db-" 40 | vpc_id = aws_vpc.startup_vpc.id 41 | 42 | ingress { 43 | from_port = 3306 44 | to_port = 3306 45 | protocol = "tcp" 46 | cidr_blocks = [aws_vpc.startup_vpc.cidr_block] 47 | } 48 | 49 | tags = { 50 | Name = "db-security-group" 51 | } 52 | } 53 | 54 | 55 | resource "aws_security_group" "lbsg" { 56 | name = "lbsg-${terraform.workspace}" 57 | description = "controls access to the LB" 58 | vpc_id = aws_vpc.startup_vpc.id 59 | tags = merge( 60 | { 61 | "Name" : "lbsg-${terraform.workspace}" 62 | }, 63 | ) 64 | } 65 | 66 | 67 | resource "aws_security_group_rule" "web_to_lb" { 68 | security_group_id = aws_security_group.lbsg.id 69 | type = "ingress" 70 | from_port = 80 71 | to_port = 80 72 | protocol = "tcp" 73 | cidr_blocks = ["0.0.0.0/0"] 74 | 75 | } 76 | 77 | resource "aws_security_group_rule" "web_to_lb_secure" { 78 | security_group_id = aws_security_group.lbsg.id 79 | type = "ingress" 80 | from_port = 443 81 | to_port = 443 82 | protocol = "tcp" 83 | cidr_blocks = ["0.0.0.0/0"] 84 | 85 | } 86 | 87 | resource "aws_security_group_rule" "lb_egress" { 88 | security_group_id = aws_security_group.lbsg.id 89 | type = "egress" 90 | from_port = 0 91 | to_port = 0 92 | protocol = "-1" 93 | cidr_blocks = ["0.0.0.0/0"] 94 | } 95 | 96 | 97 | resource "aws_db_subnet_group" "subnet_group" { 98 | name = "db-subnet-group" 99 | subnet_ids = [aws_subnet.database_subnet_1.id, aws_subnet.database_subnet_2.id] 100 | } 101 | 102 | -------------------------------------------------------------------------------- /subnets.tf: -------------------------------------------------------------------------------- 1 | # Define the subnets 2 | 3 | data "aws_availability_zones" "available" {} 4 | resource "random_integer" "random" { 5 | min = 1 6 | max = 100 7 | } 8 | 9 | resource "random_shuffle" "public_az" { 10 | input = data.aws_availability_zones.available.names 11 | result_count = 10 12 | } 13 | resource "aws_subnet" "web_server_subnet_1" { 14 | vpc_id = aws_vpc.startup_vpc.id 15 | availability_zone = "eu-west-2a" 16 | cidr_block = "10.0.8.0/24" 17 | map_public_ip_on_launch = true 18 | 19 | tags = { 20 | Name = "Web Server Subnet - 1" 21 | } 22 | } 23 | 24 | resource "aws_subnet" "web_server_subnet_2" { 25 | vpc_id = aws_vpc.startup_vpc.id 26 | availability_zone = "eu-west-2b" 27 | cidr_block = "10.0.6.0/24" 28 | map_public_ip_on_launch = true 29 | 30 | tags = { 31 | Name = "Web Server Subnet - 2" 32 | } 33 | } 34 | 35 | 36 | 37 | resource "aws_subnet" "database_subnet_1" { 38 | vpc_id = aws_vpc.startup_vpc.id 39 | cidr_block = "10.0.5.0/24" 40 | map_public_ip_on_launch = false 41 | availability_zone = "eu-west-2a" 42 | 43 | tags = { 44 | Name = "Database Subnet 1" 45 | } 46 | } 47 | 48 | resource "aws_subnet" "database_subnet_2" { 49 | vpc_id = aws_vpc.startup_vpc.id 50 | map_public_ip_on_launch = false 51 | cidr_block = "10.0.4.0/24" 52 | availability_zone = "eu-west-2b" 53 | 54 | tags = { 55 | Name = "Database Subnet 2" 56 | } 57 | } -------------------------------------------------------------------------------- /user_data.tpl: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | sudo apt-get update 3 | sudo apt-get install -y apache2 4 | sudo systemctl start apache2 5 | sudo systemctl enable apache2 6 | echo "The page was created by the user data" | sudo tee /var/www/html/index.html -------------------------------------------------------------------------------- /variable.tf: -------------------------------------------------------------------------------- 1 | # This block defines a Terraform variable named "instance_type", which is used to specify the instance type of the EC2 instances. 2 | # The given instance type is "t2.micro" 3 | variable "instance_type" { 4 | default = "t2.micro" 5 | } 6 | 7 | variable "db_instance_names" { 8 | type = map(string) 9 | default = { 10 | "db_instance_1" = "db_name_1" 11 | "db_instance_2" = "db_name_2" 12 | } 13 | } 14 | 15 | data "aws_ami" "ubuntu" { 16 | 17 | most_recent = true 18 | 19 | filter { 20 | name = "name" 21 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 22 | } 23 | 24 | filter { 25 | name = "virtualization-type" 26 | values = ["hvm"] 27 | } 28 | 29 | owners = ["099720109477"] 30 | } 31 | -------------------------------------------------------------------------------- /vpc.tf: -------------------------------------------------------------------------------- 1 | # Define the VPC 2 | 3 | 4 | resource "aws_vpc" "startup_vpc" { 5 | cidr_block = "10.0.0.0/16" 6 | enable_dns_support = true 7 | enable_dns_hostnames = true 8 | 9 | tags = { 10 | Name = "Startup VPC" 11 | } 12 | } 13 | --------------------------------------------------------------------------------