# Automate Deploying a LAMP Stack Application using Ansible

# What is Ansible?

Ansible is a software tool that provides simple but powerful automation for cross-platform computer support. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, cloud provisioning, configuration management, intra-service orchestration, and nearly anything a systems administrator does on a weekly or daily basis. Ansible doesn't depend on agent software and has no additional security infrastructure, so it's easy to deploy.

In this project, we will use Ansible to deploy a LAMP stack web application on multi-node setup.

# Pre-requisites
- User with `root` privileges
- 3 CentOS hosts on the same network. One will act the Ansible controller, and the other two will acts as target hosts. The hostnames should be configured as below to follow along: 
  - ansible-controller
  - lamp-db
  - lamp-web

- Ansible installed on ansible-controller host
- /etc/hosts/ on `ansible-controller` host is configured with the IP addresses of the targets as below example

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1666967198317/wyue6mFQd.png align="left")

- SSH Password-less login enabled between the ansible-controller host and the two target hosts

# **Steps**

## Step1: Create the project folder and access it by running below commands:
```
mkdir lamp-stack-playbooks
cd lamp-stack-playbooks
```

## Step 2: Configure the inventory file

- Create an inventory file
```
vi inventory
```
- Copy below lines and paste them in the inventory file. Also, make sure to replace <YOUR-DB-HOST-IP> and <YOUR-WEB-HOST-IP> with your proper DB and Web hosts' IP addresses. In addition you need to specify the user and SSH private key path for both DB and WEB.

```
[db_servers]
lamp-db ansible_host=<YOUR-DB-HOST-IP> ansible_user=<DB_USER> ansible_ssh_private_key_file=<PATH_TO_DB_SSH_PRIVATE_KEY> mysqlservice=mysqld mysql_port=3306 dbname=ecomdb dbuser=ecomuser dbpassword=ecompassword

[web_servers]
lamp-web ansible_host=<YOUR-WEB-HOST-IP> ansible_user=<WEB_USER> ansible_ssh_private_key_file=<PATH_TO_WEB_SSH_PRIVATE_KEY> httpd_port=80 repository=https://github.com/noweder/lamp-app-ecommerce.git
``` 

## Step 3: Prepare the configuration files 

- Create `files` sub-folder and move into it:
```
mkdir files
```

- Create MySQL Configuration file `my.cnf` under `files` folder:
```
vi files/my.cnf
```
- Paste the below lines inside `my.cnf` and save it:
```
[mysqld]
innodb-buffer-pool-size=5242880
# datadir=/var/lib/mysql
# socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
port=3306
```
- Create another file named `db-load-script.sql` on the controller host under `files` folder:
```
vi files/db-load-script.sql
```
- Paste below lines inside `db-load-script.sql` file and save it:

```
USE ecomdb;
CREATE TABLE products (id mediumint(8) unsigned NOT NULL auto_increment,Name varchar(255) default NULL,Price varchar(255) default NULL, ImageUrl varchar(255) default NULL,PRIMARY KEY (id)) AUTO_INCREMENT=1;

INSERT INTO products (Name,Price,ImageUrl) VALUES ("Laptop","100","c-1.png"),("Drone","200","c-2.png"),("VR","300","c-3.png"),("Tablet","50","c-5.png"),("Watch","90","c-6.png"),("Phone Covers","20","c-7.png"),("Phone","80","c-8.png"),("Laptop","150","c-4.png");
```
- Create `index.php` file on controller host under `files` folder
```
vi files/index.php
```
 - Paste below code inside `index.php` file and make sure to replace `<YOUR-DB-HOST-IP>` with your specific DB Host IP address before saving it:

