One of the simplest ways Ansible can be used is by using ad-hoc commands. These can be used when you want to issue some commands on a server or a bunch of servers. Ad-hoc commands are not stored for future uses but represent a fast way to interact with the desired servers.
- You can run ansible either ad-hoc or as a playbook
- Both methods have the same capabilities
- Ad-hoc commands are effectively one-liners
Use cases for Ad-hoc
- Operational commands
- Checking log contents
- Daemon control
- Process management
- Informational commands
- Check installed software
- Check system properties
- Gather system performance information (CPU, disk space, memory use)
- Research
- Work with unfamiliar modules on test systems
- Practice for playbook engineering
Ad-hoc vs Playbook
Ad-hoc mode | Playbook mode |
|
|
Common modules
ping
– Validate if server is up and recheable. No required parameters.setup
– Gather ansible facts. No required parameters.yum
– Use yum package manager. Parametersname
andstate
.service
– Control Daemons. Parametersname
andstate
.user
– Manipulate system users. Parametersname
.copy
– Copy files. Parameterssrc
.file
– Work with files. Parameterspath
.git
– Interact with git repositories. Parametersrepo
anddest
.
You can check if the hosts are accessible from the ansible server by issuing a ping command on all hosts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[root@controlnode ansible]# cat inventory managedhost1 ansible_host=managedhost1.example.com [labservers] managedhost1.example.com managedhost2.example.com [root@controlnode ansible]# ansible -i inventory all -m ping managedhost2.example.com | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname managedhost2.example.com: Name or service not known\r\n", "unreachable": true } managedhost1 | SUCCESS => { "changed": false, "ping": "pong" } managedhost1.example.com | SUCCESS => { "changed": false, "ping": "pong" } |
You can issue the same command only on a specific group:
1 2 3 4 5 6 7 8 9 |
[root@controlnode ansible]# ansible -i inventory labservers -m ping managedhost1.example.com | SUCCESS => { "changed": false, "ping": "pong" } managedhost2.example.com | SUCCESS => { "changed": false, "ping": "pong" } |
You can issue the same command only on a specific host if needed.
1 2 3 4 5 |
[root@controlnode ansible]# ansible -i inventory all -m ping --limit managedhost1 managedhost1 | SUCCESS => { "changed": false, "ping": "pong" } |
The flag -a
may be used without -m
(module) to run shell command.
1 2 3 4 5 6 7 8 9 |
[root@controlnode ansible]# ansible all -i inventory -a "/usr/bin/echo hi" managedhost2.example.com | SUCCESS | rc=0 >> hi managedhost1 | SUCCESS | rc=0 >> hi managedhost1.example.com | SUCCESS | rc=0 >> hi |
To reboot all managed hosts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@controlnode ansible]# ansible all -i inventory -a "/sbin/reboot" managedhost2.example.com | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: Shared connection to managedhost2.example.com closed.\r\n", "unreachable": true } managedhost1.example.com | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: Shared connection to managedhost1.example.com closed.\r\n", "unreachable": true } managedhost1 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: Shared connection to managedhost1.example.com closed.\r\n", "unreachable": true } |
If you need to copy a file to multiple destinations rapidly, you can use the copy module in ansible which uses SCP. So the command and its output look like below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
[root@controlnode ansible]# touch /home/miro/ansible/testfile [root@controlnode ansible]# ansible -i inventory all -m copy -a "src=/home/miro/ansible/testfile dest=/tmp/testfile" managedhost2.example.com | SUCCESS => { "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/tmp/testfile", "gid": 0, "group": "root", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "src": "/root/.ansible/tmp/ansible-tmp-1579610363.34-77937205353512/source", "state": "file", "uid": 0 } managedhost1.example.com | SUCCESS => { "changed": true, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "dest": "/tmp/testfile", "gid": 0, "group": "root", "md5sum": "d41d8cd98f00b204e9800998ecf8427e", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "src": "/root/.ansible/tmp/ansible-tmp-1579610363.35-210160212137566/source", "state": "file", "uid": 0 } managedhost1 | SUCCESS => { "changed": false, "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "path": "/tmp/testfile", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "state": "file", "uid": 0 } |
In the next example, you will find out how to install a package via the yum module on two Centos hosts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@controlnode ansible]# ansible -i inventory all -m yum -a 'name=tcpdump state=present' managedhost2.example.com | SUCCESS => { "changed": false, "msg": "", "rc": 0, "results": [ "14:tcpdump-4.5.1-3.el7.x86_64 providing tcpdump is already installed" ] } managedhost1 | SUCCESS => { "changed": false, "msg": "", "rc": 0, "results": [ "14:tcpdump-4.9.2-4.el7_7.1.x86_64 providing tcpdump is already installed" ] } managedhost1.example.com | SUCCESS => { "changed": false, "msg": "", "rc": 0, "results": [ "14:tcpdump-4.9.2-4.el7_7.1.x86_64 providing tcpdump is already installed" ] } |
If you are not logged as superuser you will have problem to use yum:
1 2 3 4 5 6 7 8 9 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m yum -a "name=elinks state=latest" managedhost1 | FAILED! => { "changed": true, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": [ "Loaded plugins: fastestmirror\n" ] } |
So you can use -b parameter to beacome a root on remote host:
1 2 3 4 5 6 7 8 9 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m yum -a "name=elinks state=latest" managedhost1 | SUCCESS => { "changed": true, "msg": "", "rc": 0, "results": [ "Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: centos1.hti.pl\n * centosplus: mirroronet.pl\n * extras: mirroronet.pl\n * updates: mirroronet.pl\nResolving Dependencies\n--> Running transaction check\n---> Package elinks.x86_64 0:0.12-0.37.pre6.el7.0.1 will be installed\n--> Processing Dependency: libnss_compat_ossl.so.0()(64bit) for package: elinks-0.12-0.37.pre6.el7.0.1.x86_64\n--> Processing Dependency: libmozjs185.so.1.0()(64bit) for package: elinks-0.12-0.37.pre6.el7.0.1.x86_64\n--> Running transaction check\n---> Package js.x86_64 1:1.8.5-20.el7 will be installed\n---> Package nss_compat_ossl.x86_64 0:0.9.6-8.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository\n Size\n================================================================================\nInstalling:\n elinks x86_64 0.12-0.37.pre6.el7.0.1 base 882 k\nInstalling for dependencies:\n js x86_64 1:1.8.5-20.el7 base 2.3 M\n nss_compat_ossl x86_64 0.9.6-8.el7 base 37 k\n\nTransaction Summary\n================================================================================\nInstall 1 Package (+2 Dependent packages)\n\nTotal download size: 3.2 M\nInstalled size: 9.6 M\nDownloading packages:\n--------------------------------------------------------------------------------\nTotal 1.7 MB/s | 3.2 MB 00:01 \nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : nss_compat_ossl-0.9.6-8.el7.x86_64 1/3 \n Installing : 1:js-1.8.5-20.el7.x86_64 2/3 \n Installing : elinks-0.12-0.37.pre6.el7.0.1.x86_64 3/3 \n Verifying : elinks-0.12-0.37.pre6.el7.0.1.x86_64 1/3 \n Verifying : 1:js-1.8.5-20.el7.x86_64 2/3 \n Verifying : nss_compat_ossl-0.9.6-8.el7.x86_64 3/3 \n\nInstalled:\n elinks.x86_64 0:0.12-0.37.pre6.el7.0.1 \n\nDependency Installed:\n js.x86_64 1:1.8.5-20.el7 nss_compat_ossl.x86_64 0:0.9.6-8.el7 \n\nComplete!\n" ] } |
But user miro must be added to sudoers file.
Creating a file on managed host in user directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a "path=/home/miro/newfile state=touch" managedhost1 | SUCCESS => { "changed": true, "dest": "/home/miro/newfile", "gid": 1000, "group": "miro", "mode": "0664", "owner": "miro", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 1000 } |
The same in the root home directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m file -a "path=/root/newfile state=touch" managedhost1 | SUCCESS => { "changed": true, "dest": "/root/newfile", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "state": "file", "uid": 0 } |
Informations about the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a "path=/home/miro/newfile" managedhost1 | SUCCESS => { "changed": false, "gid": 1000, "group": "miro", "mode": "0664", "owner": "miro", "path": "/home/miro/newfile", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 1000 } |
Changing properties of the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a "path=/home/miro/newfile mode=0400" managedhost1 | SUCCESS => { "changed": true, "gid": 1000, "group": "miro", "mode": "0400", "owner": "miro", "path": "/home/miro/newfile", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 1000 } |
Changing ownership of file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a "path=/home/miro/newfile owner=root" managedhost1 | FAILED! => { "changed": false, "gid": 1000, "group": "miro", "mode": "0400", "msg": "chown failed: [Errno 1] Operacja niedozwolona: '/home/miro/newfile'", "owner": "miro", "path": "/home/miro/newfile", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 1000 } |
The ownership of file can be only changed by root. We must add -b
parameter:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m file -a "path=/home/miro/newfile owner=root" managedhost1 | SUCCESS => { "changed": true, "gid": 1000, "group": "miro", "mode": "0400", "owner": "root", "path": "/home/miro/newfile", "secontext": "unconfined_u:object_r:user_home_t:s0", "size": 0, "state": "file", "uid": 0 } |
Creating a user sam in the labservers
group:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
[miro@controlnode ansible]$ ansible labservers -i inventory -b -m user -a "name=sam" managedhost1.example.com | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 4002, "home": "/home/sam", "name": "sam", "shell": "/bin/bash", "state": "present", "system": false, "uid": 4002 } yes managedhost2.example.com | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 6004, "home": "/home/sam", "name": "sam", "shell": "/bin/bash", "state": "present", "system": false, "uid": 6004 } |
Adding user sam to the wheel group. Append parameter is needed due to we dont wan’t to wipe out group file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
[miro@controlnode ansible]$ ansible labservers -i inventory -b -m user -a "name=sam append=yes groups=wheel" managedhost1.example.com | SUCCESS => { "append": true, "changed": true, "comment": "", "group": 4002, "groups": "wheel", "home": "/home/sam", "move_home": false, "name": "sam", "shell": "/bin/bash", "state": "present", "uid": 4002 } managedhost2.example.com | SUCCESS => { "append": true, "changed": true, "comment": "", "group": 6004, "groups": "wheel", "home": "/home/sam", "move_home": false, "name": "sam", "shell": "/bin/bash", "state": "present", "uid": 6004 } |
Gathering facts about managed hosts:
1 |
[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m setup |