Friday, June 7, 2019

Ansible modules and examples









Ansible modules are the small programs which will execute in remote systems through ssh protocol. The library of modules resides in any machines which is called by the playbooks on timely manner and executes it.  I will introduce some common modules which is used in ansible and its working examples ..

1. The setup module 

This module is used to pullout configuration details of remote machines which is called as facts.

Let's create a temp inventory file called inventory.1

[unixchips@unixchips-server ~]$ cat inventory.1
unixchips

use the below command to collect all the details from the remote host

[unixchips@unixchips-server ~]$ ansible -m setup -i inventory.1 all
[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host unixchips should use
/usr/bin/python3, but is using /usr/bin/python for backward compatibility with
prior Ansible releases. A future Ansible release will default to using the
discovered platform python for this host. See https://docs.ansible.com/ansible/
2.8/reference_appendices/interpreter_discovery.html for more information. This
feature will be removed in version 2.12. Deprecation warnings can be disabled
by setting deprecation_warnings=False in ansible.cfg.
unixchips | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.56.1",
            "192.168.2.16"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::800:27ff:fe00:0",
            "fe80::766b:d730:8352:3325"
        ],
        "ansible_apparmor": {
            "status": "enabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "01/11/2011",
        "ansible_bios_version": "81ET49WW (1.25 )",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/boot/vmlinuz-4.15.0-50-generic",
            "quiet": true,
            "ro": true,
            "root": "UUID=4f41e130-df1e-4a41-b2ae-2539efa5f605",
            "splash": true,
----------------<output omitted>------------------------------------------------------------

Use the below command to use the filter option to filter particular facts. Below command will provide the memory details 

[unixchips@unixchips-server ~]$ ansible -m setup -i inventory.1 unixchips -a 'filter=ansible_*_mb'
[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host unixchips should use /usr/bin/python3, but is using /usr/bin/python for backward 
compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See 
https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in 
version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
unixchips | SUCCESS => {
    "ansible_facts": {
        "ansible_memfree_mb": 3482, 
        "ansible_memory_mb": {
            "nocache": {
                "free": 5136, 
                "used": 2637
            }, 
            "real": {
                "free": 3482, 
                "total": 7773, 
                "used": 4291
            }, 
            "swap": {
                "cached": 0, 
                "free": 7986, 
                "total": 7986, 
                "used": 0
            }
        }, 
        "ansible_memtotal_mb": 7773, 
        "ansible_swapfree_mb": 7986, 
        "ansible_swaptotal_mb": 7986, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

Now if we want too get the outputs of the facts in a play book we have use the facts like below . Below ply book will provide the memory details 

[unixchips@unixchips-server ~]$ cat os_mem.yml 
- name: print out operating system
  hosts: all
  gather_facts: True
  tasks:
  - debug: var=ansible_memory_mb

[unixchips@unixchips-server ~]$ ansible-playbook os_mem.yml 
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, 
but still be user configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details


PLAY [print out operating system] *************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [debug] **********************************************************************************************************************************
ok: [unixchips] => {
    "ansible_memory_mb": {
        "nocache": {
            "free": 4849, 
            "used": 2924
        }, 
        "real": {
            "free": 3193, 
            "total": 7773, 
            "used": 4580
        }, 
        "swap": {
            "cached": 0, 
            "free": 7986, 
            "total": 7986, 
            "used": 0
        }
    }
}

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

2.The File module 

first let's take the file details using below command 
---------------------------------------------------------------------------------
[unixchips@unixchips-server ~]$ ansible unixchips -m file -a 'path=/etc/fstab'
unixchips | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "gid": 0, 
    "group": "root", 
    "mode": "0664", 
    "owner": "root", 
    "path": "/etc/fstab", 
    "size": 594, 
    "state": "file", 
    "uid": 0
}

create a file using file module 
---------------------------------------------------------
[unixchips@unixchips-server ~]$ cat file.yml 
- hosts: all
  tasks:
  - name: Ansible create file if it doesn't exist example
    file:
      path: "/tmp/devops_server.txt"
      state: touch

[unixchips@unixchips-server ~]$ ansible-playbook file.yml 
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, 
but still be user configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details


PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible create file if it doesn't exist example] ****************************************************************************************
changed: [unixchips]

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

if we login to the unixchips machine we can see that file is created 

unixchips@unixchips:/tmp$ ls -lrt |grep *.txt
-rw-rw-r-- 1 unixchips unixchips    0 Jun  4 13:47 devops_server.txt
-------------------------------------------------------------------------------------
Now let's create a file with owner as root and permission as 777

[unixchips@unixchips-server ~]$ cat file.yml 
- hosts: all
  become: yes
  tasks:
  - name: Ansible create file if it doesn't exist example
    file:
      path: "/tmp/foo.txt"
      state: touch
      mode: 0777
      owner: root

[unixchips@unixchips-server ~]$ ansible-playbook file.yml 

PLAY [all] *******************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************
ok: [unixchips]

TASK [Ansible create file if it doesn't exist example] ***********************************************************************************************
changed: [unixchips]

PLAY RECAP *******************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

if we check the output 

unixchips@unixchips:/tmp$ ls -lrt |grep foo
-rwxrwxrwx 1 root      root         0 Jun  4 14:16 foo.txt


To create a file using a content we have to use copy module as below
-----------------------------------------------------------------------------------------
[unixchips@unixchips-server ~]$ cat text.yml
- hosts: all
  tasks:
  - name: Ansible create file with content example
    copy:
      dest: "/tmp/unixchips.txt"
      content: |
        welcome to unixchips 

unixchips@unixchips:/tmp$ cat unixchips.txt 
welcome to unixchips 
*************************************************************************

3. The Pause module 

If we are in a situation where certain actions needs to be performed  after the inputs from the user we have to pause the playbook execution till that time. The pause module is using for that

[unixchips@unixchips-server ~]$ cat pause.yml
- name: Pause prompt on localhost
  hosts: localhost
  tasks:
    - name: Prompt
      pause:
        prompt: "Do you want to install mysql (yes/no)?"

output
----------


[unixchips@unixchips-server ~]$ ansible-playbook pause.yml 

PLAY [Pause prompt on localhost] **************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [localhost]

TASK [Prompt] *********************************************************************************************************************************
[Prompt]
Do you want to install mysql (yes/no)?:
[ [ok: [localhost]

PLAY RECAP ************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

4. The WaitFor module 

The waitfor module is using to check some conditions , for example in below example we will wait the apache2 need to be installed and started to complete the execution

[unixchips@unixchips-server ~]$ cat waitfor.yml
--- # The wait for module
- hosts: all
  sudo: yes
  gather_facts: no
  tasks:
    - name: Installing Apache Tomcat
      action: apt name=apache2 state=installed
    - name: Waiting for the port 80 to listen
      wait_for:
        port: 80
        state: started


output
------------
[unixchips@unixchips-server ~]$ ansible-playbook waitfor.yml 
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, 
but still be user configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

[DEPRECATION WARNING]: Instead of sudo/sudo_user, use become/become_user and make sure become_method is 'sudo' (default). This feature will be
 removed in version 2.9. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.

PLAY [all] ************************************************************************************************************************************

TASK [Installing Apache Tomcat] ***************************************************************************************************************
 [WARNING]: Could not find aptitude. Using apt-get instead

[DEPRECATION WARNING]: State 'installed' is deprecated. Using state 'present' instead.. This feature will be removed in version 2.9. 
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host unixchips should use /usr/bin/python3, but is using /usr/bin/python for backward 
compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See 
https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in 
version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
changed: [unixchips]

TASK [Waiting for the port 80 to listen] ******************************************************************************************************
ok: [unixchips]

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

We can see 2 tasks are get executed succesfully and ansible wait for the port 80 comes up to complete the task

from the client side we can see apache2 is installed and listening to the port

unixchips@unixchips:/tmp$ service apache2 status
● apache2.service - LSB: Apache2 web server
   Loaded: loaded (/etc/init.d/apache2; bad; vendor preset: enabled)
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: active (running) since Thu 2019-06-06 00:14:46 IST; 1min 16s ago
     Docs: man:systemd-sysv-generator(8)
   CGroup: /system.slice/apache2.service
           ├─15058 /usr/sbin/apache2 -k start
           ├─15061 /usr/sbin/apache2 -k start
           └─15062 /usr/sbin/apache2 -k start

Jun 06 00:14:45 unixchips systemd[1]: Starting LSB: Apache2 web server...
Jun 06 00:14:45 unixchips apache2[15036]:  * Starting Apache httpd web server apache2
Jun 06 00:14:45 unixchips apache2[15036]: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.
Jun 06 00:14:46 unixchips apache2[15036]:  *
Jun 06 00:14:46 unixchips systemd[1]: Started LSB: Apache2 web server.

***********************************************************

5. The yum module 

The yum module can be tested in local host ( ansible server) as the client is in ubuntu . We can install any packages using the yum module and the sample is below 

[unixchips@unixchips-server ~]$ cat waitfor.yml 
--- # The wait for module
- hosts: localhost
  sudo: yes
  gather_facts: no
  tasks:
    - name: Installing Ansible
      action: yum name=ansible state=installed
           

[unixchips@unixchips-server ~]$ ansible-playbook yum.yml 
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, 
but still be user configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [localhost] ******************************************************************************************************************************

TASK [Installing Ansible] *********************************************************************************************************************
ok: [localhost]

PLAY RECAP ************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

In the above example as we have already installed the ansible the task will be executed succesfully without any changes. If we want to update any packagesusing yum module instead of state installed we need to give as latest. It will search in the repository and update to the latest version 

6. The shell module 

If we need to execute multiple commands in a remote machine we can use the shell module. For example if we want to create multiple files in the client machine use the shell module as below

[unixchips@unixchips-server ~]$ cat shell.yml
- hosts: all
  sudo: yes
  tasks:
    - name: Ansible shell module multiple commands
      shell: "touch {{ item }} "
      with_items:
        - hello.txt
        - hello2.txt
        - hello3.txt
      args:
       chdir: /tmp

[unixchips@unixchips-server ~]$ ansible-playbook shell.yml 

PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible shell module multiple commands] *************************************************************************************************
changed: [unixchips] => (item=hello.txt)
changed: [unixchips] => (item=hello2.txt)
changed: [unixchips] => (item=hello3.txt)
 [WARNING]: Consider using the file module with state=touch rather than running 'touch'.  If you need to use command because file is
insufficient you can add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.

This module can be useful if we need to execute multiple commands in remote machine. For example 

[unixchips@unixchips-server ~]$ cat shell_new.yml 
- hosts: all
  sudo: yes
  tasks:
    - name: Ansible shell module multiple commands
      shell: |
        ls -lrt 
        touch "ubuntu.txt"
      args:
       chdir: /tmp
      register: SEQOUT
    - debug: msg={{SEQOUT.stdout_lines}}

[unixchips@unixchips-server ~]$ ansible-playbook shell_new.yml 

PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible shell module multiple commands] *************************************************************************************************
changed: [unixchips]

TASK [debug] **********************************************************************************************************************************
ok: [unixchips] => {
    "msg": [
        "total 24", 
        "drwx------ 3 root      root      4096 Jun  5 11:38 systemd-private-7ccc3280b7b2446c8f016430e315661d-systemd-timesyncd.service-vM8Mv4", 
        "drwx------ 3 root      root      4096 Jun  5 11:38 systemd-private-7ccc3280b7b2446c8f016430e315661d-rtkit-daemon.service-t32t2T", 
        "-rw------- 1 unixchips unixchips    0 Jun  5 11:46 config-err-q2fjTB", 
        "-rw-rw-r-- 1 unixchips unixchips    0 Jun  5 11:46 unity_support_test.0", 
        "drwx------ 3 root      root      4096 Jun  5 11:46 systemd-private-7ccc3280b7b2446c8f016430e315661d-colord.service-YGKa2X", 
        "-rw-rw-r-- 1 unixchips unixchips   22 Jun  5 13:09 unixchips.txt", 
        "drwx------ 2 unixchips unixchips 4096 Jun  6 06:55 gnome-software-GHVV2Z", 
        "-rw-r--r-- 1 root      root         0 Jun  6 13:11 hello.txt", 
        "-rw-r--r-- 1 root      root         0 Jun  6 13:11 hello2.txt", 
        "-rw-r--r-- 1 root      root         0 Jun  6 13:11 hello3.txt", 
        "drwx------ 2 root      root      4096 Jun  6 13:58 ansible_command_payload_jmXznO"
    ]
}

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

7. The command module 

command module can also be used to execute system commands, but we cannot execute multiple commands using this. For example if we want to check the uptime of the unixchips node 

[unixchips@unixchips-server ~]$ cat command.yml 
- hosts: all
  sudo: yes
  tasks:
    - name: Ansible shell module multiple commands
      command: "uptime"
      register: SEQOUT
    - debug: msg={{SEQOUT.stdout_lines}}

[unixchips@unixchips-server ~]$ ansible-playbook command.yml 


PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible shell module multiple commands] *************************************************************************************************
changed: [unixchips]

TASK [debug] **********************************************************************************************************************************
ok: [unixchips] => {
    "msg": [
        " 23:59:57 up 5 min,  2 users,  load average: 0.70, 1.38, 0.76"
    ]
}

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

8. The user module 

This module is used to create , delete users and also add/remove the users from the groups . For example , we can create the user using below playbook .

first we have to add the password in md5 format in the playbook, let's generate the password in encrypted format

[unixchips@unixchips-server ~]$ openssl passwd -1
Password:
Verifying - Password:
$1$C7ScacjV$xEeePriRC74kJlGdnT1jm.

Let's add the user first 

[unixchips@unixchips-server ~]$ cat useradd.yml 
- hosts: all
  sudo: yes
  tasks:
  - name: Ansible create user example.
    user:
      name: ansibletest
      password: $1$C7ScacjV$xEeePriRC74kJlGdnT1jm.

[unixchips@unixchips-server ~]$ ansible-playbook useradd.yml 

PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible create user example.] ***********************************************************************************************************
changed: [unixchips]

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Now if we check the unixchips host we can see that the mentoned user is added 

root@unixchips:~# cat /etc/shadow | grep ansible
ansibletest:$1$C7ScacjV$xEeePriRC74kJlGdnT1jm.:18053:0:99999:7:::
-------------------------------------------------------------------------------------------
Let's add a user to an existing group as below 

[unixchips@unixchips-server ~]$ cat usrgrp.yml 
- hosts: all
  sudo: yes
  tasks:
  - name: Ansible create user example.
    user:
      name: ansibletest
      groups: testgroup

[unixchips@unixchips-server ~]$ ansible-playbook usrgrp.yml 

PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible create user example.] ***********************************************************************************************************
changed: [unixchips]

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


output 

root@unixchips:~# groups ansibletest
ansibletest : ansibletest testgroup

Add a user to multiple groups 
------------------------------------
Ok let's add the ansible test user to 2 more groups called aws and azure as below 

[unixchips@unixchips-server ~]$ cat append.yml 
- hosts: all
  sudo: yes
  tasks:
  - name: Ansible create user example.
    user:
      name: ansibletest
      groups: aws, azure      
      append: yes

[unixchips@unixchips-server ~]$ ansible-playbook append.yml 

PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible create user example.] ***********************************************************************************************************
changed: [unixchips]

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

