Creating an Infrastructure, by integrating Terraform and Ansible on AWS

Akhilesh Jain
12 min readMar 26, 2021

--

In this article, we will create an Infrastructure, which includes an EC2 Instance on which Wordpress Application will be installed and for the backend purpose for this application will be MySQL Database Engine, which will be created using AWS-RDS Service.

A similar type of Infrastructure, I have created using AWS-RDS fore backend and Kubernetes Pods for frontend which have wordpress application deployed on it. Link for the following article is:

So, to create this Infrastructure and configuring the same, we will use Automation Tool Terraform and Configuration Management Tool as Ansible.

What is Terraform?
Terraform is an open-source infrastructure as code software tool that provides a consistent CLI workflow to manage hundreds of cloud services.
→ Basically I am using this tool to deploy RDS Service of AWS, which will create this Engine with code.

To Install Terraform and use it, I have created a article, do refer this:

RDS — This is a Database Service provided by AWS, In this Service I will be using MySQL Engine.

What is Ansible?
Ansible is a Configuration Management tool through which we can create and configure servers, clusters, VM’s and other things very easily.
→ So, in this Task, I will be using Ansible to create an EC2 Instance, and then installing Wordpress on it and configure it. Configuring here means some credentials that we have to give it on browser, instead we can give it initially also. In later part we will see to that file also.

So Let’s Start:

Firstly, we will be Creating our DB Instance using Terraform. I have used module approach for creating the same, as it is a better way of creating and managing it. Using this, we can create multiple Deployments of Services at the same time.
So, Following is the Structure of the folder:

So in this Folder ‘Task_18’, I have created:

  • 1 folder which contains a file ‘rds.tf’, which will create multiple resources like Configuring to AWS, Creating Security Group, Creating a DB Instance, Creating a local file so that, all the credentials I want can be fetched and copied easily.
  • ‘main.tf’ file which contains a module which will be called, when we run this file. In this module, I have passed some variables to the rds.tf file.
  • ‘variable.tf’ file contains 3 variables, which contains the value of my name, username, password (of this username) of the Database.

→ These variables can only be transferred to the inner code of modules, if the main file and the variable file are at the same location.

Following is the code for main.tf file:

# Initializing Modulemodule "rds" {
source = "./rds"
rdsusername = var.rdsusername
rdspasswd = var.rdspasswd
# rdsdbname = var.rdsdbname
}

Following is the code for variable.tf file:

#WORDPRESS_DB_USER (Username)
variable "rdsusername" {
type = string
default = "admin"
description = "Username for database"
}
#WORDPRESS_DB_PASSWORD (Password)
variable "rdspasswd" {
type = string
default = "admin12345"
description = "Password for AWS-RDS MySQL Database"
}
# #WORDPRESS_DB_NAME(Database Name)
# variable "rdsdbname" {
# type = string
# default = "mysqldb"
# description = "Name of AWS-RDS MySQL Database"
# }

Following is the code for main.tf file:

# Declaring Variables
variable "rdspasswd" {}
variable "rdsusername" {}
# variable "rdsdbname" {}
# Logging into AWS using IAM role
provider "aws" {
region = "ap-south-1"
profile = "akhil"
}
# Using Default VPC
resource "aws_default_vpc" "default" {
tags = {
Name = "Default VPC"
}
}
# Creating a Security Group for our DB Instance
resource "aws_security_group" "sg" {
name = "db-wizard"
description = "Allow Database inbound traffic"
vpc_id = aws_default_vpc.default.id
ingress {
description = "MYSQL/Aurora"
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "sg-for-db"
}
}
# Creating DB Instance in RDS
resource "aws_db_instance" "default" {
identifier = "wpdatabase"
engine = "mysql"
engine_version = "5.7.21"
name = "mysqldb"
username = var.rdsusername
password = var.rdspasswd
allocated_storage = 20
max_allocated_storage = 25
storage_type = "gp2"
availability_zone = "ap-south-1b"
instance_class = "db.t2.micro"
port = 3306
publicly_accessible = true
skip_final_snapshot = true
vpc_security_group_ids = [ "${aws_security_group.sg.id}" ]
tags = {
Name = "awsrds"
}
depends_on = [
aws_security_group.sg,
]
}
#WORDPRESS_DB_HOST (Database Host)
output "rds_dbhost" {
value = aws_db_instance.default.endpoint
}
#WORDPRESS_DB_NAME(Database Name)
output "rds_dbname" {
value = aws_db_instance.default.name
}
# Creating a Local file, which contains details of our Login Details #in Wordpressresource "local_file" "credentials" {
content = "WORDPRESS_DB_HOST => ${aws_db_instance.default.endpoint}\n WORDPRESS_DB_USER ${aws_db_instance.default.username}\n WORDPRESS_DB_PASSWORD ${aws_db_instance.default.password}\n WORDPRESS_DB_NAME ${aws_db_instance.default.name}"
filename = "details"
}

