Ansible is an open-source software provisioning, configuration management, and application-deployment tool. Ansible uses its own declarative language, based on YAML (Yet Another Markup Language), to describe system configurations.
The main functionality that differs Ansible from other configuration management software is it’s agent-less architecture. And because of this agent-less architecture, installation and configuration of Ansible is very easy and straight-forward. We just need to configure the key-based ssh authentication on the nodes and Ansible will then uses the ssh to perform configurations on the nodes.
As compare to Puppet and Chef; Ansible is a new candidate in the domain of configuration management. But it is getting famous and converting the users from competitors’ technologies.
One major shift is the inclusion of Ansible by Red Hat in Industry’s most reputable certification i.e. RHCE (Red Hat Certified Engineer) specific to Red Hat Enterprise Linux 8.
In this article, we are installing Ansible on CentOS 7 and then write and execute Ansible playbooks to configure our Linux servers using Ansible.
Environment Specification:
We are using three CentOS 7 virtual machines with following specifications.
Ansible Control Node:
- Hostname – ansible-01.example.com
- IP Address – 192.168.116.201 /24
- Operating System – CentOS 7.6
Lighttpd Web Server:
- Hostname – lighttpd-01.example.com
- IP Address – 192.168.116.202 /24
- Operating System – CentOS 7.6
MariaDB Database Server:
- Hostname – mariadb-01.example.com
- IP Address – 192.168.116.203 /24
- Operating System – CentOS 7.6
Here, we will install Ansible on ansible-01.example.com and configure the other two servers as Ansible managed nodes.
Configure Name Resolution of Linux Servers:
If you have configured a Authoritative DNS Server for you domain, then you can add the RR (Resource Records) of all three servers in it. Otherwise, you have to add the name resolution entries in Local DNS Resolver (/etc/hosts) file.
[root@ansible-01 ~]# cat >> /etc/hosts << EOF > 192.168.116.201 ansible-01.example.com ansible-01 > 192.168.116.202 lighttpd-01.example.com lighttpd-01 > 192.168.116.203 mariadb-01.example.com mariadb-01 > EOF
Repeat the above command on lightttpd-01 and mariadb-01 machines.
Configure SSH for key-based authentication on CentOS 7:
Generate a SSH Key on ansible-01.example.com server using ssh-keygen command.
[root@ansible-01 ~]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/root/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:r2CBUntEfwcot16lGdI2K8b1rli6UUmfdEfiifps3cw root@ansible-01.example.com The key's randomart image is: +---[RSA 2048]----+ | . o. . .| | ...+ *..o + | | . .+.=oO+ + .| | . + =o=*.o . | | . o ooSo+.o | | . . ..o.o.. + | | o .+..+ . E| | . .oo.. | | o. | +----[SHA256]-----+
Copy SSH public key to lighttpd-01 and mariadb-01 servers.
[root@ansible-01 ~]# ssh-copy-id root@lighttpd-01 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub" The authenticity of host 'lighttpd-01 (192.168.116.202)' can't be established. ECDSA key fingerprint is SHA256:kzyCimDDwGPsfsuGXxdrcBqlxVQlU8FZTsYrwbPzZHM. ECDSA key fingerprint is MD5:b4:3f:a2:86:30:7a:b7:d7:b3:b0:10:8f:a3:3e:8a:bc. Are you sure you want to continue connecting (yes/no)? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@lighttpd-01's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'root@lighttpd-01'" and check to make sure that only the key(s) you wanted were added. [root@ansible-01 ~]# ssh-copy-id root@mariadb-01 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub" The authenticity of host 'mariadb-01 (192.168.116.203)' can't be established. ECDSA key fingerprint is SHA256:kzyCimDDwGPsfsuGXxdrcBqlxVQlU8FZTsYrwbPzZHM. ECDSA key fingerprint is MD5:b4:3f:a2:86:30:7a:b7:d7:b3:b0:10:8f:a3:3e:8a:bc. Are you sure you want to continue connecting (yes/no)? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@mariadb-01's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'root@mariadb-01'" and check to make sure that only the key(s) you wanted were added.
Verify SSH authentication by connecting to each server.
[root@ansible-01 ~]# ssh root@lighttpd-01 Last login: Sun Sep 8 10:18:40 2019 [root@lighttpd-01 ~]# exit logout Connection to lighttpd-01 closed. [root@ansible-01 ~]# ssh root@mariadb-01 Last login: Sun Sep 8 10:21:05 2019 [root@mariadb-01 ~]# exit logout Connection to mariadb-01 closed.
Installing Ansible on CentOS 7:
Connect with ansible-01.example.com using ssh as root user.
Ansible is available in extras yum repository, therefore, we can install it using yum command.
[root@ansible-01 ~]# yum install -y ansible Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.ges.net.pk * extras: mirrors.ges.net.pk * updates: mirrors.ges.net.pk Resolving Dependencies --> Running transaction check ---> Package ansible.noarch 0:2.4.2.0-2.el7 will be installed --> Processing Dependency: sshpass for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python2-jmespath for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-six for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-setuptools for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-passlib for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-paramiko for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-jinja2 for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-httplib2 for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: python-cryptography for package: ansible-2.4.2.0-2.el7.noarch --> Processing Dependency: PyYAML for package: ansible-2.4.2.0-2.el7.noarch --> Running transaction check ---> Package PyYAML.x86_64 0:3.10-11.el7 will be installed --> Processing Dependency: libyaml-0.so.2()(64bit) for package: PyYAML-3.10-11.el7.x86_64 ---> Package python-httplib2.noarch 0:0.9.2-1.el7 will be installed ---> Package python-jinja2.noarch 0:2.7.2-3.el7_6 will be installed --> Processing Dependency: python-babel >= 0.8 for package: python-jinja2-2.7.2-3.el7_6.noarch --> Processing Dependency: python-markupsafe for package: python-jinja2-2.7.2-3.el7_6.noarch ---> Package python-paramiko.noarch 0:2.1.1-9.el7 will be installed --> Processing Dependency: python2-pyasn1 for package: python-paramiko-2.1.1-9.el7.noarch ---> Package python-passlib.noarch 0:1.6.5-2.el7 will be installed ---> Package python-setuptools.noarch 0:0.9.8-7.el7 will be installed --> Processing Dependency: python-backports-ssl_match_hostname for package: python-setuptools-0.9.8-7.el7.noarch ---> Package python-six.noarch 0:1.9.0-2.el7 will be installed ---> Package python2-cryptography.x86_64 0:1.7.2-2.el7 will be installed --> Processing Dependency: python-idna >= 2.0 for package: python2-cryptography-1.7.2-2.el7.x86_64 --> Processing Dependency: python-cffi >= 1.4.1 for package: python2-cryptography-1.7.2-2.el7.x86_64 --> Processing Dependency: python-ipaddress for package: python2-cryptography-1.7.2-2.el7.x86_64 --> Processing Dependency: python-enum34 for package: python2-cryptography-1.7.2-2.el7.x86_64 ---> Package python2-jmespath.noarch 0:0.9.0-3.el7 will be installed ---> Package sshpass.x86_64 0:1.06-2.el7 will be installed --> Running transaction check ---> Package libyaml.x86_64 0:0.1.4-11.el7_0 will be installed ---> Package python-babel.noarch 0:0.9.6-8.el7 will be installed ---> Package python-backports-ssl_match_hostname.noarch 0:3.5.0.1-1.el7 will be installed --> Processing Dependency: python-backports for package: python-backports-ssl_match_hostname-3.5.0.1-1.el7.noarch ---> Package python-cffi.x86_64 0:1.6.0-5.el7 will be installed --> Processing Dependency: python-pycparser for package: python-cffi-1.6.0-5.el7.x86_64 ---> Package python-enum34.noarch 0:1.0.4-1.el7 will be installed ---> Package python-idna.noarch 0:2.4-1.el7 will be installed ---> Package python-ipaddress.noarch 0:1.0.16-2.el7 will be installed ---> Package python-markupsafe.x86_64 0:0.11-10.el7 will be installed ---> Package python2-pyasn1.noarch 0:0.1.9-7.el7 will be installed --> Running transaction check ---> Package python-backports.x86_64 0:1.0-8.el7 will be installed ---> Package python-pycparser.noarch 0:2.14-1.el7 will be installed --> Processing Dependency: python-ply for package: python-pycparser-2.14-1.el7.noarch --> Running transaction check ---> Package python-ply.noarch 0:3.4-11.el7 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: ansible noarch 2.4.2.0-2.el7 extras 7.6 M Installing for dependencies: PyYAML x86_64 3.10-11.el7 base 153 k libyaml x86_64 0.1.4-11.el7_0 base 55 k python-babel noarch 0.9.6-8.el7 base 1.4 M python-backports x86_64 1.0-8.el7 base 5.8 k python-backports-ssl_match_hostname noarch 3.5.0.1-1.el7 base 13 k python-cffi x86_64 1.6.0-5.el7 base 218 k python-enum34 noarch 1.0.4-1.el7 base 52 k python-httplib2 noarch 0.9.2-1.el7 extras 115 k python-idna noarch 2.4-1.el7 base 94 k python-ipaddress noarch 1.0.16-2.el7 base 34 k python-jinja2 noarch 2.7.2-3.el7_6 updates 518 k python-markupsafe x86_64 0.11-10.el7 base 25 k python-paramiko noarch 2.1.1-9.el7 updates 269 k python-passlib noarch 1.6.5-2.el7 extras 488 k python-ply noarch 3.4-11.el7 base 123 k python-pycparser noarch 2.14-1.el7 base 104 k python-setuptools noarch 0.9.8-7.el7 base 397 k python-six noarch 1.9.0-2.el7 base 29 k python2-cryptography x86_64 1.7.2-2.el7 base 502 k python2-jmespath noarch 0.9.0-3.el7 extras 39 k python2-pyasn1 noarch 0.1.9-7.el7 base 100 k sshpass x86_64 1.06-2.el7 extras 21 k Transaction Summary ================================================================================ Install 1 Package (+22 Dependent packages) Total download size: 12 M Installed size: 60 M Downloading packages: (1/23): PyYAML-3.10-11.el7.x86_64.rpm | 153 kB 00:00 (2/23): python-backports-ssl_match_hostname-3.5.0.1-1.el7. | 13 kB 00:00 (3/23): libyaml-0.1.4-11.el7_0.x86_64.rpm | 55 kB 00:01 (4/23): python-backports-1.0-8.el7.x86_64.rpm | 5.8 kB 00:01 (5/23): python-enum34-1.0.4-1.el7.noarch.rpm | 52 kB 00:00 (6/23): python-cffi-1.6.0-5.el7.x86_64.rpm | 218 kB 00:01 (7/23): python-idna-2.4-1.el7.noarch.rpm | 94 kB 00:00 (8/23): python-ipaddress-1.0.16-2.el7.noarch.rpm | 34 kB 00:00 (9/23): python-markupsafe-0.11-10.el7.x86_64.rpm | 25 kB 00:00 (10/23): python-httplib2-0.9.2-1.el7.noarch.rpm | 115 kB 00:01 (11/23): python-paramiko-2.1.1-9.el7.noarch.rpm | 269 kB 00:01 (12/23): python-passlib-1.6.5-2.el7.noarch.rpm | 488 kB 00:02 (13/23): python-jinja2-2.7.2-3.el7_6.noarch.rpm | 518 kB 00:03 (14/23): python-ply-3.4-11.el7.noarch.rpm | 123 kB 00:01 (15/23): python-six-1.9.0-2.el7.noarch.rpm | 29 kB 00:00 (16/23): python-pycparser-2.14-1.el7.noarch.rpm | 104 kB 00:00 (17/23): python2-jmespath-0.9.0-3.el7.noarch.rpm | 39 kB 00:00 (18/23): python2-pyasn1-0.1.9-7.el7.noarch.rpm | 100 kB 00:00 (19/23): sshpass-1.06-2.el7.x86_64.rpm | 21 kB 00:00 (20/23): python-babel-0.9.6-8.el7.noarch.rpm | 1.4 MB 00:08 (21/23): python2-cryptography-1.7.2-2.el7.x86_64.rpm | 502 kB 00:03 (22/23): python-setuptools-0.9.8-7.el7.noarch.rpm | 397 kB 00:03 (23/23): ansible-2.4.2.0-2.el7.noarch.rpm | 7.6 MB 00:16 -------------------------------------------------------------------------------- Total 760 kB/s | 12 MB 00:16 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : python2-pyasn1-0.1.9-7.el7.noarch 1/23 Installing : python-ipaddress-1.0.16-2.el7.noarch 2/23 Installing : python-six-1.9.0-2.el7.noarch 3/23 Installing : python-httplib2-0.9.2-1.el7.noarch 4/23 Installing : python-enum34-1.0.4-1.el7.noarch 5/23 Installing : libyaml-0.1.4-11.el7_0.x86_64 6/23 Installing : PyYAML-3.10-11.el7.x86_64 7/23 Installing : python-backports-1.0-8.el7.x86_64 8/23 Installing : python-backports-ssl_match_hostname-3.5.0.1-1.el7.noarch 9/23 Installing : python-setuptools-0.9.8-7.el7.noarch 10/23 Installing : python-babel-0.9.6-8.el7.noarch 11/23 Installing : python-passlib-1.6.5-2.el7.noarch 12/23 Installing : python-ply-3.4-11.el7.noarch 13/23 Installing : python-pycparser-2.14-1.el7.noarch 14/23 Installing : python-cffi-1.6.0-5.el7.x86_64 15/23 Installing : python-markupsafe-0.11-10.el7.x86_64 16/23 Installing : python-jinja2-2.7.2-3.el7_6.noarch 17/23 Installing : python-idna-2.4-1.el7.noarch 18/23 Installing : python2-cryptography-1.7.2-2.el7.x86_64 19/23 Installing : python-paramiko-2.1.1-9.el7.noarch 20/23 Installing : sshpass-1.06-2.el7.x86_64 21/23 Installing : python2-jmespath-0.9.0-3.el7.noarch 22/23 Installing : ansible-2.4.2.0-2.el7.noarch 23/23 Verifying : python-backports-ssl_match_hostname-3.5.0.1-1.el7.noarch 1/23 Verifying : python2-jmespath-0.9.0-3.el7.noarch 2/23 Verifying : sshpass-1.06-2.el7.x86_64 3/23 Verifying : python-setuptools-0.9.8-7.el7.noarch 4/23 Verifying : python-jinja2-2.7.2-3.el7_6.noarch 5/23 Verifying : python-six-1.9.0-2.el7.noarch 6/23 Verifying : python-idna-2.4-1.el7.noarch 7/23 Verifying : python-markupsafe-0.11-10.el7.x86_64 8/23 Verifying : python-ply-3.4-11.el7.noarch 9/23 Verifying : python-passlib-1.6.5-2.el7.noarch 10/23 Verifying : python-paramiko-2.1.1-9.el7.noarch 11/23 Verifying : python-babel-0.9.6-8.el7.noarch 12/23 Verifying : python-backports-1.0-8.el7.x86_64 13/23 Verifying : python-cffi-1.6.0-5.el7.x86_64 14/23 Verifying : python-pycparser-2.14-1.el7.noarch 15/23 Verifying : libyaml-0.1.4-11.el7_0.x86_64 16/23 Verifying : ansible-2.4.2.0-2.el7.noarch 17/23 Verifying : python-ipaddress-1.0.16-2.el7.noarch 18/23 Verifying : python-enum34-1.0.4-1.el7.noarch 19/23 Verifying : python-httplib2-0.9.2-1.el7.noarch 20/23 Verifying : python2-pyasn1-0.1.9-7.el7.noarch 21/23 Verifying : PyYAML-3.10-11.el7.x86_64 22/23 Verifying : python2-cryptography-1.7.2-2.el7.x86_64 23/23 Installed: ansible.noarch 0:2.4.2.0-2.el7 Dependency Installed: PyYAML.x86_64 0:3.10-11.el7 libyaml.x86_64 0:0.1.4-11.el7_0 python-babel.noarch 0:0.9.6-8.el7 python-backports.x86_64 0:1.0-8.el7 python-backports-ssl_match_hostname.noarch 0:3.5.0.1-1.el7 python-cffi.x86_64 0:1.6.0-5.el7 python-enum34.noarch 0:1.0.4-1.el7 python-httplib2.noarch 0:0.9.2-1.el7 python-idna.noarch 0:2.4-1.el7 python-ipaddress.noarch 0:1.0.16-2.el7 python-jinja2.noarch 0:2.7.2-3.el7_6 python-markupsafe.x86_64 0:0.11-10.el7 python-paramiko.noarch 0:2.1.1-9.el7 python-passlib.noarch 0:1.6.5-2.el7 python-ply.noarch 0:3.4-11.el7 python-pycparser.noarch 0:2.14-1.el7 python-setuptools.noarch 0:0.9.8-7.el7 python-six.noarch 0:1.9.0-2.el7 python2-cryptography.x86_64 0:1.7.2-2.el7 python2-jmespath.noarch 0:0.9.0-3.el7 python2-pyasn1.noarch 0:0.1.9-7.el7 sshpass.x86_64 0:1.06-2.el7 Complete!
Check version of Ansible software.
[root@ansible-01 ~]# ansible --version ansible 2.4.2.0 config file = /etc/ansible/ansible.cfg configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Oct 30 2018, 23:45:53) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
Create Ansible Inventory on CentOS 7:
File /etc/ansible/hosts holds the inventory of Ansible managed nodes.
[root@ansible-01 ~]# vi /etc/ansible/hosts
Add lighttpd-01 and mariadb-01 nodes in this file.
[centos76-servers] lighttpd-01.example.com mariadb-01.example.com [lighttpd-webservers] lighttpd-01.example.com [mariadb-dbserver] mariadb-01.example.com
We have created three groups here.
- centos76-servers – group of CentOS 7.6 based servers, so we can configure all our CentOS 7.6 servers with a single command.
- lighttpd-servers – group of Lighttpd based web servers.
- mariadb-servers – group of MariaDB based database servers.
Here, we can create as many groups as we like, according to our requirement.
We have successfully installed Ansible on CentOS 7 and added two Linux servers in Ansible Inventory.
Configure Ansible Managed Nodes in Adhoc mode:
Now ping a group of servers using ansible command.
[root@ansible-01 ~]# ansible -m ping 'centos76-servers' lighttpd-01.example.com | SUCCESS => { "changed": false, "ping": "pong" } mariadb-01.example.com | SUCCESS => { "changed": false, "ping": "pong" }
Similarly, we can execute any command using following syntax.
[root@ansible-01 ~]# ansible -m command -a 'cat /etc/redhat-release' 'centos76-servers' 192.168.116.203 | SUCCESS | rc=0 >> CentOS Linux release 7.6.1810 (Core) 192.168.116.202 | SUCCESS | rc=0 >> CentOS Linux release 7.6.1810 (Core)
Using Ansible Playbooks to configure CentOS 7 nodes:
Although, we can execute commands in adhoc mode, to configure our nodes. But, it violates the concept of configuration management. Therefore, we will write playbooks (YAML scripts) to perform consistent configurations on our nodes.
Now, we are writing a playbook that will perform some initial configurations like it creates user, install some packages, and perform some configurations on centos76-servers group.
[root@ansible-01 ~]# vi centos76_servers_initial_conf.yaml
and add following YAML script.
--- - hosts: centos76-servers user: root tasks: - name: Installing Common Packages action: yum name=wget,bzip2 state=installed - name: Create an Admin User user: name: "ahmer" groups: "wheel" password: "{{ '123' | password_hash('sha512') }}"
Save and exit from vim editor.
Execute this playbook using ansible-playbook command.
[root@ansible-01 ~]# ansible-playbook centos76_servers_initial_conf.yaml PLAY [centos76-servers] ******************************************************** TASK [Gathering Facts] ********************************************************* ok: [lighttpd-01.example.com] ok: [mariadb-01.example.com] TASK [Installing Common Packages] ********************************************** changed: [lighttpd-01.example.com] changed: [mariadb-01.example.com] TASK [Create an Admin User] **************************************************** changed: [mariadb-01.example.com] changed: [lighttpd-01.example.com] PLAY RECAP ********************************************************************* lighttpd-01.example.com : ok=3 changed=2 unreachable=0 failed=0 mariadb-01.example.com : ok=3 changed=2 unreachable=0 failed=0
Our playbook is executed successfully without any error and the required configurations has been performed on both nodes.
Now create another playbook to install Lighttpd web server using ansible command.
[root@ansible-01 ~]# vi lighttpd_servers.yaml
Add following YAML script therein.
--- - hosts: lighttpd-webservers user: root vars: myhomepage: '
Apache installed using Ansible
' tasks: - name: Installing EPEL yum Repository action: yum name=epel-release state=installed - name: Installing Lighttpd Server action: yum name=lighttpd state=installed - name: Configure Lighttpd Server replace: path: /etc/lighttpd/lighttpd.conf regexp: 'server.use-ipv6 = "enable"' replace: 'server.use-ipv6 = "disable"' backup: yes - name: Create Index.html File. copy: dest: /var/www/lighttpd/index.html content: '{{ myhomepage }}' backup: yes - name: Allow HTTPS Service in Linux Firewall firewalld: service: http permanent: yes state: enabled - name: Restart Lighttpd service service: name: lighttpd enabled: yes state: restarted - name: Restart Firewalld service service: name: firewalld state: restarted
Execute this playbook as follows:
[root@ansible-01 ~]# ansible-playbook lighttpd_servers.yaml PLAY [lighttpd-webservers] ***************************************************** TASK [Gathering Facts] ********************************************************* ok: [lighttpd-01.example.com] TASK [Installing EPEL yum Repository] ****************************************** ok: [lighttpd-01.example.com] TASK [Installing Lighttpd Server] ********************************************** ok: [lighttpd-01.example.com] TASK [Configure Lighttpd Server] *********************************************** changed: [lighttpd-01.example.com] TASK [Create Index.html File.] ************************************************* ok: [lighttpd-01.example.com] TASK [Allow HTTPS Service in Linux Firewall] *********************************** ok: [lighttpd-01.example.com] TASK [Restart Lighttpd service] ************************************************ changed: [lighttpd-01.example.com] TASK [Restart Firewalld service] *********************************************** changed: [lighttpd-01.example.com] PLAY RECAP ********************************************************************* lighttpd-01.example.com : ok=8 changed=3 unreachable=0 failed=0
Verify our Lighttpd web server using curl command.
[root@ansible-01 ~]# curl http://lighttpd-01.example.com <html><h1>Apache installed using Ansible</h1></html>
We have successfully write and executed two playbooks. If you want to write more advance Ansible playbooks, then you should refer to Ansible Documentation.
We have successfully installed Ansible and used playbooks on CentOS 7 to configure managed nodes.