root@unixchips:~# groups ansibletest
ansibletest : ansibletest testgroup aws azure

Now deleting the user 

[unixchips@unixchips-server ~]$ cat userdel.yml 
- hosts: all
  sudo: yes
  tasks:
  - name: Ansible create user example.
    user:
      name: ansibletest
      state: absent

[unixchips@unixchips-server ~]$ ansible-playbook userdel.yml 

PLAY [all] ************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [unixchips]

TASK [Ansible create user example.] ***********************************************************************************************************
changed: [unixchips]

PLAY RECAP ************************************************************************************************************************************
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

output
root@unixchips:~# id ansibletest
id: ‘ansibletest’: no such user

9.The file system & mount module 

This module is used to format the file system and mount it to the required mount point using the playbooks 

First i have created the disk as normal way

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048     2596473     1297213   83  Linux

now let's create a playbook to format the file system as ext3

[root@unixchips-server ~]# cat filesystem.yml 
--- #file system module example
- hosts: localhost
  sudo: yes
  gather_facts: no
  tasks:
    - name: format the disk
      filesystem: fstype=ext4 dev=/dev/sdb1 opts="-cc"

[root@unixchips-server ~]# ansible-playbook filesystem.yml 

PLAY [localhost] ***************************************************************

TASK [format the disk] *********************************************************

changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

we can check the status by manually mounting the file system to a directory.

Now let's mount it to the mount point using the mount module 

[unixchips@unixchips-server ~]$ cat mount.yml 
--- #file system module example
- hosts: localhost
  sudo: yes
  gather_facts: no
  tasks:
    - name: mount the file system 
      mount: name=/tmp/test src=/dev/sdb1 fstype=ext4 opts=rw state=present

[unixchips@unixchips-server ~]$ ansible-playbook mount.yml 

PLAY [localhost] ******************************************************************************************************************************

TASK [mount the file system] ******************************************************************************************************************
changed: [localhost]

PLAY RECAP ************************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

if we check the /etc/fstab the entry is added automatically 

[unixchips@unixchips-server ~]$ cat /etc/fstab |grep /dev/sdb
/dev/sdb1 /tmp/test ext4 rw 0 0

we can see the file system is mounted succesfully 

[unixchips@unixchips-server ~]$ mount |grep /dev/sdb
/dev/sdb1 on /tmp/test type ext4 (rw,relatime,seclabel,data=ordered)

10 The temple module 

The temple module is used when we need to copy any configuration files with in the nodes where any of the variables need to be changed automatically according the node details . We are using a temple called jinja2  temple with an extension of j2.

Let's create a jinja temple as beow inside the file directory of ansible server

