{"id":3197,"date":"2020-01-14T16:43:00","date_gmt":"2020-01-14T15:43:00","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=3197"},"modified":"2021-05-25T16:21:16","modified_gmt":"2021-05-25T14:21:16","slug":"ansible-ad-hoc-commands","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2020\/01\/14\/ansible-ad-hoc-commands\/","title":{"rendered":"Ansible Ad-hoc commands"},"content":{"rendered":"<p>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.<\/p>\n<p><!--more--><\/p>\n<ul>\n<li>You can run ansible either ad-hoc or as a playbook<\/li>\n<li>Both methods have the same capabilities<\/li>\n<li>Ad-hoc commands are effectively one-liners<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Use cases for Ad-hoc<\/span><\/p>\n<ul>\n<li>Operational commands\n<ul>\n<li>Checking log contents<\/li>\n<li>Daemon control<\/li>\n<li>Process management<\/li>\n<\/ul>\n<\/li>\n<li>Informational commands\n<ul>\n<li>Check installed software<\/li>\n<li>Check system properties<\/li>\n<li>Gather system performance information (CPU, disk space, memory use)<\/li>\n<\/ul>\n<\/li>\n<li>Research\n<ul>\n<li>Work with unfamiliar modules on test systems<\/li>\n<li>Practice for playbook engineering<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Ad-hoc vs Playbook<\/span><\/p>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<tbody>\n<tr>\n<td style=\"width: 50%;\"><span style=\"color: #99cc00;\">Ad-hoc mode<\/span><\/td>\n<td style=\"width: 50%;\"><span style=\"color: #99cc00;\">\u00a0Playbook mode<\/span><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">\n<ul>\n<li>Command: ansible<\/li>\n<li>Effective for one-time commands, operational<\/li>\n<li>Similar to a single bash command<\/li>\n<\/ul>\n<\/td>\n<td style=\"width: 50%;\">\n<ul>\n<li>Command: ansible-playbook<\/li>\n<li>Effective for deployments, routine tasks, system activities, information gathering, and research deployment<\/li>\n<li>Similar to bash script<\/li>\n<\/ul>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color: #3366ff;\">Common modules<\/span><\/p>\n<ul>\n<li><code>ping<\/code> &#8211; Validate if server is up and recheable. No required parameters.<\/li>\n<li><code>setup<\/code> &#8211; Gather ansible facts. No required parameters.<\/li>\n<li><code>yum<\/code> &#8211; Use yum package manager. Parameters <code>name<\/code> and <code>state<\/code>.<\/li>\n<li><code>service<\/code> &#8211; Control Daemons. Parameters <code>name<\/code> and <code>state<\/code>.<\/li>\n<li><code>user<\/code> &#8211; Manipulate system users. Parameters <code>name<\/code>.<\/li>\n<li><code>copy<\/code> &#8211; Copy files. Parameters <code>src<\/code>.<\/li>\n<li><code>file<\/code> &#8211; Work with files. Parameters <code>path<\/code>.<\/li>\n<li><code>git<\/code> &#8211; Interact with git repositories. Parameters <code>repo<\/code> and <code>dest<\/code>.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>You can check if the hosts are accessible from the ansible server by issuing a ping command on all hosts.<\/p>\n<pre class=\"lang:sh decode:true\">[root@controlnode ansible]# cat inventory\r\nmanagedhost1 ansible_host=managedhost1.example.com\r\n\r\n[labservers]\r\nmanagedhost1.example.com\r\nmanagedhost2.example.com\r\n\r\n[root@controlnode ansible]# ansible -i inventory all -m ping\r\nmanagedhost2.example.com | UNREACHABLE! =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"Failed to connect to the host via ssh: ssh: Could not resolve hostname managedhost2.example.com: Name or service not known\\r\\n\",\r\n\"unreachable\": true\r\n}\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"ping\": \"pong\"\r\n}\r\nmanagedhost1.example.com | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"ping\": \"pong\"\r\n}\r\n<\/pre>\n<p>You can issue the same command only on a specific group:<\/p>\n<pre class=\"lang:sh decode:true \">[root@controlnode ansible]# ansible -i inventory labservers -m ping\r\nmanagedhost1.example.com | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"ping\": \"pong\"\r\n}\r\nmanagedhost2.example.com | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"ping\": \"pong\"\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>You can issue the same command only on a specific host if needed.<\/p>\n<pre class=\"lang:sh decode:true\">[root@controlnode ansible]# ansible -i inventory all -m ping --limit managedhost1\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"ping\": \"pong\"\r\n}<\/pre>\n<p>The flag <code>-a<\/code> may be used without <code>-m<\/code> (module) to run shell command.<\/p>\n<pre class=\"lang:sh decode:true \">[root@controlnode ansible]# ansible all -i inventory -a \"\/usr\/bin\/echo hi\"\r\nmanagedhost2.example.com | SUCCESS | rc=0 &gt;&gt;\r\nhi\r\n\r\nmanagedhost1 | SUCCESS | rc=0 &gt;&gt;\r\nhi\r\n\r\nmanagedhost1.example.com | SUCCESS | rc=0 &gt;&gt;\r\nhi<\/pre>\n<p>To reboot all managed hosts:<\/p>\n<pre class=\"lang:sh decode:true \">[root@controlnode ansible]# ansible all -i inventory -a \"\/sbin\/reboot\"\r\nmanagedhost2.example.com | UNREACHABLE! =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"Failed to connect to the host via ssh: Shared connection to managedhost2.example.com closed.\\r\\n\",\r\n\"unreachable\": true\r\n}\r\nmanagedhost1.example.com | UNREACHABLE! =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"Failed to connect to the host via ssh: Shared connection to managedhost1.example.com closed.\\r\\n\",\r\n\"unreachable\": true\r\n}\r\nmanagedhost1 | UNREACHABLE! =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"Failed to connect to the host via ssh: Shared connection to managedhost1.example.com closed.\\r\\n\",\r\n\"unreachable\": true\r\n}<\/pre>\n<p>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:<\/p>\n<pre class=\"lang:sh decode:true \">[root@controlnode ansible]# touch \/home\/miro\/ansible\/testfile\r\n[root@controlnode ansible]# ansible -i inventory all -m copy -a \"src=\/home\/miro\/ansible\/testfile dest=\/tmp\/testfile\"\r\nmanagedhost2.example.com | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"checksum\": \"da39a3ee5e6b4b0d3255bfef95601890afd80709\",\r\n\"dest\": \"\/tmp\/testfile\",\r\n\"gid\": 0,\r\n\"group\": \"root\",\r\n\"md5sum\": \"d41d8cd98f00b204e9800998ecf8427e\",\r\n\"mode\": \"0644\",\r\n\"owner\": \"root\",\r\n\"secontext\": \"unconfined_u:object_r:admin_home_t:s0\",\r\n\"size\": 0,\r\n\"src\": \"\/root\/.ansible\/tmp\/ansible-tmp-1579610363.34-77937205353512\/source\",\r\n\"state\": \"file\",\r\n\"uid\": 0\r\n}\r\nmanagedhost1.example.com | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"checksum\": \"da39a3ee5e6b4b0d3255bfef95601890afd80709\",\r\n\"dest\": \"\/tmp\/testfile\",\r\n\"gid\": 0,\r\n\"group\": \"root\",\r\n\"md5sum\": \"d41d8cd98f00b204e9800998ecf8427e\",\r\n\"mode\": \"0644\",\r\n\"owner\": \"root\",\r\n\"secontext\": \"unconfined_u:object_r:admin_home_t:s0\",\r\n\"size\": 0,\r\n\"src\": \"\/root\/.ansible\/tmp\/ansible-tmp-1579610363.35-210160212137566\/source\",\r\n\"state\": \"file\",\r\n\"uid\": 0\r\n}\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"checksum\": \"da39a3ee5e6b4b0d3255bfef95601890afd80709\",\r\n\"gid\": 0,\r\n\"group\": \"root\",\r\n\"mode\": \"0644\",\r\n\"owner\": \"root\",\r\n\"path\": \"\/tmp\/testfile\",\r\n\"secontext\": \"unconfined_u:object_r:admin_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 0\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>In the next example, you will find out how to install a package via the yum module on two Centos hosts.<\/p>\n<pre class=\"lang:sh decode:true\">[root@controlnode ansible]# ansible -i inventory all -m yum -a 'name=tcpdump state=present'\r\nmanagedhost2.example.com | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"\",\r\n\"rc\": 0,\r\n\"results\": [\r\n\"14:tcpdump-4.5.1-3.el7.x86_64 providing tcpdump is already installed\"\r\n]\r\n}\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"\",\r\n\"rc\": 0,\r\n\"results\": [\r\n\"14:tcpdump-4.9.2-4.el7_7.1.x86_64 providing tcpdump is already installed\"\r\n]\r\n}\r\nmanagedhost1.example.com | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"msg\": \"\",\r\n\"rc\": 0,\r\n\"results\": [\r\n\"14:tcpdump-4.9.2-4.el7_7.1.x86_64 providing tcpdump is already installed\"\r\n]\r\n}<\/pre>\n<p>If you are not logged as superuser you will have problem to use yum:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m yum -a \"name=elinks state=latest\"\r\nmanagedhost1 | FAILED! =&gt; {\r\n\"changed\": true,\r\n\"msg\": \"You need to be root to perform this command.\\n\",\r\n\"rc\": 1,\r\n\"results\": [\r\n\"Loaded plugins: fastestmirror\\n\"\r\n]\r\n}<\/pre>\n<p>So you can use -b parameter to beacome a root on remote host:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m yum -a \"name=elinks state=latest\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"msg\": \"\",\r\n\"rc\": 0,\r\n\"results\": [\r\n\"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--&gt; Running transaction check\\n---&gt; Package elinks.x86_64 0:0.12-0.37.pre6.el7.0.1 will be installed\\n--&gt; Processing Dependency: libnss_compat_ossl.so.0()(64bit) for package: elinks-0.12-0.37.pre6.el7.0.1.x86_64\\n--&gt; Processing Dependency: libmozjs185.so.1.0()(64bit) for package: elinks-0.12-0.37.pre6.el7.0.1.x86_64\\n--&gt; Running transaction check\\n---&gt; Package js.x86_64 1:1.8.5-20.el7 will be installed\\n---&gt; Package nss_compat_ossl.x86_64 0:0.9.6-8.el7 will be installed\\n--&gt; 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\"\r\n]\r\n}<\/pre>\n<p>But user miro must be added to sudoers file.<\/p>\n<p>Creating a file on managed host in user directory:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a \"path=\/home\/miro\/newfile state=touch\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"dest\": \"\/home\/miro\/newfile\",\r\n\"gid\": 1000,\r\n\"group\": \"miro\",\r\n\"mode\": \"0664\",\r\n\"owner\": \"miro\",\r\n\"secontext\": \"unconfined_u:object_r:user_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 1000\r\n}<\/pre>\n<p>The same in the root home directory:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m file -a \"path=\/root\/newfile state=touch\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"dest\": \"\/root\/newfile\",\r\n\"gid\": 0,\r\n\"group\": \"root\",\r\n\"mode\": \"0644\",\r\n\"owner\": \"root\",\r\n\"secontext\": \"unconfined_u:object_r:admin_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 0\r\n}<\/pre>\n<p>Informations about the file:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a \"path=\/home\/miro\/newfile\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": false,\r\n\"gid\": 1000,\r\n\"group\": \"miro\",\r\n\"mode\": \"0664\",\r\n\"owner\": \"miro\",\r\n\"path\": \"\/home\/miro\/newfile\",\r\n\"secontext\": \"unconfined_u:object_r:user_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 1000\r\n}<\/pre>\n<p>Changing properties of the file:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a \"path=\/home\/miro\/newfile mode=0400\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"gid\": 1000,\r\n\"group\": \"miro\",\r\n\"mode\": \"0400\",\r\n\"owner\": \"miro\",\r\n\"path\": \"\/home\/miro\/newfile\",\r\n\"secontext\": \"unconfined_u:object_r:user_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 1000\r\n}<\/pre>\n<p>Changing ownership of file:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -m file -a \"path=\/home\/miro\/newfile owner=root\"\r\nmanagedhost1 | FAILED! =&gt; {\r\n\"changed\": false,\r\n\"gid\": 1000,\r\n\"group\": \"miro\",\r\n\"mode\": \"0400\",\r\n\"msg\": \"chown failed: [Errno 1] Operacja niedozwolona: '\/home\/miro\/newfile'\",\r\n\"owner\": \"miro\",\r\n\"path\": \"\/home\/miro\/newfile\",\r\n\"secontext\": \"unconfined_u:object_r:user_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 1000\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>The ownership of file can be only changed by root. We must add <code>-b<\/code> parameter:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m file -a \"path=\/home\/miro\/newfile owner=root\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"gid\": 1000,\r\n\"group\": \"miro\",\r\n\"mode\": \"0400\",\r\n\"owner\": \"root\",\r\n\"path\": \"\/home\/miro\/newfile\",\r\n\"secontext\": \"unconfined_u:object_r:user_home_t:s0\",\r\n\"size\": 0,\r\n\"state\": \"file\",\r\n\"uid\": 0\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>Creating a user sam in the <code>labservers<\/code> group:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible labservers -i inventory -b -m user -a \"name=sam\"\r\nmanagedhost1.example.com | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"comment\": \"\",\r\n\"createhome\": true,\r\n\"group\": 4002,\r\n\"home\": \"\/home\/sam\",\r\n\"name\": \"sam\",\r\n\"shell\": \"\/bin\/bash\",\r\n\"state\": \"present\",\r\n\"system\": false,\r\n\"uid\": 4002\r\n}\r\nyes\r\nmanagedhost2.example.com | SUCCESS =&gt; {\r\n\"changed\": true,\r\n\"comment\": \"\",\r\n\"createhome\": true,\r\n\"group\": 6004,\r\n\"home\": \"\/home\/sam\",\r\n\"name\": \"sam\",\r\n\"shell\": \"\/bin\/bash\",\r\n\"state\": \"present\",\r\n\"system\": false,\r\n\"uid\": 6004\r\n}<\/pre>\n<p>Adding user sam\u00a0 to the wheel group. Append parameter is needed due to we dont wan&#8217;t to wipe out group file:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible labservers -i inventory -b -m user -a \"name=sam append=yes groups=wheel\"\r\nmanagedhost1.example.com | SUCCESS =&gt; {\r\n\"append\": true,\r\n\"changed\": true,\r\n\"comment\": \"\",\r\n\"group\": 4002,\r\n\"groups\": \"wheel\",\r\n\"home\": \"\/home\/sam\",\r\n\"move_home\": false,\r\n\"name\": \"sam\",\r\n\"shell\": \"\/bin\/bash\",\r\n\"state\": \"present\",\r\n\"uid\": 4002\r\n}\r\nmanagedhost2.example.com | SUCCESS =&gt; {\r\n\"append\": true,\r\n\"changed\": true,\r\n\"comment\": \"\",\r\n\"group\": 6004,\r\n\"groups\": \"wheel\",\r\n\"home\": \"\/home\/sam\",\r\n\"move_home\": false,\r\n\"name\": \"sam\",\r\n\"shell\": \"\/bin\/bash\",\r\n\"state\": \"present\",\r\n\"uid\": 6004\r\n}<\/pre>\n<p>Gathering facts about managed hosts:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -b -m setup<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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.<\/p>\n","protected":false},"author":1,"featured_media":3210,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[86],"tags":[],"_links":{"self":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3197"}],"collection":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/comments?post=3197"}],"version-history":[{"count":22,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3197\/revisions"}],"predecessor-version":[{"id":3225,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3197\/revisions\/3225"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media\/3210"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=3197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=3197"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=3197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}