Ansible: Automating Apache Web Server Configuration over AWS with Dynamic Inventory
Ankit Kumar
Platform Engineer @ Brevo | Kubernetes | Python | Linux | Cloud | RHCE | RHCSA
Today we're going to launch an EC2 instance running on RHEL8 over AWS Cloud and configure Apache Web Server on top of it. The interesting part is that we're going to automate this entire process with the help of Ansible. And this time we'll be using the concept of dynamic inventory. For this you require an AWS account, you may go with the free tier account provided by AWS, as it has all we need.
What's a dynamic inventory?
A dynamic inventory is a script which when called upon, goes to a specified infrastructure (in this case, AWS EC2) over the network, fetches the public IP of all active instances and updates them in the host list. It comes very handy while working with AWS EC2 instances whose IP changes every time it's stopped and restarted.
Since now you know something about dynamic inventory, so let's get started:
Step 1: Updating Ansible's configuration file
We'll have to update the configuration file of Ansible present at /etc/ansible/ansible.conf directory. This includes providing the path of you inventory and the private key file (for authentication), a few other parameters and providing privilege escalation.
[defaults] inventory = /etc/myverse/myinventory host_key_checking = False private_key_file = /etc/myverse/myroles/awsec2setup/files/mykey951753.pem remote_user = ec2-user ask_pass = False become = TRUE [privilege_escalation] become = TRUE become_user = root become_method = sudo become_ask_pass = FALSE
Step 2: Downloading the scripts and updating them
For configuring dynamic inventory we'll have to download two per-created scripts (i.e. ec2.py and ec2.ini). The GitHub repository link is provided below:
https://github.com/ansible/ansible/tree/stable-2.9/contrib/inventory
To download the files directly on your local system use the following commands:
cmd# wget https://raw.githubusercontent.com/ansible/ansible/stable-2.9/contrib/inventory/ec2.ini cmd# wget https://raw.githubusercontent.com/ansible/ansible/stable-2.9/contrib/inventory/ec2.py
Note: Download/Save the files in the same directory that you provided in the Ansible's configuration file as inventory.
Since we're using a Linux based OS so we'll have to modify the shebang in ec2.py file as follows:
To make this script an executable use the following command:
cmd# chmod +x ec2.py
Now, the name of the file will be displayed in green (which represents an executable),
Also do the same for ec2.ini file.
Now, whenever Ansible will come to this directory as directed by it's configuration file and find this executable script it'll automatically run it. And the ec2.py will go to the specified AWS account through the help of boto module and fetch the public IPs of all active instances.
Note: If you don't have the boto module installed then use the following command:
cmd# pip3 install boto
To initialize use the following commands:
cmd# export AWS_REGION='ap-south-1' cmd# export AWS_ACCESS_KEY_ID=your_AWS_Access_Key_ID cmd# export AWS_SECRET_ACCESS_KEY=your_AWS_Secret_Key
Now, you can run the following command to list all host's IPs:
cmd# ansible all --list-hosts
Note: It takes some time so don't panic, just sit back and relax.
In my case, currently there are zero active instances so no host IP is being displayed.
Step 3: Launching AWS EC2 instance
Before we write the playbook we need to collect some information as per our EC2 instance requirements. This can either be done through the CLI or from the WebUI's EC2 console management window. I'm going with the WebUI.
region
image
instance_type
count and vpc_subnet_id
group_id
key_name
aws_access_key and aws_secret_key
Note: You get the Secret access key only once i.e. when you create the user in IAM services.
Once we have all that information we can start writing our playbook,
- hosts: localhost vars_files: - securevault.yml tasks: - name: Provisioning OS in AWS cloud ec2: region: "ap-south-1" image: "ami-052c08d70def0ac62" instance_type: "t2.micro" count: 1 vpc_subnet_id: "subnet-8b2b27e3" group_id: "sg-0636cf1d8d7737ecf" instance_tags: Name: task2 key_name: "mykey951753" assign_public_ip: yes state: present wait: yes aws_access_key: "{{ acc_key_id }}" aws_secret_key: "{{ sec_acc_key }}" register: log - debug: var: log
It's always a good idea to save the sensitive data on another file and hash it instead of storing it in plane text format. And for that Ansible provides a service called vault which is capable of hashing and locking the files securely. As you can see, I've used two variables (i.e. acc_key_id and sec_acc_key) which is stored in another file named securevault.yml present in the same directory. The securevault.yml file contains the value of both the variables and as it's name suggests, it's been hashed and locked using Ansible-vault.
To create a file with vault and to associate with unique id use:
cmd# ansible-vault create --vault-id ankit@prompt abc.yml
To hash and lock a pre-created file using Ansible-vault use the following command:
cmd# ansible-vault encrypt secure.yml
It'll prompt for a password, provide that, confirm it and lock the file.
To edit a file locked with Ansible-vault use the following command:
cmd# ansible-vault edit secure.yml
It'll prompt for the password, provide that to edit the file. Once you exit the file it'll automatically get locked again.
Remember, one can only access, read, or modify the contents of this file if he/she has the password.
Now, let's run the playbook:
cmd# ansible-playbook --vault-id ankit@prompt awsec2.yml
It'll prompt for the vault password, provide it and wait for some time.
You can also view it in the EC2 management console on WebUI.
Now if you use the following command it'll fetch the IP of this instance dynamically:
cmd# ansible all --list-hosts
To ensure that you are properly use the ping module:
cmd# ansible all -m ping
Step 4: Configuring Apache Web Server
The playbook for configuring Apache Web Server or httpd goes something like this:
- name: Configuring Apache Webserver hosts: all tasks: - name: Installing httpd software package: name: "httpd" state: present when: ansible_distribution == "RedHat" - name: Copying webpages copy: src: "/etc/myverse/mydata/html_files/" dest: "/var/www/html/" mode: preserve - name: Enabling web server service: name: "httpd" state: started enabled: yes
Here, I'm copying the contents from the local directory /etc/myverse/mydata/html_files/ into the directory /var/www/html/ present on the instance. The html_files directory contains a demo webpage named home.html that we'll be hosting on the web server.
Note: Only the webpages present in /var/www/html/ directory will be hosted.
Now, let's run the playbook,
cmd# ansible-playbook httpdaws.yml
Finally, the web server has been configured over the EC2 instance. You can visit the webpage home.html and see it's content using the public IP of the instance.
You can merge both the playbooks so that you have to run the command only once.
- name: Launching EC2 instance on AWS hosts: localhost vars_files: - securevault.yml tasks: - name: Provisioning OS in AWS cloud ec2: region: "ap-south-1" image: "ami-052c08d70def0ac62" instance_type: "t2.micro" count: 1 vpc_subnet_id: "subnet-8b2b27e3" group_id: "sg-0636cf1d8d7737ecf" instance_tags: name: task2 key_name: "mykey951753" assign_public_ip: yes state: present wait: yes aws_access_key: "{{ acc_key_id }}" aws_secret_key: "{{ sec_acc_key }}" register: log - debug: var: log - name: Configuring Apache Webserver hosts: all tasks: - name: Installing httpd software package: name: "httpd" state: present when: ansible_distribution == "RedHat" - name: Copying webpages copy: src: "/etc/myverse/mydata/html_files/" dest: "/var/www/html/" mode: preserve - name: Enabling web server service: name: "httpd" state: started enabled: yes
Thanks...
Hope you liked it...
See you again...!!!