[root@unixchips-server files]# cat test.conf.j2
# configuration file for testing

<connectivity>
  connection type {{ connectionType }}
</connectivity>


<accountInfo>
username {{ username }}
password {{ password }}
</accountInfo>

<system_info>
DistributionType {{ ansible_os_family }}
</DistributionType>

Now we will call this jinja temple inside the playbook using temple module . Lets create the playbook as below 

[unixchips@unixchips-server ~]$ cat templete.yml 
--- # Testing the J2 templete module
- hosts: localhost,ansible-test
  sudo: yes
  gather_facts: yes
  vars:
    username: unixchips
    password: Onm0bile
    connectionType: ssh
  tasks:
    - name: install the test config file 
      template: src=/etc/ansible/files/test.conf.j2 dest=/home/unixchips/test.conf owner=unixchips group=unixchips


[unixchips@unixchips-server ~]$ ansible-playbook templete.yml 

PLAY [localhost,ansible-test] *****************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [localhost]
ok: [unixchips]

TASK [install the test config file] ***********************************************************************************************************
changed: [localhost]
changed: [unixchips]

PLAY RECAP ************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
unixchips                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

if we check the test.conf in both the nodes we can see the output as below

in localhost ( ansible-server)
------------------------------------------------
[unixchips@unixchips-server ~]$ cat test.conf
# configuration file for testing

<connectivity>
  connection type ssh
</connectivity>


<accountInfo>
username unixchips
password Onm0bile
</accountInfo>

<system_info>
DistributionType RedHat
</DistributionType>

in client ( unixchips)
-----------------------------------------
unixchips@unixchips:~$ cat test.conf 
# configuration file for testing 

<connectivity>
  connection type ssh
</connectivity>


<accountInfo>
username unixchips
password Onm0bile
</accountInfo>

<system_info>
DistributionType Debian
</DistributionType>




We have many more modules which can be useful along with ansible for system operations and i will discus that in another part

Thank you for reading 












































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































Friday, May 31, 2019

Ansible installation and configuration

Purpose of the configuration management tool

Configuration changes in a large environment is a challenge for  complex IT architectures where manual alterations are not practical in many cases. Initially we  were using the customised images for the installation purpose for our organisation needs which was the first approach to the configuration management . But later where the changes are more after the installation or during the operation period we may need specific tools for the configuration management . Below are the some of the configuration management tools available in the market