So now we will run the Code using the Terminal:

cmd> terraform init

→ This command is used once to install the required plugins.

cmd> terraform apply --auto-approve

→ This command will deploy everything we have written on our Code.

So, until this is created, Lets see the Code written on Ansible VM.

As I have used the concept of Dynamic Inventory in Ansible, which will automatically fetch the results from AWS Console that, how many Instances with their Names and all are there currently running.

So, for this also, I have created a article before which uses the concept of Dynamic Inventory. Following is the link:

Following is the Directory Structure of the Required Task in my Anisble VM, on which my Ansible is pre-installed.

  • ‘ansible.cfg’ — This file is the configuration file which ansible uses to see the hosts, permissions to the user, location of roles, keys etc.
[defaults]
inventory = ./hosts
host_key_checking = false
remote_user = ec2-user
private_key_file = ./key123.pem
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
  • ‘key123.pem’ — This is the private key attached to our Instance.
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAhxiL2lp7c/uGAWk6X733iaT2soDRPvVQQVFStI1tfax+msZg
LUS9VKip5SygH5dJsN62EaqEbWqoR6ES0tPkiSs6HSydYs2LD54yjCJxCVkJhSrc
cG52X6dpDcSEhQPeJt2hMqnYvW2L7lfI6SOjSaNwXHSvhtmnckEx1xYg8JSP/L76
w9zhuoiWqXX9g4EcSPkWmMperVIli4mYJzD5/MbgWGtbnVJlZ3MlPa9vrRqNf25A
MPkAAKQ8V/qCZ3ZlQI+T64yDKEg5NAkFW2pMrbcmlkfQjKRGrtv7rfo1P0on5KNL
Pw2M+BsXdnDdS+bkNVM8225PzbfFZZOc5gHOEwIDAQABAoIBADx7BMFwmKxIAqpH
DdcnGNcKf1dSzFq/QHq9iaVDW61TuCpafVxG1ew8xjLPU7BQ7rC8RA6MpFTH1yaa
Oe8g5cNzEsVU3/EHzCXl0QNjt+9TaSuxEJdVHLGeJS4AuMNEBASqXCxuVZYYoPjH
XC7jwYqKHReHNb3NW2WPQlzkj0Kk1WAEIRMkaOJZNzXhqSv2AjEKmNccLZXF1VbT
+se/05DkOhYpx1Ibb/25zK7DJy6szyLfEqZzIKlu9+5xMSPKrbOKivYg3iA8+hyU
HfGDKIVfbmaZOkV/drKF2LMrZiy/zFgWCQnX7HPCQv2IJzg7+SWSv4cOUTuwptbl
aDfNGmECgYEA6CxLL1DVEMhjXcIxMYNMX5f0WbPuC2NGAkJ6M/uoaJL9JkmO/eid
nwo7MPep5fP6dQgsxHzEm4q+YJZ82399ERw0dd1povrgNnEQhvZQ99bociZ4mlQF
Hv4EWdWLULTolmYINwldPfMTXyegACf/jEc2XkXBt4ALMyOBvadhy78CgYEAlPXR
fqxTRbN6wFzX1/4efHtMem1ilYO+fZGknVcsDQ/QiptE861aU+DQsg+lpcSJwq58
LHcK1TimSp/fB6TBgjoh0qDB6wWgddogJ4L/59aQRMUuT1OD7jD1sFr/Xy6S6Lar
0lov2jVPRzrcIV5ew2CW6MKTbb3Lip5hmBIUYq0CgYAPp6TuLNIhDpH8qXJttz+4
FmPohIRhijEXR+o7hRWG75pYMY+NuVifd64kEB8JnVje+U0jdpI/Nqy9kIgcuMzz
EWbMJ8DOt4HUyezmXMd63qfPwp5RMacivtgGQqrhJ0Gjmn+lTmFWIwTEXsSgHhJS
IB8fXi7As8aNjTBbXGTwuwKBgH+1XJ2oql/4p0Xik17nzEVXBFN2Em4zHA7V3fbT
NL4iD921juEHf4ioFuSCC7daD+2r4GPSz6PMRK138SPBefHnWvYUwwx2r4I6txSI
+FNQnjGHh9OUu2hr60f+TDDTYjpH2nmmvp3q1IQyD2ZAXShOWDNIFlOgw6+dZ/iT
j4ylAoGBALNSARg4uzeT+GxVRPIdSsMRYKBxTs+lbx5L7v7SrCw5MEXB57GQcILj
D6weJP6+INGz6c3mX/WILCvECEu0sLQUa6tW3gGG3eTy/lsr0eNF24RrXv0XpCvF
I3BFRKmBAxQRjsk3uwjBPa30JlJ/oin6rhA1cddNx7+ZpWR7+bLc
-----END RSA PRIVATE KEY-----
  • ‘vars.yml’ — This is the file which consists of the variables which are actually some credentials and some are like URL and destinaton for the wordpress application.