```
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>E-Commerce Website</title>

        <!-- Favicon -->
        <link rel="icon" href="img/favicon.png" type="image/png" />
        <!-- Bootstrap CSS -->
        <link href="css/bootstrap.min.css" rel="stylesheet">
        <!-- Icon CSS-->
        <link rel="stylesheet" href="vendors/font-awesome/css/font-awesome.min.css">
        <link rel="stylesheet" href="vendors/linearicons/linearicons-1.0.0.css">
        <!-- Animations CSS-->
        <link rel="stylesheet" href="vendors/wow-js/animate.css">
        <!-- owl_carousel-->
        <link rel="stylesheet" href="vendors/owl_carousel/owl.carousel.css">

        <!-- Theme style CSS -->
        <link href="css/style.css" rel="stylesheet">
<!--        <link href="css/responsive.css" rel="stylesheet">  -->

        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
          <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
          <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->
    </head>
    <body>
        <!--==========Main Header==========-->
        <header class="main_header_area">
            <nav class="navbar navbar-default navbar-fixed-top" id="main_navbar">
                <div class="container-fluid searchForm">
                    <form action="#" class="row">
                        <div class="input-group">
                            <span class="input-group-addon"><i class="lnr lnr-magnifier"></i></span>
                            <input type="search" name="search" class="form-control" placeholder="Type & Hit Enter">
                            <span class="input-group-addon form_hide"><i class="lnr lnr-cross"></i></span>
                        </div>
                    </form>
                </div>
                <div class="container">
                    <div class="row">
                    <!-- Brand and toggle get grouped for better mobile display -->
                    <div class="col-md-2 p0">
                        <div class="navbar-header">
                            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                            <span class="sr-only">Toggle navigation</span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            </button>
                            <a class="navbar-brand" href="index.html">
                                <img src="img/logo.png" alt="">
                                <img src="img/logo-2.png" alt="">
                            </a>
                        </div>
                    </div>

                    <!-- Collect the nav links, forms, and other content for toggling -->
                    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                        <div class="col-md-9 p0">
                            <ul class="nav navbar-nav main_nav">
                              <li><a href="#">Laptops</a></li>
                              <li><a href="#">Drones</a></li>
                                <li><a href="#">Gadgets</a></li>
                                <li><a href="#">Phones</a></li>
                                <li><a href="#">VR</a></li>
                                <li><a href="#">Contact us</a></li>
                            </ul>
                        </div>
                        <div class="col-md-1 p0">
                            <ul class="nav navbar-nav navbar-right">
                                <li><a href="#" class="nav_searchFrom"><i class="lnr lnr-magnifier"></i></a></li>
                            </ul>
                        </div>
                    </div><!-- /.navbar-collapse -->
                    </div>
                </div><!-- /.container-fluid -->
            </nav>
        </header>
        <!--==========Main Header==========-->

        <!--==========Slider area==========-->
        <section class="slider_area row m0">
            <div class="slider_inner">
                <div class="camera_caption">
                    <h2 class="wow fadeInUp animated">Make Your Shopping Easy</h2>
                    <h5 class="wow fadeIn animated" data-wow-delay="0.3s">Find everything accordingly</h5>
                    <a class="learn_mor wow fadeInU" data-wow-delay="0.6s" href="#product-list">Show Now!</a>
                </div>
            </div>
        </section>
        <!--==========End Slider area==========-->

        <section class="best_business_area row">
            <div class="check_tittle wow fadeInUp" data-wow-delay="0.7s" id="product-list">
                <h2>Product List</h2>
            </div>
            <div class="row it_works">
              <?php

                        $link = mysqli_connect('<YOUR_DB_HOST_IP>', 'ecomuser', 'ecompassword', 'ecomdb');

                        if ($link) {
                        $res = mysqli_query($link, "select * from products;");
                        while ($row = mysqli_fetch_assoc($res)) { ?>

                <div class="col-md-3 col-sm-6 business_content">
                    <?php echo '<img src="img/' . $row['ImageUrl'] . '" alt="">' ?>
                    <div class="media">
                        <div class="media-left">

                        </div>
                        <div class="media-body">
                            <a href="#"><?php echo $row['Name'] ?></a>
                            <p>Purchase <?php echo $row['Name'] ?> at the lowest price <span><?php echo $row['Price'] ?>$</span></p>
                        </div>
                    </div>
                </div>

                <?php
                        }
                    }
                    else {
                ?>
                <div style="width: 100%">
                <div class="error-content">

                    <h1>Database connection error</h1>
                    <p>
                    <?php
                          echo mysqli_connect_errno() . ":" . mysqli_connect_error();
                    ?>
                    </p>
                  </div>
                  </div>
                  <?php
                    }
                  ?>


            </div>
        </section>


        <footer class="footer_area row">
            <div class="container custom-container">



                <div class="copy_right_area">
                    <h4 class="copy_right">© Copyright 2019 Ecommerce Website | All Rights Reserved</h4>
                </div>
            </div>
        </footer>

        <!-- jQuery -->
        <script src="js/jquery-1.12.4.min.js"></script>
        <!-- Bootstrap -->
        <script src="js/bootstrap.min.js"></script>
        <!-- Wow js -->
        <script src="vendors/wow-js/wow.min.js"></script>
        <!-- Wow js -->
        <script src="vendors/Counter-Up/waypoints.min.js"></script>
        <script src="vendors/Counter-Up/jquery.counterup.min.js"></script>
        <!-- Stellar js -->
        <script src="vendors/stellar/jquery.stellar.js"></script>
        <!-- owl_carousel js -->
        <script src="vendors/owl_carousel/owl.carousel.min.js"></script>
        <!-- Theme js -->
        <script src="js/theme.js"></script>
    </body>
</html>
```