ToolLanguageFLOSS version
CFEngineCCFEngine Community
PuppetRubyOpen Source Puppet
ChefRubyChef
AnsiblePythonAnsible
SaltStackPythonSaltOpen

What is ansible & Why is ansible  

Ansible is a simple and efficient automation tool for cloud provisioning , configuration management, application deployment , intra-service orchestration and many more. Designed for multi-tier deployments since day one, Ansible models your IT infrastructure by describing how all of your systems inter-relate, rather than just managing one system at a time.
It uses no agents and no additional custom security infrastructure, so it's easy to deploy - and most importantly, it uses a very simple language (YAML, in the form of Ansible Playbooks) that allow you to describe your automation jobs in a way that approaches plain English.


  • Ansible is an open source tool
  • Ansible is a agent less tool which is not a server client architecture (unlike puppet,chef, salt)  
  • It uses ssh protocol to connect to the nodes
  • Enterprise version is supported by redhat 
  • We can manage the inventory using simple text files 



Difference between Ansible & Ansible tower `

Ansible, an open source product is available on Windows, Linux/Unix Operating systems. Because of the inefficiencies in the then CM tools, the Ansible was developed. The features that are the main focus of Ansible are as follows:

Causing Of the Overheads by the Agents

Ansible is a unique tool that uses an agentless architecture. To deploy the modules to the nodes it generally relies on the tested SSH. The modules are stored temporarily in the nodes and thus help in controlling the machines over a standard protocol through JSON protocol. When the nodes are not being managed by it the resources are also not consumed because no daemons or programs are executed in the background.

No Dependence on Ruby

Other CM apps were dependent on Ruby, but, Ansible is not. Thus, to create commands in Puppet and Chef one needs to have knowledge of Ruby. As the creators have made Ansible in python, it is relatively easy to write the commands. The added advantage is that they can be written in any programming language. So, there is no requirement for one to learn Ruby.

Features of Ansible tower

  • Role-based access control: you can set up teams and users in various roles. These can integrate with your existing LDAP or AD environment
  • Job scheduling: schedule your jobs and set repetition option
  • Portal mode: this is a simplified view of automation jobs for newbies and less experienced Ansible users. This is an excellent feature as it truly lowers the entry barriers to starting to use Ansible.
  • Fully documented REST API: allows you to integrate Asible into your existing toolset and environment
  • Tower Dashboard: use this to quickly view a summary of your entire environment. Simplifies things for sysadmins while sipping their coffee
  • Cloud integration: Tower is compatible with the major cloud environments: Amazon EC2, Rackspace, Azure.

Installing the ansible 

I have configured centOS 7 as ansible server and ubuntu 16.4 as client , below are the steps of the configuration

1. First we need to install epel-release on the centos7

[root@unixchips-server ~]# yum install epel-release

Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile epel/x86_64/metalink | 7.2 kB 00:00 * base: mirror.dhakacom.com * epel: download.nus.edu.sg * extras: mirror.dhakacom.com * updates: centos-hcm.viettelidc.com.vn base | 3.6 kB 00:00 docker-ce-edge | 3.5 kB 00:00 docker-ce-stable | 3.5 kB 00:00

*******************<output is omitted>*****************

2. Now we need to install the ansible

[root@unixchips-server ~]# yum install ansible

(as i have already installed the ansible i am getting the below output)

[root@unixchips-server ~]# yum install ansible Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirror.dhakacom.com * epel: download.nus.edu.sg * extras: mirror.horizon.vn * updates: centos-hcm.viettelidc.com.vn Package ansible-2.8.0-2.el7.noarch already installed and latest version Nothing to do

3. Next step is to configure the ansible hosts .we have to move to /etc/ansible/hosts and configure the client details as below , where unixchips is the ubuntu client 

[ansible-test]
unixchips

4. Now login to the client machine and install the ansible as below 

unixchips@unixchips:~$ sudo apt-add-repository ppa:ansible/ansible

[sudo] password for unixchips: Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications— automate in a language that approaches plain English, using SSH, with no agents to install on remote systems. http://ansible.com/ More info: https://launchpad.net/~ansible/+archive/ubuntu/ansible Press [ENTER] to continue or ctrl-c to cancel adding it
gpg: keyring `/tmp/tmpmyotgwkm/secring.gpg' created gpg: keyring `/tmp/tmpmyotgwkm/pubring.gpg' created gpg: requesting key 7BB9C367 from hkp server keyserver.ubuntu.com gpg: /tmp/tmpmyotgwkm/trustdb.gpg: trustdb created gpg: key 7BB9C367: public key "Launchpad PPA for Ansible, Inc." imported gpg: Total number processed: 1 gpg: imported: 1 (RSA: 1) OK

5. We have to update the packages and then install the ansible

