{"id":3701,"date":"2020-05-17T11:06:29","date_gmt":"2020-05-17T09:06:29","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=3701"},"modified":"2020-09-28T22:57:13","modified_gmt":"2020-09-28T20:57:13","slug":"ansible-comprehensive-notes","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2020\/05\/17\/ansible-comprehensive-notes\/","title":{"rendered":"Ansible Training Notes"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p><!--more--><\/p>\n<h2><span style=\"color: #3366ff;\">Configuration file<\/span><\/h2>\n<p>When installed, the ansible package provides a base configuration file located at <code>\/etc\/ansible\/ansible.cfg<\/code>.<\/p>\n<p>Priority in which the configuration files are processed:<\/p>\n<ol>\n<li><code>$ANSIBLE_CONFIG<\/code> (an environment variable)<\/li>\n<li><code>.\/ansible.cfg<\/code> (in the current directory)<\/li>\n<li><code>~\/.ansible.cfg<\/code> (the user&#8217;s home directory)<\/li>\n<li><code>\/etc\/ansible\/ansible.cfg<\/code><\/li>\n<\/ol>\n<p>To find out what config file is in use, run the following:<\/p>\n<pre><code>$ ansible --version\r\nansible 2.7.9\r\nconfig file = \/home\/ansible\/ansible.cfg\r\n<\/code><\/pre>\n<p>To get started, copy configuration files to the home directory:<\/p>\n<pre><code>$ cp \/etc\/ansible\/ansible.cfg ~\/ansible.cfg\r\n$ cp \/etc\/ansible\/hosts ~\/inventory\r\n<\/code><\/pre>\n<p>Note the maximum number of simultaneous connections that Ansible makes is controlled by the forks parameter in <code>ansible.cfg<\/code>:<\/p>\n<pre><code>forks 5\r\n<\/code><\/pre>\n<h2><span style=\"color: #3366ff;\">Inventory<\/span><\/h2>\n<p>Example inventory configuration:<\/p>\n<pre class=\"lang:sh decode:true\">$ cat ~\/inventory\r\n\r\n[myself]\r\ncontrolnode.example.com host=controlnode ansible_connection=local\r\n\r\n[web]\r\nmanagedhost[1:2].example.com host=managedhost[1:2]\r\n\r\n[db] \r\nmanagedhost[3:4].example.com host=managedhost[3:4] ansible_user=dbadmin\r\n\r\n[lab:children]\r\nweb\r\ndb<\/pre>\n<p>Listing hosts from inventory:<\/p>\n<pre class=\"lang:sh decode:true \">[ansible@controlnode ~]$ ansible all --list-hosts\r\nhosts (5):\r\ncontrolnode.example.com\r\nmanagedhost1.example.com\r\nmanagedhost2.example.com\r\nmanagedhost3.example.com\r\nmanagedhost4.example.com\r\n\r\n[ansible@controlnode ~]$ ansible web --list-hosts\r\nhosts (2):\r\nmanagedhost1.example.com\r\nmanagedhost2.example.com\r\n\r\n[ansible@controlnode ~]$ ansible db --list-hosts\r\nhosts (2):\r\nmanagedhost3.example.com\r\nmanagedhost4.example.com<\/pre>\n<p>There is a special group named &#8220;all&#8221; that matches all managed hosts in the inventory.<\/p>\n<pre><code>- hosts: all\r\n<\/code><\/pre>\n<p>There is also a special group named &#8220;ungrouped&#8221; which matches all managed hosts in the inventory that are not members of any group.<\/p>\n<pre><code>- hosts: ungrouped\r\n<\/code><\/pre>\n<p>Quote host patterns used on the CLI to protect them from unwanted shell expansion:<\/p>\n<pre class=\"\"><code>- hosts: '*.example.com'\r\n- hosts: '10.0.0.*'\r\n<\/code><\/pre>\n<p>Multiple entries in the inventory can be referenced using lists:<\/p>\n<pre><code>- hosts: web,managedhost1.example.com\r\n<\/code><\/pre>\n<p>If you have static and dynamic inventory files in the same directory, then they are merged and treated as one inventory!<\/p>\n<p>Show inventory for all:<\/p>\n<pre><code>$ ansible \"*\" -i ~\/inventory --list-hosts<\/code><\/pre>\n<p>Target two inventories from the command line like this:<\/p>\n<pre class=\"lang:sh decode:true \">$ ansible-playbook play.yml -i staging -i production<\/pre>\n<p>Aggregating inventory sources with a directory::<\/p>\n<pre class=\"lang:sh decode:true \">inventory\/\r\n  openstack.yml # configure inventory plugin to get hosts from Openstack cloud\r\n  dynamic-inventory.py # add additional hosts with dynamic inventory script\r\n  static-inventory # add static hosts and groups\r\n  group_vars\/\r\n    all.yml # assign variables to all hosts<\/pre>\n<p>Privilege escalation configuration in <code>ansible.cfg<\/code>:<\/p>\n<pre><code>[privilege_escalation]\r\nbecome = false\r\nbecome_method = sudo\r\nbecome_user = root\r\nbecome_ask_pass = false\r\n<\/code><\/pre>\n<p>How to set the default user to use for playbooks in <code>ansible.cfg<\/code>:<\/p>\n<pre><code>remote_user = ansible\r\n<\/code><\/pre>\n<p>How to set the log file in <code>ansible.cfg<\/code>:<\/p>\n<pre><code>log_path = \/home\/ansible\/ansible.log\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ad Hoc Commands<\/span><\/h2>\n<div id=\"primary\" class=\"content-area\">\n<article id=\"post-3197\" class=\"post-3197 post type-post status-publish format-standard has-post-thumbnail hentry category-ansible category-infrastructure-as-code\">\n<header class=\"entry-header\">\n<div class=\"entry-meta\">Common modules:<\/div>\n<\/header>\n<div class=\"entry-content\">\n<ul>\n<li><strong><code>ping<\/code> <\/strong>\u2013 Validate if server is up and recheable. No required parameters.<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true\">$ ansible all -m ping\r\n$ ansible all -m ping --limit managedhost1<\/pre>\n<ul>\n<li><strong><code>command<\/code> <\/strong>&#8211; If no module is defined, Ansible uses the internally predefined &#8220;<em>command<\/em>&#8221; module.\n<pre class=\"lang:sh decode:true\">$ ansible all -m command -a \/usr\/bin\/hostname -o\r\n$ ansible all -a \"\/usr\/bin\/echo hi\"\r\n$ ansible all -a \"\/sbin\/reboot\"\r\n$ ansible all -a \"\/sbin\/reboot\" -f 10\r\n$ ansible managedhost1 -a '\/bin\/yum list installed postfix'<\/pre>\n<\/li>\n<\/ul>\n<ul>\n<li><strong><code>yum<\/code> <\/strong>\u2013 Use yum package manager. Parameters <code>name<\/code> and <code>state<\/code>.<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true \">$ ansible all -m yum -a 'name=tcpdump state=present'\r\n$ ansible all -m yum -a 'list=tcpdump'\r\n$ ansible managedhost1 -b -m yum -a \"name=elinks state=latest\"<\/pre>\n<ul>\n<li><strong><code>service<\/code> <\/strong>\u2013 Control Daemons. Parameters <code>name<\/code> and <code>state<\/code>.<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true \">$ ansible managedhost1 -m service -a 'name=httpd state=started'<\/pre>\n<ul>\n<li><strong><code>user<\/code> <\/strong>\u2013 Manipulate system users. Parameters <code>name<\/code>.<\/li>\n<\/ul>\n<p>Creating a user sam in the labservers group:<\/p>\n<pre class=\"lang:sh decode:true\">$ ansible labservers -i inventory -b -m user -a \"name=sam\"<\/pre>\n<p>Adding user <em>sam<\/em> to the <em>wheel<\/em> group. Append parameter is needed due to we dont wan\u2019t to wipe out group file:<\/p>\n<pre class=\"lang:sh decode:true\">$ ansible labservers -b -m user -a \"name=sam append=yes groups=wheel\"<\/pre>\n<ul>\n<li><strong><code>copy<\/code> <\/strong>\u2013 Copy files. Parameters <code>src<\/code>.<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true\">$ ansible all -m copy -a \"src=\/home\/miro\/ansible\/testfile dest=\/tmp\/testfile\"<\/pre>\n<p>Use the copy module to change content of a file:<\/p>\n<pre class=\"\"><code>$ ansible all -b -m copy -a 'content=\"Host managed by Ansible\\n\" dest=\/etc\/motd'<\/code><\/pre>\n<ul>\n<li><strong><code>file<\/code> <\/strong>\u2013 Work with files. Parameters <code>path<\/code>.<\/li>\n<\/ul>\n<div id=\"crayon-5ed38ed873a6e030971957-2\" class=\"crayon-line crayon-striped-line\"><span class=\"crayon-e\">Creating a file on managed host in user directory:<\/span><\/div>\n<div class=\"crayon-line crayon-striped-line\">\n<pre class=\"lang:sh decode:true\">$ ansible managedhost1\u00a0 -m file -a \"path=\/home\/miro\/newfile state=touch\"<\/pre>\n<\/div>\n<div>\n<p>Informations about the file:<\/p>\n<pre class=\"lang:sh decode:true\">$ ansible managedhost1\u00a0 -m file -a \"path=\/home\/miro\/newfile\"<\/pre>\n<p>Changing properties of the file:<\/p>\n<pre class=\"lang:sh decode:true\">$ ansible managedhost1 -m file -a \"path=\/home\/miro\/newfile mode=0400\"\r\n<\/pre>\n<\/div>\n<ul>\n<li><code><strong>setup<\/strong> <\/code>\u2013 Gather ansible facts. No required parameters.\n<pre class=\"lang:sh decode:true \">$ ansible managedhost1\u00a0 -b -m setup<\/pre>\n<\/li>\n<li><code><strong>debug<\/strong> <\/code>&#8211; prints statements during execution and can be useful for debugging variables or expressions without necessarily halting the playbook<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true\">$ ansible managedhost1 -m debug -a \"var=vars\"<\/pre>\n<ul>\n<li><strong><code>git<\/code> <\/strong>\u2013 Interact with git repositories. Parameters <code>repo<\/code> and <code>dest<\/code>.<\/li>\n<\/ul>\n<\/div>\n<\/article>\n<p>Note: if possible, try to avoid the <code>command<\/code>, <code>shell<\/code> and <code>raw<\/code> modules in playbooks! It&#8217;s easy to write non-idempotent playbooks this way.<\/p>\n<\/div>\n<h2><span style=\"color: #3366ff;\">Documentation<\/span><\/h2>\n<p>Ansible module documentation and playbook snippets:<\/p>\n<pre class=\"\"><code>$ ansible-doc -l\r\n$ ansible-doc -s module_name\r\n$ ansible-doc module_name\r\n<\/code><\/pre>\n<h2><span style=\"color: #3366ff;\">Ansible-playbook command<\/span><\/h2>\n<p>Ansible executes plays and tasks in the order they are presented! How to check for YAML syntax errors:<\/p>\n<pre><code>$ ansible-playbook --syntax-check playbook.yaml\r\n<\/code><\/pre>\n<p>Limit playbook to specyfied host:<\/p>\n<pre class=\"lang:sh decode:true \">$ ansible-playbook playbook.yaml -l managedhost1<\/pre>\n<p>Which host(s) the playbook applies to?<\/p>\n<pre><code>$ ansible-playbook playbook.yaml --list-hosts\r\n<\/code><\/pre>\n<p>Whats tasks will be performed?<\/p>\n<pre><code>$ ansible-playbook playbook.yaml --list-tasks\r\n<\/code><\/pre>\n<p>Run one task at a time:<\/p>\n<pre><code>$ ansible-playbook playbook.yaml --step\r\n<\/code><\/pre>\n<p>Playbook dry-run:<\/p>\n<pre><code>$ ansible-playbook -C playbook.yaml\r\n<\/code><\/pre>\n<p>Playbook step-by-step execution:<\/p>\n<pre><code>$ ansible-playbook --step playbook.yaml\r\n<\/code><\/pre>\n<p>The beginning of each play begins with a single dash followed by a space.<\/p>\n<p>To only run the first task, the<code> \u2013tags<\/code> argument can be used:<\/p>\n<pre class=\"lang:sh decode:true \">$ ansible-playbook main.yml --tags 'production'<\/pre>\n<p>To skip tagged task:<\/p>\n<pre class=\"lang:sh decode:true \">$ ansible-playbook main.yml --skip-tags 'production'<\/pre>\n<p>&nbsp;<\/p>\n<p><span class=\"crayon-s\">\u00a0<\/span><span style=\"color: #3366ff;\">Playbook<\/span><\/p>\n<p>Playbook attributes:<\/p>\n<ul>\n<li><code><strong>hosts<\/strong> <\/code>&#8211; hosts which are managed by ansible. Must be defined in every play.<\/li>\n<li><strong><code>user<\/code> <\/strong>&#8211; user which will run ansible playbook<\/li>\n<li><code><strong>remote_user<\/strong> <\/code>&#8211; can be used to define the user that runs the tasks.<\/li>\n<li><code><strong>become<\/strong> <\/code>&#8211; can be used to enable privilege escalation.<\/li>\n<li><strong><code>become_method<\/code> <\/strong>&#8211; can be used to define the privilege escalation method.<\/li>\n<li><strong><code>become_user<\/code> <\/strong>&#8211; can define the user account to be used for privilege escalation.<\/li>\n<li><code><strong>gether_facts<\/strong> <\/code>&#8211; to gether or not to gether the facts<\/li>\n<li><code><strong>tasks<\/strong> <\/code>&#8211; is defined as a list of dictionaries.<\/li>\n<li><strong><code>block<\/code> <\/strong>&#8211; can be used to group related tasks together.<\/li>\n<li><strong><code>name<\/code> <\/strong>&#8211; can be used to give a descriptive label to a play.<\/li>\n<\/ul>\n<p>Skeleton of simple playbook:<\/p>\n<pre class=\"lang:sh decode:true\">---\r\n- hosts: managedhost1\r\n  user: ansible\r\n  become: yes\r\n  #remote_user: ansible\r\n  #become_method: sudo\r\n  #become_user: root\r\n  #gether_facts: no\r\n  \r\n  vars_files: path_to_file\r\n  vars:\r\n    variable1: value1\r\n    variable2: value2\r\n\r\n  tasks:\r\n\r\n  - block:\r\n    - name: name of task1\r\n    - name: name of task2\r\n    - name: name of task3\r\n\r\n  - block:\r\n    - name: name of task4\r\n    - name: name of task5\r\n    - name: name of task6\r\n\r\n  - block:\r\n    - name: name of task7\r\n    - name: name of task8<\/pre>\n<p>Common modules:<\/p>\n<ul>\n<li><code><strong>yum<\/strong> <\/code>&#8211; Installs, upgrade, downgrades, removes, and lists packages<\/li>\n<li><code><strong>yum_repository<\/strong><\/code> &#8211; Add or remove YUM repositories<\/li>\n<li><code><strong>service<\/strong> <\/code>&#8211; Controls services on remote hosts.<\/li>\n<li><code><strong>file <\/strong><\/code>&#8211; Set attributes of files, symlinks or directories.<\/li>\n<li><code><strong>copy<\/strong> <\/code>&#8211; copies a file from the local or remote machine to a location on the remote machine.<\/li>\n<li><code><strong>firewalld<\/strong> <\/code>&#8211; manages firewalld rules.<\/li>\n<li><code><strong>template<\/strong> <\/code>&#8211; tTemplates processed by the Jinja2 templating<br \/>\nlanguage.<\/li>\n<li><strong><code>lineinfile<\/code> <\/strong>&#8211; ensures a particular line is in a file, or replace<br \/>\nan existing line using a back-referenced regular expression.<\/li>\n<li><strong><code>blockinfile<\/code> <\/strong>&#8211; This module will insert\/update\/remove a block of multi-line text<\/li>\n<li><strong><code>replace<\/code> <\/strong>&#8211; replace all instances of a pattern within a file<\/li>\n<li><strong><code>command<\/code> <\/strong>&#8211; The given command will be executed on all selected nodes. The command(s) will not be processed through the shell, so variables like `$HOME&#8217; and operations like `&#8221;&lt;&#8220;&#8216;, `&#8221;&gt;&#8221;&#8216;, `&#8221;|&#8221;&#8216;, `&#8221;;&#8221;&#8216; and `&#8221;&amp;&#8221;&#8216; will not work. Use the [shell] module if you need these features.<\/li>\n<li><code><strong>shell<\/strong> <\/code>&#8211; takes the command name followed by a list of space-delimited arguments.<\/li>\n<li><code><strong>debug<\/strong> <\/code>&#8211; prints statements during execution and can be useful for<br \/>\ndebugging variables or expressions<\/li>\n<li><strong><code>fetch<\/code> <\/strong>&#8211; Download a file from managed host.<\/li>\n<li><code><strong>get_url<\/strong> <\/code>&#8211; Downloads files from HTTP, HTTPS, or FTP to the remote server.<\/li>\n<li><code><strong>uri<\/strong> <\/code>&#8211; Interacts with HTTP and HTTPS web services<\/li>\n<li><code><strong>mysql_user<\/strong> <\/code>&#8211; Adds or removes a user from a MySQL database.<\/li>\n<li><strong><code>mysql_db<\/code><\/strong> &#8211; Add or remove MySQL databases from a remote host<\/li>\n<li><code><strong>cron<\/strong> <\/code>&#8211; manage crontab and environment variables entries.<\/li>\n<li><code><strong>at<\/strong> <\/code>&#8211; schedule a command or script file to run once in the future<\/li>\n<li><code><strong>selinux<\/strong> <\/code>&#8211; Configures the SELinux mode and policy<\/li>\n<li><strong><code>package_facts<\/code> <\/strong>&#8211; Return information about installed packages as facts<\/li>\n<li><code><strong>parted<\/strong><\/code> &#8211; Allows configuring block device partition using the `parted&#8217; command line tool.<\/li>\n<li><strong><code>lvg<\/code> <\/strong>&#8211; create voulme group<\/li>\n<li><strong><code>lvol<\/code> <\/strong>&#8211; create logical volume<\/li>\n<li><strong><code>filesystem<\/code> <\/strong>&#8211; creates a filesystem.<\/li>\n<li><code><strong>mount<\/strong> <\/code>&#8211; controls active and configured mount points in `\/etc\/fstab&#8217;.<\/li>\n<li><code><strong>archive -<\/strong><\/code>Packs an archive. It is the opposite of [unarchive].<\/li>\n<li><strong><code>unarchive<\/code>&#8211;<\/strong> Unpacks an archive. It will not unpack a compressed file that does not contain an archive.<\/li>\n<li><strong><code>authorized_key<\/code><\/strong> &#8211; Adds or removes SSH authorized keys for particular user accounts.<code><\/code><\/li>\n<li><strong><code>add_host<\/code><\/strong> &#8211; create new hosts and groups in inventory<\/li>\n<\/ul>\n<p>For each play in a playbook, you get to choose which machines in your infrastructure to target and what remote user to complete the steps as. You can use keyword <code>become<\/code> on a particular task instead of the play:<\/p>\n<pre><code>---\r\n- hosts: webservers\r\n  remote_user: ansible\r\n  serial: 2\r\n  tasks:\r\n    - service:\r\n        name: httpd\r\n        state: started\r\n      become: yes\r\n      become_method: sudo\r\n<\/code><\/pre>\n<p>Use the serial keyword to run the hosts through the play in batches. Host variables take precedence over group variables, but variables defined by a playbook take precedence over both.<\/p>\n<h2><span style=\"color: #3366ff;\">Including and Importing Files<\/span><\/h2>\n<p>When you include content, then Ansible processes included content during the run of the playbook, as content is required.<\/p>\n<p>When you import content, Ansible processes imported content when the playbook is initially read, before the run starts.<\/p>\n<p>Note that <code>include<\/code> was replaced in Ansible 2.4 with new directives such as:<\/p>\n<ul>\n<li><strong><code>vars_files<\/code><\/strong><\/li>\n<li><strong><code>include_vars<\/code><\/strong><\/li>\n<li><strong><code>include_tasks<\/code><\/strong><\/li>\n<li><strong><code>import_tasks<\/code> <\/strong><\/li>\n<li><strong><code>import_playbook<\/code><\/strong><\/li>\n<\/ul>\n<p>You can find documentation in <em>ansible-doc<\/em> command.<\/p>\n<p>These can be used to enhance the ability to reuse tasks and playbooks:<\/p>\n<pre class=\"\"><code>---\r\nhosts: all\r\ntasks:\r\n  - name: Import task file and use variablesimport_playbook:\r\n    include_vars:  variables.yml\r\n    include_tasks: tasks1.yml\r\n    import_tasks:  tasks2.yml\r\n\r\n- name: Include a play after another play\r\n  import_playbook: otherplays.yml\r\n<\/code><\/pre>\n<p>Examples:<\/p>\n<pre><code>tasks:\r\n  - name: Import task file and use variables\r\n    import_tasks: task.yml\r\n    vars:\r\n      package: vsftpd\r\n      service: vsft\r\n\r\n  - name: Install the {{ package }} package\r\n    yum:\r\n      name: \"{{ package }}\"\r\n      state: latest\r\n  - name: Start the {{ service }} service\r\n    service:\r\n      name: \"{{ service }}\"\r\n      enabled: true\r\n      state: started\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Variables<\/span><\/h2>\n<p>Places to define variables:<\/p>\n<ul>\n<li><code>vars<\/code>, <code>include_vars<\/code><\/li>\n<li>Command line (<code>-e<\/code> ) as key=value:<\/li>\n<\/ul>\n<pre class=\"lang:sh decode:true\">$ ansible-playbook play.yml -e 'key=value'<\/pre>\n<p>or YAML\/JSON<\/p>\n<pre class=\"lang:sh decode:true\">$ ansible-playbook play.yml -e '{\"myVar\":\"myValue\",\"anotherVar\":\"anotherValue\"}'<\/pre>\n<p>if filename prepend with @<\/p>\n<pre class=\"lang:sh decode:true \">$ ansible-playbook pla.yml -e \"@filename\"<\/pre>\n<ul>\n<li>Variables for hosts and host groups can be defined by creating two directories, <strong><code>group_vars<\/code> <\/strong>and <strong><code>host_vars<\/code><\/strong>, in the same working directory as the inventory file or directory.<\/li>\n<\/ul>\n<p>If the value to define varies for each datacenter, a group variable can be set for each datacenter:<\/p>\n<pre class=\"lang:sh decode:true\">[admin@station project]$ cat ~\/project\/group_vars\/datacenter1\r\npackage: httpd\r\n[admin@station project]$ cat ~\/project\/group_vars\/datacenter2\r\npackage: apache<\/pre>\n<p>If the value to be defined varies for each host in every datacenter, using host variables is recommended:<br \/>\nShell<\/p>\n<pre class=\"lang:sh decode:true\">[admin@station project]$ cat ~\/project\/host_vars\/demo1.example.com\r\npackage: httpd\r\n[admin@station project]$ cat ~\/project\/host_vars\/demo2.example.com\r\npackage: apache\r\n[admin@station project]$ cat ~\/project\/host_vars\/demo3.example.com\r\npackage: mariadb-server\r\n[admin@station project]$ cat ~\/project\/host_vars\/demo4.example.com\r\npackage: mysql-server<\/pre>\n<p>The directory structure for project, if it contained all of the example files above, might look like this:<\/p>\n<pre class=\"lang:sh decode:true\">project\r\n\u251c\u2500\u2500 ansible.cfg\r\n\u251c\u2500\u2500 group_vars\r\n\u2502 \u251c\u2500\u2500 datacenters\r\n\u2502 \u251c\u2500\u2500 datacenters1\r\n\u2502 \u2514\u2500\u2500 datacenters2\r\n\u251c\u2500\u2500 host_vars\r\n\u2502 \u251c\u2500\u2500 demo1.example.com\r\n\u2502 \u251c\u2500\u2500 demo2.example.com\r\n\u2502 \u251c\u2500\u2500 demo3.example.com\r\n\u2502 \u2514\u2500\u2500 demo4.example.com\r\n\u251c\u2500\u2500 inventory\r\n\u2514\u2500\u2500 playbook.yml<\/pre>\n<ul>\n<li>Roles, blocks, and inventories<\/li>\n<\/ul>\n<p>Whether or not you define any variables, you can access information about your hosts with the Special Variables Ansible provides, including &#8220;magic&#8221; variables, facts, and connection variables.<\/p>\n<p>The most commonly used magic variables are:<\/p>\n<ul>\n<li><code><strong>hostvars <\/strong><\/code><\/li>\n<li><code><strong>groups <\/strong><\/code><\/li>\n<li><code><strong>group_names <\/strong><\/code><\/li>\n<li><code><strong>inventory_hostname<\/strong><\/code><\/li>\n<\/ul>\n<p>Prints all magic variables:<\/p>\n<pre class=\"lang:sh decode:true \">$ ansible localhost -m debug -a \"var=vars\"<\/pre>\n<pre class=\"lang:sh decode:true\">$ ansible localhost -m debug -a \"var=hostvars\"<\/pre>\n<p>The <strong><code>group_names<\/code> <\/strong>variable contains a list of all the groups the current host is in. We can use it to install specific packages:<\/p>\n<pre><code>tasks:\r\n  - name: Install webserver packages\r\n    package:\r\n      name: \"{{ item }}\"\r\n      state: latest\r\n    loop:\r\n      - httpd\r\n      - firewalld\r\n    when: \"'webserver' in group_names\"\r\n<\/code><\/pre>\n<p>The <strong><code>hostvars<\/code> <\/strong>variable lets you access variables for another host, including facts that have been gathered about that host.<\/p>\n<pre><code>{{ hostvars['managedhost1.example.com']['ansible_default_ipv4']['address'] }}\r\n{{ hostvars['managedhost1.example.com']['ansible_fqdn'] }}\r\n<\/code><\/pre>\n<p>The <strong><code>inventory_hostname<\/code> <\/strong>variable is the name of the hostname as configured in Ansible&#8217;s inventory host file. The groups variable is a list of all the groups in the inventory.<\/p>\n<pre><code>when: inventory_hostname in groups[\"webservers\"]\r\nwhen: inventory_hostname in groups.webservers<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Facts<\/span><\/h2>\n<p>How to print facts for all hosts?<\/p>\n<pre><code>- name: Ansible fact dump\r\n  hosts: all\r\n  tasks:\r\n    - name: Print all Ansible facts\r\n      debug:\r\n        var: ansible_facts\r\n<\/code><\/pre>\n<pre><code>- name: Ansible package fact dump\r\n  hosts: all\r\n  tasks:\r\n    - name: Gather package facts\r\n      package_facts:\r\n        manager: auto\r\n<\/code><\/pre>\n<p>Before Ansible 2.5, facts were injected as individual variables prefixed with the string <code>ansible_<\/code> instead of being part of the <code>ansible_facts<\/code> variable.<\/p>\n<p>At the time of writing this, Ansible recognises both the new fact naming system (using <code>ansible_facts<\/code>) and the old pre 2.5 naming system where facts are injected as separate variables.<\/p>\n<p>You can use an ad-hoc command to run the setup module to print the value of all facts:<\/p>\n<pre><code>$ ansible localhost -m setup\r\n<\/code><\/pre>\n<p>or<\/p>\n<pre class=\"lang:sh decode:true\">$ ansible localhost -m gather_facts<\/pre>\n<p>Filter results:<\/p>\n<pre><code>$ ansible localhost -m setup -a 'filter=*distribution*'\r\n<\/code><\/pre>\n<p>Some commonly used facts. In no particular order:<\/p>\n<pre><code>ansible_facts['hostname'] = ansible_hostname\r\nansible_facts['fqdn'] = ansible_fqdn\r\nansible_facts['default_ipv4']['address'] = ansible_default_ipv4.address\r\nansible_facts['distribution'] = ansible_distribution\r\nansible_facts['distribution_major_version'] = ansible_major_version\r\nansible_facts['domain'] = ansible_domain\r\nansible_facts['memtotal_mb'] = ansible_memtotal_mb\r\nansible_facts['processor_count'] = ansible_processor_count\r\n<\/code><\/pre>\n<p>To disable fact gathering for a play, you can set the <code>gather_facts<\/code> keyword to &#8220;no&#8221;:<\/p>\n<pre><code>gather_facts: no\r\n<\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><strong>Custom facts<\/strong> can be defined in a static file, formatted as an <strong>INI<\/strong> file or using JSON and placed in <code>\/etc\/ansible\/facts.d<\/code> and the file name must end in <code>.fact<\/code>.<\/p>\n<p>They can also be executable scripts which generate JSON output, just like a dynamic inventory script. Note that custom fact files cannot be in YAML format!<\/p>\n<p>Creating custom facts on managedhost1:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@managedhost1 ~]$ sudo mkdir -p \/etc\/ansible\/facts.d\r\n[miro@managedhost1 ~]$ sudo vim \/etc\/ansible\/facts.d\/prefs.fact\r\n[miro@managedhost1 ~]$ cat \/etc\/ansible\/facts.d\/prefs.fact\r\n[location]\r\ntype=physical\r\ndatacenter=Katowice\r\n<\/pre>\n<p>On the controlnode we can print local facts from the managedhost1:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost1 -m setup -a \"filter=ansible_local\"\r\nmanagedhost1 | SUCCESS =&gt; {\r\n\"ansible_facts\": {\r\n\"ansible_local\": {\r\n\"prefs\": {\r\n\"location\": {\r\n\"datacenter\": \"Katowice\",\r\n\"type\": \"physical\"\r\n}\r\n}\r\n}\r\n},\r\n\"changed\": false\r\n}<\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Loops, Conditionals<br \/>\n<\/span><\/h2>\n<p>Ansible supports iterating a task over a set of items using the <code>loop<\/code> keyword. A simple loop iterates a task over a list of items.<\/p>\n<p>Since Ansible 2.5, the recommended way to write loops is to use the <code>loop<\/code> keyword. The old syntax used loops prefixed with <code>with_<\/code>.<\/p>\n<pre><code>- name: Ensure that packages are present\r\n  package:\r\n    name: \"{{ item }}\"\r\n    state: present\r\n  loop:\r\n    - firewalld\r\n    - httpd\r\n    - vsftpd\r\n<\/code><\/pre>\n<pre><code>- name: Ensure that web server ports are open\r\n  vars:\r\n    packages:\r\n    - http\r\n    - https\r\n  firewalld:\r\n    service: \"{{ item }}\"\r\n    immediate: true\r\n    permanent: true\r\n    state: enabled\r\n  loop:\r\n    \"{{ packages }}\"\r\n   \r\n<\/code><\/pre>\n<p>Ansible conditionals operators<\/p>\n<table>\n<tbody>\n<tr>\n<td><em><strong>Operator<\/strong><\/em><\/td>\n<td><em><strong>Example<\/strong><\/em><\/td>\n<\/tr>\n<tr>\n<td>Equal<\/td>\n<td><code>\u00a0\"{{ max_memory }} == 512\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Less than<\/td>\n<td><code>\u00a0\"{{ min_memory }} &lt; 128\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Greater than<\/td>\n<td><code>\"{{ min_memory }} &gt; 256\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Less than or equal to<\/td>\n<td><code>\u00a0\"{{ min_memory }} &lt;= 256\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Greater than or equal to<\/td>\n<td><code>\u00a0\"{{ min_memory }} &gt;= 512\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Not equal to<\/td>\n<td><code>\u00a0\"{{ min_memory }} != 512\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Variable exists<\/td>\n<td><code>\"{{ min_memory }} is defined\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Variable does not exist<\/td>\n<td><code>\u00a0\"{{ min_memory }} is not defined\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Variable is set to 1, True, or yes<\/td>\n<td><code>\u00a0\"{{ available_memory }}\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Variable is set to 0, False, or no<\/td>\n<td><code>\u00a0\"not {{ available_memory }}\"<\/code><\/td>\n<\/tr>\n<tr>\n<td>Value is present in a variable or an array<\/td>\n<td><code>\"{{ users }} in<\/code><br \/>\n<code>users[\"db_admins\"]\"<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Logical AND and OR operations are supported:<\/p>\n<pre><code>when: ansible_domain == \"example.com\" and ansible_distribution == \"RedHat\"\r\nwhen: ansible_processor_cores == \"1\" or ansible_processor_cores == \"2\"\r\n<\/code><\/pre>\n<p>Loops and conditionals can be combined:<\/p>\n<pre><code>- name: Ensure that packages are present on RedHat\r\n  package:\r\n    name: \"{{ item }}\"\r\n    state: present\r\n  loop:\r\n    - firewalld\r\n    - httpd\r\n  when: ansible_distribution == \"RedHat\"\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Lookups<\/span><\/h2>\n<p>Lookup plugins allow access to outside data sources. Lookups occur on the local computer, not on the remote computer. One way of using lookups is to populate variables:<\/p>\n<pre><code>vars:\r\n  motd_value: \"{{ lookup('file', '\/etc\/motd') }}\"\r\ntasks:\r\n  - debug:\r\n      msg: \"motd value is {{ motd_value }}\"\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Handlers<\/span><\/h2>\n<p>Handlers always run in the order specified by the handlers section of the play. A handler called by a task in the tasks part of the playbook will not run until all of the tasks under tasks have been processed.<\/p>\n<pre><code>notify: restart httpd service\r\n\r\nhandlers:\r\n  - name: restart httpd service\r\n    service:\r\n      name: httpd\r\n      state: restarted\r\n<\/code><\/pre>\n<p>If a task fails and the play aborts on that host, any handlers that had been notified by earlier tasks in the play will not run. Use the following to force execution of the handler:<\/p>\n<pre><code>force_handlers: yes\r\n<\/code><\/pre>\n<p>Handlers are notified when a task reports a &#8220;changed&#8221; result. Handlers are not notified when it reports an &#8220;ok&#8221; or &#8220;failed&#8221; result.<\/p>\n<p>Ignore errors:<\/p>\n<pre><code>ignore_errors: yes\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Commonly Used Files Modules with Examples<\/span><\/h2>\n<p>The file module acts like <code>chcon<\/code> when setting file contexts:<\/p>\n<pre><code>- name: Create a file\r\n  hosts: localhost\r\n  become: true\r\n  tasks:\r\n    - name: Create a file and set permissions\r\n      file:\r\n        path: \/root\/file\r\n        owner: root\r\n        group: root\r\n        mode: 0640\r\n        state: touch\r\n        setype: user_tmp_t\r\n    - name: SELinux type is persistently set to user_tmp_t\r\n      sefcontext:\r\n        target: \/root\/file\r\n        setype: user_tmp_t\r\n        state: present\r\n<\/code><\/pre>\n<pre><code>- name: Copy a file\r\n  hosts: localhost\r\n  become: true\r\n  tasks:\r\n    - name: Copy a file\r\n      copy:\r\n        src: file\r\n        dest: \/root\/file\r\n        force: yes\r\n        owner: root\r\n        group: ansible\r\n        mode: 0640\r\n<\/code><\/pre>\n<pre><code>- name: Add a line to a file\r\n  hosts: localhost\r\n  become: true\r\n  tasks:\r\n    - name: Add a new line to a file\r\n      lineinfile:\r\n        path: \/root\/file\r\n        line: \"this is the new line\"\r\n        state: present\r\n<\/code><\/pre>\n<pre><code>- name: Add block of text to a file\r\n  hosts: localhost\r\n  become: true\r\n  tasks:\r\n    - name: Add a block of text to an existing file\r\n      blockinfile:\r\n        path: \/root\/file\r\n        block: |\r\n          This is the first line to be added.\r\n          This is the second line to be added.\r\n        state: present\r\n<\/code><\/pre>\n<pre><code>- name: Download a file from managed hosts\r\n  hosts: localhost\r\n  become: false\r\n  tasks:\r\n    - name: Fetch a file\r\n      fetch:\r\n        src: \/etc\/hosts\r\n        dest: my-folder\r\n        flat: no\r\n<\/code><\/pre>\n<pre><code>- name: Download a file from remote host\r\n  hosts: localhost\r\n  become: false\r\n    - name: Download file from URL\r\n      get_url:\r\n        url: http:\/\/katello.hl.local\/pub\/index.html\r\n        dest: \/tmp\/index.html\r\n<\/code><\/pre>\n<pre><code>- name: Disable SSH root login\r\n  hosts: localhost\r\n  become: true\r\n  tasks:\r\n  - name: Modify SSH server config\r\n    lineinfile:\r\n      dest: \"\/etc\/ssh\/sshd_config\"\r\n      regexp: \"^PermitRootLogin\"\r\n      line: \"PermitRootLogin no\"\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Jinja2 Templates<\/span><\/h2>\n<p>Similar to Puppet. Puppet templates are based upon Ruby&#8217;s ERB, Ansible templates are based upon Jinja2.<\/p>\n<p>A file containing a Jinja2 template does not need to have any specific file extension.<\/p>\n<p>Use the template module to deploy it:<\/p>\n<pre><code>- name: Use a template file\r\n  hosts: localhost\r\n  gather_facts: yes\r\n  tasks:\r\n    - name: Deploy index.html\r\n      template:\r\n        src: templates\/index.j2\r\n        dest: \/var\/www\/html\/index.html\r\n<\/code><\/pre>\n<pre><code>$ cat templates\/index.j2\r\nThe system kernel is: {{ ansible_kernel }}\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Roles<\/span><\/h2>\n<p>Similar to Puppet modules, reusable code in a modular fashion. Create a role skeleton:<\/p>\n<pre class=\"\">$ mkdir roles &amp;&amp; cd roles\r\n$ ansible-galaxy init my_role\r\n<\/pre>\n<pre class=\"\">$ tree my_role\/\r\nmy_role\/\r\n\u251c\u2500\u2500 defaults\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\r\n\u251c\u2500\u2500 files\r\n\u251c\u2500\u2500 handlers\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\r\n\u251c\u2500\u2500 meta\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\r\n\u251c\u2500\u2500 README.md\r\n\u251c\u2500\u2500 tasks\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\r\n\u251c\u2500\u2500 templates\r\n\u251c\u2500\u2500 tests\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 inventory\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 test.yml\r\n\u2514\u2500\u2500 vars\r\n    \u2514\u2500\u2500 main.yml\r\n<\/pre>\n<p>The original way to use roles is via the<code> roles:<\/code> option for a play:<\/p>\n<pre><code>---\r\n- name name of a task \r\n  hosts: webservers\r\n  roles:\r\n     - webservers<\/code><\/pre>\n<p>Changing a role\u2019s behavior with variables:<\/p>\n<pre class=\"\">---\r\n- <code>name name of a task <\/code> hosts: webservers roles: - role: webservers variable: value<\/pre>\n<p>As of Ansible 2.4, you can use roles inline with any other tasks using <code>import_role<\/code> or <code>include_role<\/code>:<\/p>\n<pre><code>---\r\n- hosts: webservers\r\n  tasks:\r\n  - debug:\r\n      msg: \"before we run our role\"\r\n  - import_role:\r\n      name: example\r\n  - include_role:\r\n      name: example\r\n  - debug:\r\n      msg: \"after we ran our role\"\r\n<\/code><\/pre>\n<p>The order of execution for your playbook is as follows:<\/p>\n<ol>\n<li>Any <code>pre_tasks<\/code> defined in the play.<\/li>\n<li>Any handlers triggered so far will be run.<\/li>\n<li>Each role listed in roles will execute in turn (with dependencies).<\/li>\n<li>Any tasks defined in the play.<\/li>\n<li>Any handlers triggered so far will be run.<\/li>\n<li>Any <code>post_tasks<\/code> defined in the play.<\/li>\n<li>Any handlers triggered so far will be run.<\/li>\n<\/ol>\n<pre class=\"lang:sh decode:true \">---\r\n- hosts: remote.example.com\r\n  \r\n  pre_tasks:\r\n  - debug:\r\n    msg: 'hello'\r\n  \r\n  roles:\r\n  - role1\r\n  - role2\r\n  \r\n  tasks:\r\n  - debug:\r\n    msg: 'still busy'\r\n  \r\n  post_tasks:\r\n  - debug:\r\n    msg: 'goodbye'<\/pre>\n<p>Role dependencies are always executed before the role that includes them, and may be recursive.<\/p>\n<p>Search for roles from the CLI:<\/p>\n<pre><code>$ ansible-galaxy search --author redhat\r\n$ ansible-galaxy search --galaxy-tags tomcat\r\n$ ansible-galaxy search tomcat --platforms EL\r\n<\/code><\/pre>\n<p>Ansible Galaxy is a public library of Ansible roles written by users. Similar to Puppet Forge. Install a role from Galaxy:<\/p>\n<pre><code>$ ansible-galaxy install &lt;author.name&gt; -p ~\/.ansible\/roles\r\n<\/code><\/pre>\n<p>List installed roles:<\/p>\n<pre><code>$ ansible-galaxy list -p ~\/.ansible\/roles\/\r\n- &lt;author.name&gt;, 1.9.5\r\n<\/code><\/pre>\n<p>To define role source, use <code>requirements.yml<\/code>:<\/p>\n<pre><code>- src: http:\/\/katello.hl.local\/pub\/ansible-haproxy.tar.gz\r\n  name: haproxy\r\n- src: http:\/\/katello.hl.local\/pub\/ansible-mysql.tar.gz\r\n  name: mysql\r\n<\/code><\/pre>\n<pre><code>$ ansible-galaxy init -r requirements.yml\r\n<\/code><\/pre>\n<p>&nbsp;<\/p>\n<h3><span style=\"color: #3366ff;\">RHEL System Roles<\/span><\/h3>\n<pre><code>$ sudo yum install rhel-system-roles\r\n$ ls \/usr\/share\/doc\/rhel-system-roles-1.0\/\r\nkdump\r\nnetwork\r\npostfix\r\nselinux\r\ntimesync\r\n<\/code><\/pre>\n<pre><code>$ ansible-galaxy list\r\n- rhel-system-roles.selinux, (unknown version)\r\n- linux-system-roles.network, (unknown version)\r\n- rhel-system-roles.postfix, (unknown version)\r\n- rhel-system-roles.timesync, (unknown version)\r\n- rhel-system-roles.kdump, (unknown version)\r\n- linux-system-roles.timesync, (unknown version)\r\n- linux-system-roles.kdump, (unknown version)\r\n- linux-system-roles.postfix, (unknown version)\r\n- rhel-system-roles.network, (unknown version)\r\n- linux-system-roles.selinux, (unknown version)\r\n<\/code><\/pre>\n<h2><span style=\"color: #3366ff;\">Password Hashing<\/span><\/h2>\n<p>How to generate SHA512 crypted passwords for the user module? The answer is taken from Ansible FAQ.<\/p>\n<pre><code>- name: Create user with password\r\n    user:\r\n      name: sandy\r\n      password: \"{{ user_pw | password_hash('sha512') }}\"\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">meta &#8211; Execute Ansible Actions<\/span><\/h2>\n<p>Meta tasks are a special kind of task which can influence Ansible internal execution or state. Choices:<\/p>\n<pre><code>* flush_handlers\r\n* refresh_inventory\r\n* noop\r\n* clear_facts\r\n* clear_host_errors\r\n* end_play\r\n* reset_connection\r\n<\/code><\/pre>\n<p>Example meta task for how to run handlers after an error occurred:<\/p>\n<pre><code> tasks:\r\n   - name: Attempt and graceful roll back demo\r\n     block:\r\n       - debug:\r\n           msg: 'I execute normally'\r\n         notify: run me even after an error\r\n       - command: \/bin\/false\r\n     rescue:\r\n       - name: make sure all handlers run\r\n         meta: flush_handlers\r\n handlers:\r\n    - name: run me even after an error\r\n      debug:\r\n        msg: 'This handler runs even on error'\r\n<\/code><\/pre>\n<p>Note that meta is not really a module as such it cannot be overwritten.<\/p>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Check Mode (Dry Run)<\/span><\/h2>\n<p>When <code>ansible-playbook<\/code> is executed with <code>--check<\/code> or <code>-C<\/code> it will not make any changes on remote systems. To modify the check mode behavior of individual tasks, you can use the check_mode option:<\/p>\n<pre><code>- name: this task will run under checkmode and not change the system\r\n  lineinfile:\r\n    line: \"PermitRootLogin no\"\r\n    dest: \/etc\/ssh\/sshd_config\r\n    state: present\r\n  check_mode: yes\r\n<\/code><\/pre>\n<p>The above will force a task to run in check mode, even when the playbook is called without <code>--check<\/code>.<\/p>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Playbook Debugger<\/span><\/h2>\n<p>Ansible includes a debugger as part of the strategy plugins. This debugger enables you to debug as task.<\/p>\n<p>The debugger keyword can be used on any block where you provide a name attribute, such as a play, role, block or task.<\/p>\n<pre><code>---\r\n- hosts: ansible2.hl.local\r\n  tasks:\r\n    - unarchive:\r\n        src: files\/archive.tgz\r\n        dest: \/tmp\/\r\n        remote_src: no\r\n    - name: archive file\r\n      debugger: on_failed\r\n      archive:\r\n        path: \/tmp\/archive.html\r\n        dest: \/tmp\/file.gz\r\n        format: tgz\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Ignoring Failed Commands<\/span><\/h2>\n<p>Playbooks will stop executing any more steps on a host that has a task fail. To ignore this behaviour, set <code>ignore_errors<\/code> to <code>true<\/code>:<\/p>\n<pre><code>- name: this will not be counted as a failure\r\n  command: \/bin\/false\r\n  ignore_errors: yes\r\n<\/code><\/pre>\n<p>This is useful when you expect a task to fail. For example, you are checking if Apache website is reachable. It may be unreachable, but you don&#8217;t want the play to fail because of that.<\/p>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Run Once<\/span><\/h2>\n<p>There may be a need to only run a task one time for a batch of hosts. This can be achieved by configuring <code>run_once<\/code> on a task:<\/p>\n<pre><code>- command: \/tmp\/upgrade_database.sh\r\n  run_once: true\r\n<\/code><\/pre>\n<h2><span style=\"color: #3366ff;\">Ansible Aborting the Play<\/span><\/h2>\n<p>There will be cases when you will need to abort the entire play on failure, not just skip remaining tasks for a host, to avoid breaking the system. The <code>any_errors_fatal<\/code> play option will mark all hosts as failed if any fails, causing an immediate abort:<\/p>\n<pre><code>- hosts: all\r\n  any_errors_fatal: true\r\n  roles:\r\n    - database\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Keywords<\/span><\/h2>\n<p>These are some of the keywords available on common playbook objects.<\/p>\n<p>Notable play keywords:<\/p>\n<pre><code>any_errors_fatal\r\nbecome\r\nbecome_method\r\nbecome_user\r\ncheck_mode\r\ndebugger\r\nforce_handlers\r\ngather_facts\r\nhandlers\r\nhosts\r\nignore_errors\r\nname\r\nno_log\r\norder\r\nport\r\npost_tasks\r\npre_tasks\r\nremote_user\r\nroles\r\nrun_once\r\nserial\r\ntasks\r\nvars\r\nvars_files\r\n<\/code><\/pre>\n<p>Notable block keywords:<\/p>\n<pre><code>always\r\nblock\r\nrescue\r\nwhen\r\n<\/code><\/pre>\n<p>Notable task keywords:<\/p>\n<pre><code>loop\r\nregister\r\n<\/code><\/pre>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Setting Defaults for Modules<\/span><\/h2>\n<p>It can be useful to define default arguments for a particular module using the <code>module_defaults<\/code> attribute:<\/p>\n<pre><code>- hosts: all\r\n  become: true\r\n  module_defaults:\r\n    file:\r\n      owner: root\r\n      group: devops\r\n      mode: \"664\"\r\n      state: touch\r\n  tasks:\r\n    - file:\r\n        path: \/tmp\/file1\r\n    - file:\r\n        path: \/tmp\/file2\r\n    - file:\r\n        path: \/tmp\/file3\r\n<\/code><\/pre>\n<p>It is a time saver when you need to use the same module repeatedly.<\/p>\n<h2><\/h2>\n<h2><span style=\"color: #3366ff;\">Ansible Vault<\/span><\/h2>\n<pre><code>$ ansible-vault [create|decrypt|edit|encrypt|rekey|view] vaultfile.yml\r\n<\/code><\/pre>\n<p>By default, Ansible uses functions from the <code>python-crypto<\/code> package to encrypt and decrypt vault files. To speed up decryption at startup, you install the <code>python-cryptography<\/code> package.<\/p>\n<pre><code>$ ansible-vault create vaultfile.yml\r\n$ ansible-vault view vaultfile.yml\r\n<\/code><\/pre>\n<p>Check syntax of a playbook that uses the vault file:<\/p>\n<pre><code>$ ansible-playbook --syntax-check --ask-vault-pass playbook.yml\r\n<\/code><\/pre>\n<p>Create a password file to use for the playbook execution:<\/p>\n<pre class=\"\">$ echo \"ansible\" &gt; key\r\n$ ansible-playbook --syntax-check --vault-password-file=key playbook.yml<\/pre>\n<div class=\"application-main \" data-commit-hovercards-enabled=\"\">\n<div class=\"\">\n<p>&nbsp;<\/p>\n<div class=\"container-lg clearfix new-discussion-timeline p-responsive\">\n<div class=\"repository-content \">\n<div class=\"Box mt-3 position-relative \">\n<div id=\"readme\" class=\"Box-body readme blob js-code-block-container p-5 p-xl-6\">\n<article class=\"markdown-body entry-content container-lg\">\n<h2><span style=\"color: #3366ff;\">Ansible Best Practices<\/span><\/h2>\n<p>Tips for making the most of Ansible and Ansible playbooks.<\/p>\n<ol>\n<li>Always mention the state. The <code>state<\/code> parameter is optional to a lot of modules. Whether <code>state=present<\/code> or <code>state=absent<\/code>, it is always best to leave that parameter in your playbooks to make it clear.<\/li>\n<li>Generous use of whitespace to break things up, and use of comments, which start with <code>#<\/code>, is encouraged.<\/li>\n<li>Always name tasks. It is recommended to provide a description about why something is being done!<\/li>\n<li>Keep it simple. Do not attemtp to use every feature of Ansible together, all at once. Use what works for you!<\/li>\n<li>Ansible best practice has no limit on the amount of variable and vault files or their names.<\/li>\n<\/ol>\n<\/article>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h2><span style=\"color: #3366ff;\">Ansible and Vim<\/span><\/h2>\n<p>When writing playbooks in vim editor, modify its action in response to Tab key entries. Add the following line to <code>$HOME\/.vimrc<\/code>. It will perform a two space indentation when the Tab key is pressed.<\/p>\n<pre><code>autocmd FileType yaml setlocal ai ts=2 sw=2 et \r\n<\/code><\/pre>\n<p>Settings explained:<\/p>\n<pre><code>ai   - autoindent (turns it on)\r\nsw=2 - shiftwidth (indenting is 2 spaces)\r\net   - expandtab (do not use actual tab character)\r\nts=2 - tabstop (tabs are at proper location)\r\n<\/code><\/pre>\n<p>Two other helpful but optional vim settings:<\/p>\n<pre><code>nu (show line number)\r\ncursorline (line to show current cursor position)\r\n<\/code><\/pre>\n<h2><\/h2>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp;<\/p>\n","protected":false},"author":1,"featured_media":3730,"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\/3701"}],"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=3701"}],"version-history":[{"count":64,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3701\/revisions"}],"predecessor-version":[{"id":3806,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3701\/revisions\/3806"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media\/3730"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=3701"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=3701"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=3701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}