How to Install and Configure Ansible and Run Ad-hoc Commands
Ansible is open source configuration management and orchestration tool. Other automation tools are: Puppet, Chef, Salt, etc.
Power On:
→ Developer1 machine: developer1 (192.168.13.141)
→ Developer2 machine: developer2 (192.168.13.142)
→ Tomcat server: tomcatsrv (192.168.13.135)
→ Ansible server: controlnode (192.168.13.145)
Configuring Ansible Server
[root@original ~]# hostnamectl set-hostname controlnode.srg.com
[root@original ~]# exec bash
[root@controlnode ~]# vim /etc/hosts
---------------------------------
192.168.13.145 controlnode.srg.com
---------------------------------
[root@controlnode ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33
---------------------------------
BOOTPROTO=static
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.13.145
NETMASK=255.255.255.0
GATEWAY=192.168.13.2
DNS1=192.168.13.2
---------------------------------
[root@controlnode ~]# systemctl restart network
[root@controlnode ~]# systemctl restart network
[root@controlnode ~]# hostname
controlnode.srg.com
[root@controlnode ~]# hostname -I
192.168.13.145 192.168.122.1
[root@controlnode ~]# ping google.com
- Preparing systems for running ansible playbook: create a user called ‘devops’ on both the control node and managed hosts (clients)
[root@controlnode ~]# useradd devops
[root@controlnode ~]# passwd devops
Changing password for user devops.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[root@developer1 ~]# useradd devops
[root@developer1 ~]# passwd devops
Changing password for user devops.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[root@developer2 ~]# useradd devops
[root@developer2 ~]# passwd devops
Changing password for user devops.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[root@tomcatsrv ~]# useradd devops
[root@tomcatsrv ~]# passwd devops
Changing password for user devops.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
2. Grant full admin right to the ‘devops’ user on both the control node & managed hosts
[root@controlnode ~]# visudo
---------------------------------
devops ALL=(ALL) NOPASSWD:ALL
[root@developer1 ~]# visudo
---------------------------------
devops ALL=(ALL) NOPASSWD:ALL
[root@developer2 ~]# visudo
---------------------------------
devops ALL=(ALL) NOPASSWD:ALL
[root@tomcatsrv ~]# visudo
---------------------------------
devops ALL=(ALL) NOPASSWD:ALL
3. Login into devops user on control node and generate private and public key pair and then send a copy of the public key to the managed hosts (clients)
[root@controlnode ~]# su - devops
[devops@controlnode ~]$ whoami
devops
[devops@controlnode ~]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/devops/.ssh/id_rsa):
Created directory '/home/devops/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/devops/.ssh/id_rsa.
Your public key has been saved in /home/devops/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:+ztWRaSPgFQAzx9Xml7I0Ec8Y9ZzUfpV6g0G5VdRnMU devops@controlnode.srg.com
The key's randomart image is:
+---[RSA 3072]----+
| ..ooo..==*&|
| + . o.OO=E|
| + o B=Bo=|
| . = *.=.|
| S . o.o o|
| . . |
| . . |
| .o |
| .oo |
+----[SHA256]-----+
[devops@controlnode ~]$ ls /home/devops/.ssh
id_rsa id_rsa.pub
[devops@controlnode ~]$ ssh-copy-id devops@192.168.13.141
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/devops/.ssh/id_rsa.pub"
The authenticity of host '192.168.13.141 (192.168.13.141)' can't be established.
ECDSA key fingerprint is SHA256:0w3KwTevfkGFbPaqosRB6F8OVEN8nte193c/ICIFyqg.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 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
devops@192.168.13.141's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'devops@192.168.13.141'"
and check to make sure that only the key(s) you wanted were added.
[devops@controlnode ~]$ ssh-copy-id devops@192.168.13.142
[devops@controlnode ~]$ ssh-copy-id devops@192.168.13.135
4. Check ssh direct login to managed hosts
[devops@controlnode ~]$ ssh devops@192.168.13.141
[devops@developer1 ~]$ hostname
developer1.srg.com
[devops@developer1 ~]$ exit
[devops@controlnode ~]$ ssh devops@192.168.13.142
[devops@developer2 ~]$ hostname
developer2.srg.com
[devops@developer2 ~]$ exit
[devops@controlnode ~]$ ssh devops@192.168.13.135
[devops@tomcatsrv ~]$ hostname
tomcatsrv.srg.com
[devops@tomcatsrv ~]$ exit
5. Install required packages. Install ‘ansible’ package on the control node and ‘python’ package on the managed hosts (ie. client systems)
[devops@controlnode ~]$ rpm -q ansible
package ansible is not installed
[devops@controlnode ~]$ sudo yum -y install ansible
Last metadata expiration check: 1:32:24 ago on Tue 04 Jul 2023 09:31:50 PM +0545.
No match for argument: ansible
Error: Unable to find a match: ansible
[devops@controlnode ~]$ ls /etc/yum.repos.d/
CentOS-Stream-AppStream.repo CentOS-Stream-Extras.repo CentOS-Stream-PowerTools.repo
CentOS-Stream-BaseOS.repo CentOS-Stream-HighAvailability.repo CentOS-Stream-RealTime.repo
CentOS-Stream-Debuginfo.repo CentOS-Stream-Media.repo CentOS-Stream-ResilientStorage.repo
CentOS-Stream-Extras-common.repo CentOS-Stream-NFV.repo CentOS-Stream-Sources.repo
[devops@controlnode ~]$ sudo yum -y install epel-release
[devops@controlnode ~]$ rpm -q epel-release
epel-release-8-11.el8.noarch
[devops@controlnode ~]$ ls /etc/yum.repos.d/
[devops@controlnode ~]$ sudo yum -y install ansible
[devops@controlnode ~]$ rpm -q ansible
ansible-7.2.0-1.el8.noarch
[root@developer1 ~]# yum -y install python39
[root@developer1 ~]# rpm -q python39
python39-3.9.16-1.module_el8.8.0+1243+5f5a1e61.x86_64
[root@developer2 ~]# yum -y install python39
[root@tomcatsrv ~]# yum -y install python39
6. Create ansible configuration file and inventory file (on control node)
[devops@controlnode ~]$ mkdir -p /home/devops/playbook
[devops@controlnode ~]$ cd /home/devops/playbook
[devops@controlnode playbook]$ pwd
/home/devops/playbook
[devops@controlnode playbook]$ vi ansible.cfg
---------------------------------
[defaults]
inventory=/home/devops/playbook/inventory
remote_user=devops
[privilege_escalation]
become=true
---------------------------------
[devops@controlnode playbook]$ ls
ansible.cfg
[devops@controlnode playbook]$ vi inventory
---------------------------------
[developers]
192.168.13.141
192.168.13.142
[tomcatserver]
192.168.13.135
---------------------------------
[devops@controlnode playbook]$ ls
ansible.cfg inventory
7. To confirm inventory file working properly or not: listing hosts
[devops@controlnode playbook]$ ansible developers --list-host
hosts (2):
192.168.13.141
192.168.13.142
[devops@controlnode playbook]$ ansible tomcatserver --list-host
hosts (1):
192.168.13.135
[devops@controlnode playbook]$ ansible all --list-host
hosts (3):
192.168.13.141
192.168.13.142
192.168.13.135
8. To execute ping command on all hosts
[devops@controlnode playbook]$ ansible all -m ping
192.168.13.135 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.13.142 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
192.168.13.141 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
9. To check individual machine listed on inventory file or not
[devops@controlnode playbook]$ ansible 192.168.13.141 --list-hosts
hosts (1):
192.168.13.141
[devops@controlnode playbook]$ ansible 192.168.13.143 --list-hosts
[WARNING]: Could not match supplied host pattern, ignoring: 192.168.13.143
[WARNING]: No hosts matched, nothing to do
hosts (0):
10. List ip inside particular group from inventory file
[devops@controlnode playbook]$ ansible developers -i inventory --list-hosts
hosts (2):
192.168.13.141
192.168.13.142
[devops@controlnode playbook]$ cd ..
[devops@controlnode ~]$ pwd
/home/devops
[devops@controlnode ~]$ ls
playbook
[devops@controlnode ~]$ ansible developers --list-host
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[WARNING]: Could not match supplied host pattern, ignoring: developers
hosts (0):
[devops@controlnode ~]$ ansible developers -i playbook/inventory --list-hosts
hosts (2):
192.168.13.141
192.168.13.142
Running Ansible Ad-hoc Command
- Running/executing individual ansible command/module:
ansible <groupname> -m <module name> -a ‘<arguments>’
Add new user ‘dinesh’ to all the developers machine:
ansible developers -m user -a ‘name=dinesh’
[devops@controlnode playbook]$ ansible developers -m user -a 'name=dinesh'
192.168.13.141 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1007,
"home": "/home/dinesh",
"name": "dinesh",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1006
}
192.168.13.142 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"comment": "",
"create_home": true,
"group": 1007,
"home": "/home/dinesh",
"name": "dinesh",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 1006
}
To verify newly created user from developers’ machine
[root@developer1 ~]# grep dinesh /etc/passwd
dinesh:x:1006:1007::/home/dinesh:/bin/bash
[root@developer2 ~]# grep dinesh /etc/passwd
dinesh:x:1006:1007::/home/dinesh:/bin/bash
[root@tomcatsrv ~]# grep dinesh /etc/passwd
2. To get information about the module: ansible-doc <module name>
[devops@controlnode playbook]$ ansible-doc user
---------------------------------
…
EXAMPLES:
- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
ansible.builtin.user:
name: johnd
comment: John Doe
uid: 1040
group: admin
…
3. Deploying apache httpd web service
[devops@controlnode playbook]$ ansible-doc yum
---------------------------------
…
EXAMPLES:
- name: Install the latest version of Apache
ansible.builtin.yum:
name: httpd
state: latest
…
---------------------------------
[devops@controlnode playbook]$ ansible developers -m ansible.builtin.yum -a 'name=httpd state=latest'
192.168.13.142 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: httpd-2.4.37-54.module_el8.8.0+1256+e1598b50.x86_64"
]
}
192.168.13.141 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Installed: httpd-2.4.37-54.module_el8.8.0+1256+e1598b50.x86_64"
]
}
[devops@developer1 ~]# sudo rpm -q httpd
httpd-2.4.37-54.module_el8.8.0+1256+e1598b50.x86_64
[devops@developer2 ~]# sudo rpm -q httpd
httpd-2.4.37-54.module_el8.8.0+1256+e1598b50.x86_64
We can also execute linux local command on argument thru command module but it’s property is not idempotent.
[devops@controlnode playbook]$ ansible developers -m command -a 'yum -y install httpd'
4. To start httpd service
[devops@controlnode playbook]$ ansible-doc service
---------------------------------
…
EXAMPLES:
- name: Start service httpd, if not started
ansible.builtin.service:
name: httpd
state: started
…
---------------------------------
[devops@controlnode playbook]$ ansible developers -m ansible.builtin.service -a 'name=httpd state=started enabled=yes'
192.168.13.142 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"enabled": true,
"name": "httpd",
"state": "started",
"status": {
…
}
}
192.168.13.141 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"enabled": true,
"name": "httpd",
"state": "started",
"status": {
…
}
}
[devops@developer1 ~]# sudo systemctl status httpd
[devops@developer2 ~]# sudo systemctl status httpd
5. Allow service on firewall
[devops@controlnode playbook]$ ansible-doc firewalld
---------------------------------
…
EXAMPLES:
- name: permit traffic in default zone for https service
ansible.posix.firewalld:
service: https
permanent: true
state: enabled
…
---------------------------------
[devops@controlnode playbook]$ ansible developers -m ansible.posix.firewalld -a 'service=http permanent=true state=enabled immediate=true'
192.168.13.141 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "Permanent and Non-Permanent(immediate) operation, Changed service http to enabled"
}
192.168.13.142 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"msg": "Permanent and Non-Permanent(immediate) operation, Changed service http to enabled"
}
[devops@developer1 ~]# sudo firewall-cmd --list-all
[devops@developer2 ~]# sudo firewall-cmd --list-all
6. Move index page to web servers (control hosts)
[devops@controlnode playbook]$ pwd
/home/devops/playbook
[devops@controlnode playbook]$ ls
ansible.cfg inventory
[devops@controlnode playbook]$ vi index.html
[devops@controlnode playbook]$ cat index.html
<h1>Deploying website on the webservers using ansible ad-hoc command</h1>
[devops@controlnode playbook]$ ansible-doc copy
---------------------------------
…
EXAMPLES:
- name: Copy file with owner and permissions
ansible.builtin.copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: '0644'
…
---------------------------------
[devops@controlnode playbook]$ ansible developers -m ansible.builtin.copy -a 'src=index.html dest=/var/www/html/index.html'
192.168.13.142 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "413f0e2073c9e411464c5c99810e42ed016c973b",
"dest": "/var/www/html/index.html",
…
}
192.168.13.141 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "413f0e2073c9e411464c5c99810e42ed016c973b",
"dest": "/var/www/html/index.html",
…
}
[devops@developer1 ~]# ls /var/www/html/
[devops@developer2 ~]# ls /var/www/html/
7. Check the deployed site