unixchips@unixchips:~$ sudo apt-get update Hit:1 http://download.virtualbox.org/virtualbox/debian xenial InRelease Hit:2 http://ppa.launchpad.net/ansible/ansible/ubuntu xenial InRelease Get:3 http://security.ubuntu.com/ubuntu xenial-security InRelease [109 kB] Hit:4 http://in.archive.ubuntu.com/ubuntu xenial InRelease Get:5 http://in.archive.ubuntu.com/ubuntu xenial-updates InRelease [109 kB] Hit:6 http://in.archive.ubuntu.com/ubuntu xenial-backports InRelease Get:7 http://in.archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages [957 kB] Get:8 http://in.archive.ubuntu.com/ubuntu xenial-updates/main i386 Packages [824 kB] Get:9 http://in.archive.ubuntu.com/ubuntu xenial-updates/universe amd64 Packages [748 kB] Get:10 http://in.archive.ubuntu.com/ubuntu xenial-updates/universe i386 Packages [685 kB] Fetched 3,433 kB in 3s (1,109 kB/s)

unixchips@unixchips:~$ sudo apt-get install ansible 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
ansible is already the newest version (2.8.0-1ppa~xenial).
The following packages were automatically installed and are no longer required:
  linux-headers-4.10.0-28 linux-headers-4.10.0-28-generic linux-headers-4.13.0-32 linux-headers-4.13.0-32-generic linux-headers-4.13.0-36
  linux-headers-4.13.0-36-generic linux-headers-4.13.0-37 linux-headers-4.13.0-37-generic linux-headers-4.13.0-38
  linux-headers-4.13.0-38-generic linux-headers-4.13.0-39 linux-headers-4.13.0-39-generic linux-headers-4.13.0-41
  linux-headers-4.13.0-41-generic linux-headers-4.13.0-43 linux-headers-4.13.0-43-generic linux-headers-4.15.0-29
  linux-headers-4.15.0-29-generic linux-headers-4.15.0-30 linux-headers-4.15.0-30-generic linux-headers-4.15.0-32
  linux-headers-4.15.0-32-generic linux-headers-4.15.0-33 linux-headers-4.15.0-33-generic linux-headers-4.15.0-34
  linux-headers-4.15.0-34-generic linux-headers-4.15.0-36 linux-headers-4.15.0-36-generic linux-headers-4.15.0-39
  linux-headers-4.15.0-39-generic linux-headers-4.15.0-42 linux-headers-4.15.0-42-generic linux-image-4.10.0-28-generic
  linux-image-4.13.0-32-generic linux-image-4.13.0-36-generic linux-image-4.13.0-37-generic linux-image-4.13.0-38-generic
  linux-image-4.13.0-39-generic linux-image-4.13.0-41-generic linux-image-4.13.0-43-generic linux-image-4.15.0-24-generic
  linux-image-4.15.0-29-generic linux-image-4.15.0-30-generic linux-image-4.15.0-32-generic linux-image-4.15.0-33-generic
  linux-image-4.15.0-34-generic linux-image-4.15.0-36-generic linux-image-4.15.0-39-generic linux-image-4.15.0-42-generic
  linux-image-extra-4.10.0-28-generic linux-image-extra-4.13.0-32-generic linux-image-extra-4.13.0-36-generic
  linux-image-extra-4.13.0-37-generic linux-image-extra-4.13.0-38-generic linux-image-extra-4.13.0-39-generic
  linux-image-extra-4.13.0-41-generic linux-image-extra-4.13.0-43-generic linux-image-extra-4.13.0-45-generic linux-modules-4.15.0-24-generic
  linux-modules-4.15.0-29-generic linux-modules-4.15.0-30-generic linux-modules-4.15.0-32-generic linux-modules-4.15.0-33-generic
  linux-modules-4.15.0-34-generic linux-modules-4.15.0-36-generic linux-modules-4.15.0-39-generic linux-modules-4.15.0-42-generic
  linux-modules-extra-4.15.0-33-generic linux-modules-extra-4.15.0-39-generic

6. Once the ansible is installed we can check the version details using below command

unixchips@unixchips:~$ ansible --version 
ansible 2.8.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/unixchips/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.12 (default, Nov 12 2018, 14:36:49) [GCC 5.4.0 20160609]

7. Now we need a dedicated user for ansible operations for both the server and client, i have created a user called "unixchips" in both the nodes as ansible is communicating through simple ssh protocol . We need to configure passwordless authentication for both the nodes using "unixchips" user 

unixchips@unixchips:~$ id unixchips 
uid=1000(unixchips) gid=1000(unixchips) groups=1000(unixchips),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)

  [root@unixchips-server ~]# id unixchips 
uid=1000(unixchips) gid=1000(unixchips) groups=1000(unixchips),995(docker)

8. Now create a public key using the below steps and copy that to the client machine 

[unixchips@unixchips-server ~]$ ssh-keygen -t rsa

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/vivek/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/vivek/.ssh/id_rsa.
Your public key has been saved in /Users/vivek/.ssh/id_rsa.pub.
The key fingerprint is:
80:5f:25:7c:f4:90:aa:e1:f4:a0:01:43:4e:e8:bc:f5 vivek@desktop01
The key's randomart image is:
+--[ RSA 2048]----+
| oo    ...+.     |
|.oo  .  .ooo     |
|o .o. . .o  .    |
| o ...+o.        |
|  o .=.=S        |
| .  .Eo .        |
|                 |
|                 |
|                 |
+-----------------+

ssh-copy-id -i $HOME/.ssh/id_rsa.pub unixchips@unixchips

Now we can login to the node with out the password 

9. Let's test the ansible configuration using the ping command 

