Automating LAMP Stack Installation and Uninstallation with Ansible

Nidhin B
6 min readJun 27, 2023

--

In this tutorial, we’ll explore how to automate the installation and uninstallation of a LAMP (Linux, Apache, MySQL, PHP) stack using Ansible. Ansible is an open-source automation tool that allows you to define and manage infrastructure as code.

Checkout my channel for more contents: youtube.com/@ni6hin

Introduction

The LAMP stack is a popular software bundle used to deploy web applications. It consists of the following components:

  • Linux: The operating system (in this case, we’re using an Amazon Linux instance).
  • Apache: The web server responsible for serving web content.
  • MySQL/MariaDB: The relational database management system for storing application data.
  • PHP: The server-side scripting language for dynamic web content.

We’ll use Ansible to automate the installation and configuration of these components.

Prerequisites

Before we begin, make sure you have the following:

  • An Amazon Linux instance with Ansible installed.
  • Access to the AWS Secrets Manager service.

Creating the Ansible Playbook

Let’s start by creating the Ansible playbook that will install the LAMP stack and configure the components.

---
- name: "Installing LAMP stack and configuring"
hosts: amazon
become: true
vars_files:
- httpd.vars
- mariadb.vars
- packages.vars
tasks:
# Task 1: Installing Packages
- name: "Installing Packages"
yum:
name: "{{ packages }}"
state: present
tags:
- lamp
- never

# Task 2: Enabling/Restarting services
- name: "Enabling/Restarting services"
service:
name: "{{ item }}"
state: restarted
enabled: true
with_items: "{{ services }}"
tags:
- lamp
- never

# Task 3: Creating httpd.conf from template
- name: "Creating httpd.conf from httpd.conf.tmpl"
template:
src: httpd.conf.tmpl
dest: /etc/httpd/conf/httpd.conf
tags:
- lamp
- never

# Task 4: Creating Virtual host
- name: "Creating Virtual host"
template:
src: virtualhost.conf.tmpl
dest: /etc/httpd/conf.d/default.conf
owner: "{{ httpd_owner }}"
group: "{{ httpd_group }}"
tags:
- lamp
- never

# Task 5: Creating docroot for the website
- name: "Creating docroot for {{ hostname }}"
file:
path: "/var/www/html/default/"
state: directory
owner: "{{ httpd_owner }}"
group: "{{ httpd_group }}"
tags:
- lamp
- never

# Task 6: Setting python3 mysql module PyMySQL
- name: "Setting python3 mysql module PyMySQL"
pip:
name: PyMySQL
tags:
- lamp
- mariadb
- never

# Task 7: Setting ROOT password for mariadb-server
- name: "Setting ROOT password for mariadb-server"
ignore_errors: true
mysql_user:
login_user: "root"
login_password: ""
login_unix_socket: /var/lib/mysql/mysql.sock
name: "root"
password: "{{ mariadb_root_password }}"
tags:
- lamp
- mariadb
- never

# Task 8: Creating DB for the website
- name: "Creating DB for blog - {{blog_db_name}}"
mysql_db:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
login_unix_socket: /var/lib/mysql/mysql.sock
name: "{{ blog_db_name }}"
state: present
tags:
- lamp
- mariadb
- never

# Task 9: Creating extra user for WordPress
- name: "Creating extra user for WordPress - {{extra_user_name}}"
mysql_user:
login_user: "root"
login_password: "{{ mariadb_root_password }}"
login_unix_socket: /var/lib/mysql/mysql.sock
name: "{{ extra_user_name }}"
password: "{{ extra_user_password }}"
host: "%"
priv: "{{ blog_db_name }}.*:ALL"
tags:
- lamp
- never
- mariadb

# Task 10: Downloading WordPress
- name: "Downloading WordPress using URL"
get_url:
url: "{{ wp_url }}"
dest: "/tmp/wordpress.tar.gz"
tags:
- lamp
- mariadb
- wordpress
- never

# Task 11: Extracting WordPress
- name: "Extracting WordPress"
unarchive:
src: "/tmp/wordpress.tar.gz"
dest: "/tmp/"
remote_src: true
tags:
- lamp
- mariadb
- wordpress
- never

# Task 12: Copying WordPress files to docroot
- name: "Copying WordPress files to docroot"
copy:
src: "/tmp/wordpress/"
dest: "/var/www/html/default/"
remote_src: true
owner: "{{ httpd_owner }}"
group: "{{ httpd_group }}"
tags:
- lamp
- mariadb
- wordpress
- never

# Task 13: Copying wp-config.php file to remote server
- name: "Copying wp-config.php file to remote server"
template:
src: "./wp-config.php.tmpl"
dest: "/var/www/html/default/wp-config.php"
owner: "{{ httpd_owner }}"
group: "{{ httpd_group }}"
tags:
- lamp
- mariadb
- wordpress
- never

# Task 14: Post Installation Cleanup
- name: "Post Installation Cleanup"
file:
path: "{{ item }}"
state: absent
with_items:
- "/tmp/wordpress/"
- "/tmp/wordpress.tar.gz"
tags:
- wordpress

# Task 15: Removing Packages
- name: "Removing Packages"
yum:
name: "{{ packages }}"
state: absent
tags:
- cleanup
- never

# Task 16: Removing Directories
- name: "Removing Directories"
file:
path: "{{ item }}"
state: absent
with_items:
- /etc/httpd/conf/httpd.conf
- /etc/httpd/conf.d/default.conf
- /var/www/html/default/
tags:
- cleanup
- never