## Step 3: Create a `deploy-lamp-stack.yml` playbook file under `lamp-stack-playbooks` and paste below code: 
```
---

- name: Deploy lamp stack application
  hosts: all
  become: yes
  tasks:
    - name: Install common dependencies
      yum:
        name:
          - libselinux-python
          - libsemanage-python
          - firewalld
        state: installed

    # Install and Configure Database
- name: Deploy DB of the application
  hosts: lamp-db
  become: yes
  tasks:
    - name: Install MariaDB package
      yum:
        name:
          - mariadb-server
          - MySQL-python
        state: installed

    - name: Create Mysql configuration file
      copy: src=files/my.cnf dest=/etc/my.cnf

    - name: Start MariaDB Service
      service: name=mariadb state=started enabled=yes

    - name: Start firewalld
      service: name=firewalld state=started enabled=yes

    - name: insert firewalld rule
      firewalld: port={{ mysql_port }}/tcp permanent=true state=enabled immediate=yes

    - name: Create Application Database
      mysql_db: name={{ dbname }} state=present

    - name: Create Application DB User
      mysql_user: name={{ dbuser }} password={{ dbpassword }} priv=*.*:ALL host='<YOUR_WEB_HOST_IP>' state=present

    - name: Move db-load-script to db host
      copy:
        src: files/db-load-script.sql
        dest: /tmp/db-load-script.sql

    - name: Load Inventory Data
      shell: mysql -f < /tmp/db-load-script.sql

- name: Deploy web application
  hosts: lamp-web
  become: yes
  tasks:
    - name: Install httpd and php
      yum:
        name:
          - httpd
          - php
          - php-mysql
        state: present

    - name: Install web role specific dependencies
      yum: name=git state=installed

    - name: Start firewalld
      service: name=firewalld state=started enabled=yes

    - name: insert firewalld rule for httpd
      firewalld: port={{ httpd_port }}/tcp permanent=true state=enabled immediate=yes

    - name: Set index.php as the default page
      tags: "Set index.php as the default page"
      replace:
        path: /etc/httpd/conf/httpd.conf
        regexp: 'DirectoryIndex index.html'
        replace: 'DirectoryIndex index.php'

    - name: http service state
      service: name=httpd state=started enabled=yes

    - name: Copy the code from repository
      git: repo={{ repository }} dest=/var/www/html/  force=yes

    - name: Create the index.php file
      copy: src=files/index.php dest=/var/www/html/index.php
```
- Make sure to replace `<YOUR_WEB_HOST_IP>` with your specific Web Host IP address before saving the file.

## Step 4: Run the playbook with below command
```
ansible-playbook -i inventory deploy-lamp-stack.yml
```
- Make sure to replace the `host` value `<YOUR-WEB-HOST-IP>` in ` Create Application DB User` task with your specific web host IP address.

## Step 5: Access the web application
- Run the below command to access the web application from CLI:
```
curl <YOUR-WEB-HOST-IP>
```
- Or, copy `<YOUR-WEB-HOST-IP>` and paste it into a web browser.

Thank you for reading!