[unixchips@unixchips-server ~]$ ansible all --list-hosts
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, 
but still be user configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

  hosts (1):
    unixchips
[unixchips@unixchips-server ~]$ ansible all -m ping
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, 
but still be user configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

[DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host unixchips should use /usr/bin/python3, but is using /usr/bin/python for backward 
compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See 
https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in 
version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
unixchips | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

We have successfully installed the ansible and setup is working as expected .

Thank you for the reading   








Tuesday, May 14, 2019

How to create an ansible playbook and roles

Ansible is an open source platform which helps to automate the configuration management , application deployment in complex IT environment which includes linux,windows based systems . Ansible is commonly known as infrastructure as a code. 

Designed for multi tier deployments Ansible models our IT infrastructure by designing how all our systems are inter related rather than just managing them  .It uses no agents and no additional custom security infrastructure, so it's easy to deploy - and most importantly, it uses a very simple language (YAML, in the form of Ansible Playbooks) that allow you to describe your automation jobs in a way that approaches plain English.

Ansible uses following components to work
  • Inventory file(s)
  • Group vars
  • Host vars
  • Playbooks
  • Roles
  • Tasks
Inventory files are the list of hosts which ansible trying to interact . The default location of the ansible  host file is /etc/ansible/hosts . Group vars and Host vars are the hosts specified in the inventory file. Roles are the ways of automatically loading certain variables and files and handlers as per the known file structure.

Ansible playbooks are the organised group of scripts that defines work for a server configuration managed by the ansible platform. Ansible plays are written by using yamal format.

Directory layout of ansible playbook 

Below is the basic directory layout of ansible playbook 

####################################################
production                # inventory file for production servers
staging                   # inventory file for staging environment

group_vars/
   group1                 # here we assign variables to particular groups
   group2                 # ""
host_vars/
   hostname1              # if systems need specific variables, put them here
   hostname2              # ""

library/                  # if any custom modules, put them here (optional)
filter_plugins/           # if any custom filter plugins, put them here (optional)

site.yml                  # master playbook
webservers.yml            # playbook for webserver tier
dbservers.yml             # playbook for dbserver tier

roles/
    common/               # this hierarchy represents a "role"
        tasks/            #
            main.yml      #
#########################################################

A sample playbook is given below (which we are not using any roles to split up.)


########################################################

# site.yml
---
- hosts: all    # The hosts which the playbook is going to interact #
  vars:
  remote_user: user  # User details which is used to execute this playbook#
  become: yes # Need sudo password for execution #

  tasks: # This task is used to install the ntp package from the yum repository #
  - name: Install ntp
    yum: name=ntp state=present
    tags: ntp

  - name: Configure ntp file # If we want to copy some pre configured file we need to use this session      with template #
    template: src=ntp.conf.j2 dest=/etc/ntp.conf
    tags: ntp
    notify: restart ntp

  - name: Start the ntp service # to start and enable the ntp service after the configuration #
    service: name=ntpd state=started enabled=yes
    tags: ntp

  - name: test to see if selinux is running   # check whether the selinux is enabled and disable it if it is      enabled#
    command: getenforce
    register: sestatus
    changed_when: false

  -hosts: web # This session will install the apache in web servers
  tasks:
  - name: Install httpd Package
    yum: name=httpd state=latest
  - name: Copy httpd configuration file
    copy: src=/data/httpd.original dest=/etc/httpd/conf/httpd.conf
  - name: Copy index.html file
    copy: src=/data/index.html dest=/var/www/html
    notify:
    - restart apache
  - name: Start and Enable httpd service
    service: name=httpd state=restarted enabled=yes
  handlers:
  - name: restart apache
    service: name=httpd state=restarted


- hosts: database   # This session will install the mysql package with below mentioned variables #
  vars:
    mysql_port: 3306
    dbname: somedb
    dbuser: someuser
    dbpass: somepass
  remote_user: user
  become: yes

    tasks:  # if we need to install multiple packages we can use the item method #
  - name: Install Mysql package
    yum: name={{ item }} state=installed
    with_items:
     - mysql-server
     - MySQL-python
     - libselinux-python
     - libsemanage-python

  - name: Configure SELinux to start mysql on any port
    seboolean: name=mysql_connect_any state=true persistent=yes
    when: sestatus.rc != 0

  - name: Create Mysql configuration file
    template: src=my.cnf.j2 dest=/etc/my.cnf
    notify:
    - restart mysql

  - name: Start Mysql Service
    service: name=mysqld state=started enabled=yes

  - name: insert iptables rule     #configure IP tables rule for mysql #
    lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}" insertafter="^:OUTPUT " line="-A INPUT -p tcp --dport {{ mysql_port }} -j ACCEPT"
    notify: restart iptables

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

  - name: Create Application DB User
    mysql_user: name={{ dbuser }} password={{ dbpass }} priv=*.*:ALL host='%' state=present
#########################################################################

We can split the above playbook to short and structured way as below 

######################################################################

# site.yml
---
# This playbook deploys the whole application stack in this site.

- name: apply common configuration to all nodes
  hosts: all
  remote_user: user

  roles:
    - common

- name: configure and deploy the webservers and application code
  hosts: webservers
  remote_user: user

  roles:
    - web