The playbook consists of several tasks, each responsible for a specific action.

  • Task 1: Installing Packages: Installs the necessary packages for the LAMP stack.
  • Task 2: Enabling/Restarting services: Ensures that the required services are enabled and restarted.
  • Task 3: Creating httpd.conf from template: Generates the Apache HTTP server configuration file from a template.
  • Task 4: Creating Virtual host: Creates a virtual host configuration file for the default website.
  • Task 5: Creating docroot for the website: Creates the document root directory for the website.
  • Task 6: Setting python3 mysql module PyMySQL: Installs the PyMySQL module for Python.
  • Task 7: Setting ROOT password for mariadb-server: Sets the root password for the MariaDB server.
  • Task 8: Creating DB for the website: Creates the database for the website.
  • Task 9: Creating extra user for WordPress: Creates an additional user with privileges for the WordPress database.
  • Task 10: Downloading WordPress: Retrieves the WordPress source code from a specified URL.
  • Task 11: Extracting WordPress: Extracts the WordPress archive.
  • Task 12: Copying WordPress files to docroot: Copies the WordPress files to the document root directory.
  • Task 13: Copying wp-config.php file to remote server: Creates the wp-config.php file for WordPress.
  • Task 14: Post Installation Cleanup: Cleans up temporary files and directories.
  • Task 15: Removing Packages: Uninstalls the packages installed during the installation process.
  • Task 16: Removing Directories: Removes the directories created during the installation process.

Tags

Tags in Ansible allow you to selectively run specific tasks or groups of tasks within a playbook. You can assign tags to tasks using the tags attribute, and then specify the desired tags when running the playbook.

To run specific tasks using tags, you can use the --tags option with the ansible-playbook command. Here's an example command:

ansible-playbook lamp.yml --tags lamp

In the above command, only tasks tagged with lamp will be executed.

You can also exclude specific tasks using the --skip-tags option. For example, to skip tasks tagged with never, you can use the following command:

ansible-playbook lamp.yml --skip-tags never

In this case, tasks tagged with never will not be executed.

Additionally, you can list all the available tags in a playbook using the --list-tags option. Here's an example command:

ansible-playbook lamp.yml --list-tags

This command will display a list of all the tags defined in your playbook.

By leveraging tags, you can customize the execution of your playbook based on your specific requirements, allowing for greater flexibility and control.

Encryption

To encrypt the lamp.yml playbook using Ansible Vault, you can use the ansible-vault command. Ansible Vault provides a secure way to encrypt sensitive data within your playbooks.

Here’s the command to encrypt the lamp.yml file:

ansible-vault encrypt lamp.yml

After running this command, you’ll be prompted to enter and confirm a password to protect the encrypted file. Make sure to remember this password as you’ll need it to decrypt the file later.

Once the lamp.yml file is encrypted, its contents will be scrambled and unreadable without decrypting it first.

To run the playbook with an encrypted file, you can use the ansible-playbook command along with the --ask-vault-pass option to provide the password:

ansible-playbook lamp.yml --ask-vault-pass

When prompted, enter the password that you used to encrypt the file.

Note that you can also provide the password through a file or a password file reference using the --vault-password-file option, which can be useful when automating the execution of encrypted playbooks.

By encrypting the playbook using Ansible Vault, you can protect sensitive information such as passwords, API keys, or any other confidential data that might be present in your playbook.

For added security, I have taken the following steps:

Firstly, I created a Secrets Manager entry in AWS to securely store my vault password. This ensures that sensitive information is kept encrypted and inaccessible to unauthorized users.

Next, I created an IAM policy that grants read access to the Secrets Manager.

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SecretsManagerReadAccess",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:ap-south-1:939486479455:secret:vault_pass-yAAbi4"
}
]
}

After creating the policy, I created an IAM role that uses this policy. This role will be attached to my master machine, allowing it to retrieve the vault password securely from Secrets Manager.

To retrieve the vault password from Secrets Manager, I created a bash script. The script uses the AWS CLI command aws secretsmanager get-secret-value and passes the secret ID as an argument (vault_pass). The script then extracts the password value from the JSON response using jq and stores it in the secret_value variable.

Here is an example of the bash script:

#!/bin/bash

secret_id="vault_pass"
secret_value=$(aws secretsmanager get-secret-value --secret-id "$secret_id" --query 'SecretString' --output text | jq -r '.vault_pass')
# Use the secret value in further operations
if [ -n "$secret_value" ]; then
# Perform desired operations using the secret value
echo "Retrieved the vault password: $secret_value"
else
echo "Failed to retrieve the vault password"
fi

By running this script, I can obtain the vault password securely from Secrets Manager.

Finally, to use the vault password in my Ansible playbook, I pass the script as a command-line argument along with the ansible-playbook command. This ensures that the playbook has access to the vault password when needed.

ansible-playbook -i inventory lamp.yml --vault-password-file <path/to/bash/script.sh>

Conclusion

By using the provided Ansible playbook and the AWS Secrets Manager script, you can automate the installation and configuration of a LAMP stack. This allows for efficient management of infrastructure and simplifies the deployment process.

--

--

Nidhin B

AWS | Devops | Linux and Related contents , connect with me on linkedin - linkedin.com/in/nidhinbabukuttan/