WORDPRESS_DB_HOST: wpdatabase.cwohfiimq9cu.ap-south-1.rds.amazonaws.com:3306
WORDPRESS_DB_NAME: mysqldb
WORDPRESS_DB_USERNAME: admin
WORDPRESS_DB_PASSWORD: admin12345
dest_var: /var/www/html/
url_var: https://wordpress.org/latest.tar.gz
  • ‘vault.yml’ — This file is used to store the credentials of my IAM User.
ak: 'xxx--your access key---'
sak: '---your-secret access key---'
  • ‘vault_pass_file.yml’ — This is the file which consists of password of vault.
#This is the password of my Vault
1
  • ‘setup.yml’ — This is the playbook, which is used to create EC2 Instance, Security Group and installing some python libraries.
---
- name: "Creating 1 VM and some python libaries"
hosts: localhost
gather_facts: false
vars_files:
- vault.yml
tasks:
- name: "installing boto"
pip:
name: "boto"
executable: pip3
- name: "installing boto3"
pip:
name: "boto3"
executable: pip3
- name: "creating security group"
ec2_group:
aws_access_key: "{{ ak }}"
aws_secret_key: "{{ sak }}"
name: 'launch-wizard-1'
description: 'sg with rule descriptions'
vpc_id: 'vpc-224d514a'
tags:
Name: "task18-sg"
region: "ap-south-1"
rules:
- proto: tcp
from_port: 22
to_port: 22
cidr_ip: 0.0.0.0/0
rule_desc: allow all on port 22 for ssh
- proto: tcp
cidr_ip: 0.0.0.0/0
ports:
- 80
rule_desc: allow all on port 80 for webserver
- proto: all
from_port: 0
to_port: 0
cidr_ip: 0.0.0.0/0

- name: "Creating Wordpress Instance"
ec2:
count: 1
image: "ami-0a9d27a9f4f5c0efc"
instance_type: t2.micro
region: "ap-south-1"
wait: yes
instance_tags:
Name: ArthTask18
group: "launch-wizard-1"
key_name: "key123"
state: present
aws_access_key: "{{ ak }}"
aws_secret_key: "{{ sak }}"
  • ‘playbook.yml’ — This is the playbook which is used to install the Wordpress Application and Install some Packages over the Instance, Start and Stop the services and many more..
---
- name: Deploying HTTPD Servers
hosts: tag_Name_ArthTask18
vars_files:
- vars.yml
tasks:
- name: Install Packages
yum:
name:
- "httpd"
- "php-mysqlnd"
- "php-fpm"
- "mariadb-server"
- "php-json"
- "firewalld"
state: present
- name:
get_url:
url: "{{ url_var }}"
dest: "/root/"