- name: deploy MySQL and configure the databases
  hosts: dbservers
  remote_user: user

  roles:
    - db
#####################################################################

We can create the roles using the ansible galaxy command which will be installed along with the ansible.

#ansible-galaxy init web

This will create follwoing directories inside the ansible/role /web directory


  • files: This directory contains regular files that need to be transferred to the hosts you are configuring for this role. This may also include script files to run.
  • handlers: All handlers that were in your playbook previously can now be added into this directory.
  • meta: This directory can contain files that establish role dependencies. You can list roles that must be applied before the current role can work correctly.
  • templates: You can place all files that use variables to substitute information during creation in this directory.
  • tasks: This directory contains all of the tasks that would normally be in a playbook. These can reference files and templates contained in their respective directories without using a path.
  • vars: Variables for the roles can be specified in this directory and used in your configuration files.

structure of a role

Each folder will contain main.yml file with necessary information

├── README.md

├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml

I will explain the web role with the yml file detail separately fro better understanding

Tasks directory : 

Edit the main.yml inside the task directory as below We can split the main task in to 3 files 

root@unixchips web]# cat tasks/main.yml
---
# tasks file for /etc/ansible/roles/apache
- import_tasks: install.yml
- import_tasks: configure.yml
- import_tasks: service.yml
***************************************** 
root@unixchips apache]# cat tasks/install.yml
---
- name: Install httpd Package
  yum: name=httpd state=latest
******************************************* 
[root@unixchips web]# cat tasks/configure.yml
---
- name: Copy httpd configuration file
  copy: src=files/httpd.conf dest=/etc/httpd/conf/httpd.conf
- name: Copy index.html file
  copy: src=files/index.html dest=/var/www/html
  notify:
  - restart apache
**********************************************
[root@unixchips web]# cat tasks/service.yml
---
- name: Start and Enable httpd service
  service: name=httpd state=restarted enabled=yes

Files directory:

Copy the required files ( index.html and httpd.conf) to this directory

[root@unixchips web]# ll files/*
-rw-r--r-- 1 root root 11753 May  4 10:01 files/httpd.conf
-rw-r--r-- 1 root root    66    May  4 10:02 files/index.html
[root@unixchips web]# cat files/index.html
This is a homepage created by unixchips for ansible roles.
[root@unixchips web]#

Handlers directory:

The main.yml file in this directory contains the code to restart the apache whenever any change is executed 

[root@unixchips web]# cat handlers/main.yml
---
# handlers file for /etc/ansible/roles/apache
- name: restart apache
  service: name=httpd state=restarted

Meta directory :

Edit the main.yml in meta directory as below to update the author and other details 

root@unixchips web# cat meta/main.yml
galaxy_info:
  author:unixchips
  description: Apache Webserver Role
  company: unixchips
  # If the issue tracker for your role is not on github, uncomment the
  # next line and provide a value
  # issue_tracker_url: http://example.com/issue/tracker
  # Some suggested licenses:
  # - BSD (default)
  # - MIT
  # - GPLv2
  # - GPLv3
  # - Apache
  # - CC-BY
  license: license (GPLv2, CC-BY, etc)
  min_ansible_version: 1.2
  # If this a Container Enabled role, provide the minimum Ansible Container version.
------skipped

When we check the directory structure in detail we will bet the output as below using the tree command 

root@unixchips web]# tree
.
|-- README.md
|-- defaults
|   `-- main.yml
|-- files
|   |-- httpd.conf
|   `-- index.html
|-- handlers
|   `-- main.yml
|-- meta
|   `-- main.yml
|-- tasks
|   |-- configure.yml
|   |-- install.yml
|   |-- main.yml
|   `-- service.yml
|-- templates
|-- tests
|   |-- inventory
|   `-- test.yml
`-- vars
    `-- main.yml
8 directories, 13 files

Now we need to create the main playbook as below 

[root@unixchips web]# cat /etc/ansible/runsetup.yml
---
 - hosts: node2
   roles:
   - apache

If we run the playbook using the ansible command we will get the output as below 

root@unixchips web]# ansible-playbook /etc/ansible/runsetup.yml
PLAY [node2] ***************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************
ok: [node2]
TASK [apache : Install httpd Package] **************************************************************************
changed: [node2]
TASK [apache : Copy httpd configuration file] ******************************************************************
Changed: [node2]
TASK [apache : Copy index.html file] ***************************************************************************
changed: [node2]
TASK [apache : Start and Enable httpd service] *****************************************************************
changed: [node2]
RUNNING HANDLER [apache : restart apache] **********************************************************************
changed: [node2]
PLAY RECAP *****************************************************************************************************
node2                      : ok=6    changed=5    unreachable=0    failed=0


Login to the node and check the apache status 

root@node2 ~]# rpm -q httpd
httpd-2.4.6-67.el7.centos.6.x86_64
[root@node2 ~]# systemctl status httpd
httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled)
   Active: active (running) since Sat 2018-05-04 10:23:44 IST; 1min 58s ago
     Docs: man:httpd(8)
           man:apachectl(8)

We have succesfully installed the apache web server using ansible code. 

Thank you for the reading