- name: Extracting Wordpress Package
unarchive:
src: "/root/wordpress-5.7.tar.gz"
dest: "/root/"
remote_src: yes
- name: stop firewalld
service:
name: "firewalld"
state: stopped
- name: service httpd start
service:
name: "httpd"
state: started
enabled: yes
- name: Copying Extracted file to webserver default location
copy:
src: "/root/wordpress"
dest: "{{ dest_var }}"
remote_src: yes

- name: Making Selinux Permissive
selinux:
policy: targeted
state: permissive
- name: copy content
template:
src: "wp-config.j2"
dest: "{{ dest_var }}wordpress/wp-config.php"
force: true
  • ‘wp-config.j2’ — This file is a jinja template, which has some features through which we can add some variables and some loops, but this file consists of some variables currently.
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the
* installation. You don't have to use the web site, you can
* copy this file to "wp-config.php" and fill in the values.
*
* This file contains the following configurations:
*
* * MySQL settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://wordpress.org/support/article/editing-wp-config-php/
*
* @package WordPress
*/
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', "{{ WORDPRESS_DB_NAME }}" );
/** MySQL database username */
define( 'DB_USER', "{{ WORDPRESS_DB_USERNAME }}" );
/** MySQL database password */
define( 'DB_PASSWORD', "{{ WORDPRESS_DB_PASSWORD }}" );
/** MySQL hostname */
define( 'DB_HOST', "{{ WORDPRESS_DB_HOST}}" );
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
/**#@+
* Authentication Unique Keys and Salts.
*
* Change these to different unique phrases!
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
*
* @since 2.6.0
*/
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
/**#@-*//**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';
/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*
* For information on other constants that can be used for debugging,
* visit the documentation.
*
* @link https://wordpress.org/support/article/debugging-in-wordpress/
*/
define( 'WP_DEBUG', false );
/* That's all, stop editing! Happy publishing. *//** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
  • ‘hosts/ec2.py’ — This file consist of python code which is used to Create the Resources over AWS.
  • ‘hosts/ec2.ini’ —This file is used to take the Credentials of the IAM User created in AWS.
  • ‘wp-config-sample.php’ — This is the sample file in which we have to add the Database Details like Database Name, Username, Password, Hostname of RDS. To make it usable, we have to rename this file as wp-config.php .

OUTPUT SECTION:

→ Pre-created Key Pair

We have run the Terraform Script, so following has been created:

→ To run the Ansible Setup file:

ansible-playbook setup.yml --vault-pass-file=vault_pass_file

→ EC2 Instance and Security Group has been created:

—EC2 Instance

→ To run the playbook.yml

ansible-playbook playbook.yml

So, I have done SSH to the Instance and following are the Screenshots:

— To check the hosts and adding it to playbook.yml, using the following Command:

./hosts/ec2.py --list

— We can see that “tag_name_Arth_Task18”, is the required for us, there are other Host Groups also, which we can also take.

→ Now Using the Applications(Wordpress and MySQL), which we have created, a page comes in which we have to create a User, and after Creation, the following page comes:

So, I have Created a Post after Logging in.

After Logout and again logging in:

→ Now we have to check the Database(MySQL), if whatever we create are stored or not, so we login to the database and check:

mysql> Select + from wp_posts

→ So, As we can see that, Everything we created on Wordpress has been stored in AWS RDS Database.

→ So we can delete the Database which is in RDS, using terraform only. using the following command:

→ The resources created by Ansible can be deleted manually or using same file but we can edit the file and change state to absent, and run the Playbook again.

I have created a Video on how it works !!!

I have learned of these tools, under the mentorship of Mr. Vimal Daga Sir under Arth Training and Hybrid Multi Cloud Training.

I hope this article is Informative and Explanatory. Hope you like it !!!

Please give some claps, if you liked this article !!!

For any suggestions or if any reader find any flaw in this article, please email me to “akhileshjain9221@gmail.com”

Thank You Readers, for viewing this !!!

--

--

Akhilesh Jain
Akhilesh Jain

Written by Akhilesh Jain

I am a student and persuing under graduation in computer science and engineering.

No responses yet