{"id":3303,"date":"2020-02-08T18:46:01","date_gmt":"2020-02-08T17:46:01","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=3303"},"modified":"2021-05-25T16:20:31","modified_gmt":"2021-05-25T14:20:31","slug":"ansible-roles","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2020\/02\/08\/ansible-roles\/","title":{"rendered":"Ansible Roles"},"content":{"rendered":"<p>When dealing with extensive playbooks, it is easier to split the tasks into roles. This also helps in reusing the roles in the future. Roles are a collection of tasks, which can be moved from one playbook to another, can be run independently but only through a playbook file. In other words, roles are a feature of Ansible that facilitate reuse and further promote modularization of configuration.<\/p>\n<p><!--more--><\/p>\n<p>The role is the primary mechanism for breaking a playbook into multiple files. Therefore, this simplifies writing\u00a0<em>complex playbooks<\/em>, and it makes them easier to reuse. The breaking of playbook allows you to logically break the playbook into reusable components.<\/p>\n<p>The <code>ansible-galaxy init<\/code> command creates a directory structure for a new role that will be developed. The author and name of the role is specified as an argument to the command and it creates the directory structure in the current directory. <code>ansible-galaxy<\/code> interacts with the Ansible Galaxy website API when it performs most operations. The <code>--offline<\/code> option permits the init command to be used when Internet access is unavailable.\u00a0 To create a role using the <em>ansible-galaxy <\/em>command, we can simply use the below syntax in our terminal:<\/p>\n<pre class=\"lang:sh decode:true\"># ansible-galaxy init &lt;ROLE_NAME&gt;<\/pre>\n<p>or<\/p>\n<pre class=\"lang:sh decode:true \"># ansible-galaxy init --offline &lt;ROLE_NAME&gt;<\/pre>\n<p>Let&#8217;s create a role <code>apache<\/code> in the control node:<\/p>\n<pre class=\"lang:sh decode:true\">[root@controlnode]# cd \/etc\/ansible\/roles\r\n[root@controlnode roles]# ansible-galaxy init apache<\/pre>\n<p>Running this command will give us a basic directory structure in our current working directory and this directory structure will be like the one below:<\/p>\n<pre class=\"lang:sh decode:true\">[root@controlnode apache]# tree\r\n.\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<\/pre>\n<ul>\n<li><strong><code> defaults<\/code><\/strong>: contains default variables for the role. Variables in default have the lowest priority so they are easy to override.<\/li>\n<li><strong><code>files<\/code><\/strong>: contains files which we want to be copied to the remote host. We don\u2019t need to specify a path of resources stored in this directory.<\/li>\n<li><strong><code>handlers<\/code><\/strong>: contains handlers which can be invoked by \u201cnotify\u201d directives and are associated with service.<\/li>\n<li><strong><code>meta<\/code><\/strong>: contains metadata of role like an author, support platforms, dependencies. Defines certain meta data for the role. Relevant meta data includes role dependencies and various role level configurations such as <code>allow duplicates<\/code>. The meta-directory is entered via a main.ymi<\/li>\n<li><strong><code>tasks<\/code><\/strong>: contains the main list of steps to be executed by the role.<strong><code><\/code><\/strong><\/li>\n<li><strong><code>templates<\/code><\/strong>: contains file template which supports modifications from the role. We use the Jinja2 templating language for creating templates.<\/li>\n<li><strong><code>tests<\/code><\/strong>: This directory can contain an inventory and<code> test.yml<\/code> playbook that can be used to test the role.<\/li>\n<li><strong><code>vars<\/code><\/strong>: contains variables for the role. Variables in vars have higher priority than variables in defaults directory.<\/li>\n<\/ul>\n<p>Let&#8217;s create tasks for apache role:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat \/etc\/ansible\/roles\/apache\/tasks\/main.yml\r\n---\r\n# tasks file for apache\r\n\r\n- name: install apache\r\n  yum: name=httpd state=latest\r\n\r\n- name: copy httpd.conf template\r\n  template:\r\n    src: httpd.conf.j2\r\n    dest: \/etc\/httpd\/conf\/httpd.conf\r\n  notify: restart httpd\r\n\r\n- name: enable and start service\r\n  service:\r\n    name: httpd\r\n    enabled: yes\r\n    state: started<\/pre>\n<p>Lets&#8217;s copy <code>httpd.conf<\/code> file from apache and save as template<code> httpd.conf.j2<\/code>:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode apache]$ cat \/etc\/ansible\/roles\/apache\/templates\/httpd.conf.j2\r\n...\r\n# Example:\r\n# LoadModule foo_module modules\/mod_foo.so\r\n#\r\nInclude conf.modules.d\/*.conf\r\n\r\n#\r\n# If you wish httpd to run as a different user or group, you must run\r\n# httpd as root initially and it will switch.\r\n#\r\n# User\/Group: The name (or #number) of the user\/group to run httpd as.\r\n# It is usually good practice to create a dedicated user and group for\r\n# running httpd, as with most system services.\r\n#\r\nUser apache\r\nGroup apache\r\n\r\n# 'Main' server configuration\r\n#\r\n# The directives in this section set up the values used by the 'main'\r\n# server, which responds to any requests that aren't handled by a\r\n# &lt;VirtualHost&gt; definition. These values also provide defaults for\r\n# any &lt;VirtualHost&gt; containers you may define later in the file.\r\n#\r\n# All of these directives may appear inside &lt;VirtualHost&gt; containers,\r\n# in which case these default settings will be overridden for the\r\n# virtual host being defined.\r\n#\r\n\r\n#\r\n# ServerAdmin: Your address, where problems with the server should be\r\n# e-mailed. This address appears on some server-generated pages, such\r\n# as error documents. e.g. admin@your-domain.com\r\n#\r\nServerAdmin {{ apache_server_admin }}\r\n\r\n#\r\n# ServerName gives the name and port that the server uses to identify itself.\r\n# This can often be determined automatically, but we recommend you specify\r\n# it explicitly to prevent problems during startup.\r\n#\r\n# If your host doesn't have a registered DNS name, enter its IP address here.\r\n#\r\n#ServerName www.example.com:80\r\n...<\/pre>\n<p>As we see the template has variable:<\/p>\n<p><code>ServerAdmin {{ apache_server_admin }}<\/code><\/p>\n<p>Now we can create a file with default variables:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode apache]$ cat \/etc\/ansible\/roles\/apache\/defaults\/main.yml\r\n---\r\n# defaults file for apache\r\n\r\napache_server_admin: admin@example.com<\/pre>\n<p>And we can create a handler:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode apache]$ cat \/etc\/ansible\/roles\/apache\/handlers\/main.yml\r\n---\r\n# handlers file for apache\r\n- name: restart apache service\r\n  service: name=httpd state=restarted\r\n  listen: \"restart httpd\"<\/pre>\n<p>&nbsp;<\/p>\n<p>Now we can create a playbook which use the apache role:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode apache]$ cd ..\r\n[miro@controlnode roles]$ cat install.yml\r\n---\r\n- hosts: managedhost1\r\nbecome: yes\r\nroles:\r\n- apache<\/pre>\n<p>Let&#8217;s run the install.yml playbook:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ ansible-playbook install.yml\r\n\r\nPLAY [managedhost1] ****************************************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] *************************************************************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : install apache] *****************************************************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : copy httpd.conf template] *******************************************************************************************************************************************\r\nchanged: [managedhost1]\r\n\r\nTASK [apache : enable and start service] *******************************************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nRUNNING HANDLER [apache : restart apache service] **********************************************************************************************************************************\r\nchanged: [managedhost1]\r\n\r\nPLAY RECAP *************************************************************************************************************************************************************************\r\nmanagedhost1 : ok=5 changed=2 unreachable=0 failed=0<\/pre>\n<p>As we see apache service has been installed and started on managedhost1 with http.conf.j2 template:<\/p>\n<pre class=\"lang:sh decode:true \">[root@managedhost1 conf]# grep ServerAdmin \/etc\/httpd\/conf\/httpd.conf\r\n# ServerAdmin: Your address, where problems with the server should be\r\nServerAdmin admin@example.com<\/pre>\n<p>The varriables in defaults dir can be overwritten in other files:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat install.yml\r\n---\r\n- hosts: managedhost1\r\n  become: yes\r\n  roles:\r\n    - apache\r\n  vars:\r\n    apache_server_admin: miro@example.com<\/pre>\n<p>After we run install.yml playbook ffor the second time:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode roles]$ ansible-playbook install.yml\r\n\r\nPLAY [managedhost1] ****************************************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] *************************************************************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : install apache] *****************************************************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : copy httpd.conf template] *******************************************************************************************************************************************\r\nchanged: [managedhost1]\r\n\r\nTASK [apache : enable and start service] *******************************************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nRUNNING HANDLER [apache : restart apache service] **********************************************************************************************************************************\r\nchanged: [managedhost1]\r\n\r\nPLAY RECAP *************************************************************************************************************************************************************************\r\nmanagedhost1 : ok=5 changed=2 unreachable=0 failed=0<\/pre>\n<p>We can see that <code>ServerAdmin<\/code> parameter has changed:<\/p>\n<pre class=\"lang:sh decode:true \">[root@managedhost1 conf]# grep ServerAdmin \/etc\/httpd\/conf\/httpd.conf\r\n# ServerAdmin: Your address, where problems with the server should be\r\nServerAdmin miro@example.com<\/pre>\n<p>It is also possible to use the never syntax:<\/p>\n<pre class=\"lang:sh decode:true\">[root@controlnode roles]# cat install.yml\r\n---\r\n- hosts: managedhost1\r\n  become: yes\r\n  tasks:\r\n  - include_role:\r\n    name: apache<\/pre>\n<p>With this syntax we can use conditionals:<\/p>\n<pre class=\"lang:sh decode:true\">---\r\n- hosts: managedhost1\r\n  become: yes\r\n  tasks:\r\n  - include_role:\r\n      name: apache\r\n    when: \"ansible_facts['os_family'] == 'RedHat'\"<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Dependencies<\/span><\/p>\n<p>Roles may include other roles using the dependencies keyword. Dependent roles are applied prior to the role dependent on them. A role using the same parameters will not be applied more than one time. This can cause complication with role dependencies. Having <code>'allow_duplicates: true'<\/code> defined in <code>meta\/main.yml<\/code> within a role will allow the role to be applied more than once.<\/p>\n<p>Let&#8217;s create<code> php-webserver<\/code> role:<\/p>\n<pre class=\"lang:sh decode:true \">[root@controlnode ~]# cd \/etc\/ansible\/roles\r\n[root@controlnode roles]# ansible-galaxy init php-webserver<\/pre>\n<p>We create a tasks:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode roles]$ cat \/etc\/ansible\/roles\/php-webserver\/tasks\/main.yml\r\n---\r\n# tasks file for php-webserver\r\n\r\n- name: install php\r\n  yum: name={{ item }} state=latest\r\n  with_items:\r\n    - php\r\n    - php-gd\r\n    - php-pear\r\n    - php-mysql\r\n  notify: restart httpd<\/pre>\n<p>We modify <code>depedencies<\/code> section in meta file:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat \/etc\/ansible\/roles\/php-webserver\/meta\/main.yml\r\ndependencies:\r\n# List your role dependencies here, one per line. Be sure to remove the '[]' above,\r\n# if you add dependencies to this list.\r\n- role: apache<\/pre>\n<p>And write a playbook <code>install2<\/code> to use <code>php-webserver<\/code> role:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat install2.yml\r\n---\r\n- hosts: managedhost1\r\n  become: yes\r\n  roles:\r\n  - php-webserver<\/pre>\n<p>Let&#8217;s run install2 playbook:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ ansible-playbook install2.yml\r\n\r\nPLAY [managedhost1] *********************************************************************************************************\r\n\r\nTASK [Gathering Facts] ******************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : install apache] **********************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : copy httpd.conf template] ************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [apache : enable and start service] ************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [php-webserver : install php] ******************************************************************************************\r\nchanged: [managedhost1] =&gt; (item=[u'php', u'php-gd', u'php-pear', u'php-mysql'])\r\n\r\nRUNNING HANDLER [apache : restart apache service] ***************************************************************************\r\nchanged: [managedhost1]\r\n\r\nPLAY RECAP ******************************************************************************************************************\r\nmanagedhost1 : ok=6 changed=2 unreachable=0 failed=0<\/pre>\n<p>As we see role <code>php-webserver<\/code> has used depedency role <code>apache<\/code>.<\/p>\n<p><span style=\"color: #3366ff;\"> Controlling order of execution<\/span><br \/>\nNormally, the tasks of roles execute before the tasks of the playbooks that use them. Ansible provides a way of overriding this default behavior: the pre_tasks and post_tasks tasks. The pre_tasks tasks are performed before any roles are applied. The post_tasks tasks are performed after all the roles have completed.<\/p>\n<pre class=\"lang:sh decode:true \">---\r\n- hosts: remote.example.com\r\n  pre_tasks:\r\n    - debug:\r\n        msg: 'hello'\r\n  roles:\r\n    - role1\r\n    - role2\r\n  tasks:\r\n    - debug:\r\n        msg: 'still busy'\r\n  post_tasks:\r\n    - debug:\r\n        msg: 'goodbye'<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Example 1.<br \/>\n<\/span><\/p>\n<p>1. Creating the role directory structure<br \/>\nAnsible looks for roles in a subdirectory called roles in the project directory. Roles can also be kept in the directories referenced by the roles_path variable in Ansible configuration files. This variable contains a colon-separated list of directories to search. Each role has its own directory with specially named subdirectories. The following directory structure contains the files that define the motd role.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ~]$ cd \/home\/miro\/ansible\r\n[miro@controlnode ansible]$ mkdir roles\r\n[miro@controlnode ansible]$ cd roles\r\n[miro@controlnode roles]$ ansible-galaxy init motd\r\n- motd was created successfully\r\n\r\n[miro@controlnode ansible]$ tree roles\r\nroles\r\n\u2514\u2500\u2500 motd\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\r\n9 directories, 8 files<\/pre>\n<p>&nbsp;<\/p>\n<p>2. Defining the role content<br \/>\nAfter the directory structure is created, the content of the Ansible role must be defined. A good place to start would be the ROLENAME\/tasks\/main.yml file. This file defines which modules to call on the managed hosts that this role is applied. The following tasks\/main.yml file manages the \/etc\/motd file on managed hosts. It uses the template module to copy the template named motd.j2 to the managed host. The template is retrieved from the templates subdirectory of the role.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat motd\/tasks\/main.yml\r\n---\r\n# tasks file for motd\r\n- name: deliver motd file\r\n  template:\r\n    src: templates\/motd.j2\r\n    dest: \/etc\/motd\r\n    owner: root\r\n    group: root\r\n    mode: 0444<\/pre>\n<p>The following command displays the contents of the templates\/motd.j2 template of the motd role. It references Ansible facts and a system-owner variable.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat motd\/templates\/motd.j2\r\nThis is the system {{ ansible_hostname }}.\r\nToday's date is: {{ ansible_date_time.date }}.\r\nOnly use this system with permission.\r\nYou can ask {{ system_owner }} for access.<\/pre>\n<p>The role can define a default value for the system_owner variable. The defaults\/main.yml file in the role&#8217;s directory structure is where these values are set. The following defaults\/main.yml file sets the system_owner variable to<br \/>\nuser@host.example.com. This will be the email address that is written in the \/etc\/motd file of managed hosts that this role is applied to.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat motd\/defaults\/main.yml\r\n---\r\n# defaults file for motd\r\nsystem_owner: user@host.example.com<\/pre>\n<p>&nbsp;<\/p>\n<p>3. Using the role in a playbook<br \/>\nTo access a role, reference it in the roles: section of a playbook. The following playbook refers to the motd role. Because no variables are specified, the role will be applied with its default variable values.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cd ..\r\n[miro@controlnode ansible]$ cat use-motd-role.yml\r\n---\r\n- name: use motd role playbook\r\n  hosts: managedhost1\r\n  become: yes\r\n  roles:\r\n    - motd<\/pre>\n<p>When the playbook is executed, tasks performed because of a role can be identified by the role name prefix. The following sample output illustrates this with the motd: deliver motd file message.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook -i inventory use-motd-role.yml\r\n\r\nPLAY [use motd role playbook] *****************************************************************************************\r\n\r\nTASK [Gathering Facts] ************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [motd : deliver motd file] ***************************************************************************************\r\nchanged: [managedhost1]\r\n\r\nPLAY RECAP ************************************************************************************************************\r\nmanagedhost1 : ok=2 changed=1 unreachable=0 failed=0<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -a 'cat \/etc\/motd'\r\nmanagedhost1 | SUCCESS | rc=0 &gt;&gt;\r\nThis is the system managedhost1.\r\nToday's date is: 2020-02-08.\r\nOnly use this system with permission.\r\nYou can ask user@host.example.com for access.<\/pre>\n<p>&nbsp;<\/p>\n<p>4. Changing a role&#8217;s behavior with variables<br \/>\nVariables can be used with roles, like parameters, to override previously defined, default values. When they are referenced, the variable: value pairs must be specified as well. The following example shows how to use the motd role with a different value for the system_owner role variable. The value specified, someone@host.example.com, will replace the variable reference when the role is applied to a managed host.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat use-motd-role2.yml\r\n---\r\n- name: use motd role playbook\r\n  hosts: managedhost1\r\n  become: yes\r\n  roles:\r\n    - role: motd\r\n      system_owner: someone@host.example.com<\/pre>\n<p>Lets run the playbook <code>use-motd-role2.yml<\/code>:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible-playbook -i inventory use-motd-role2.yml\r\n\r\nPLAY [use motd role playbook] *********************************************************************************************************\r\n\r\nTASK [Gathering Facts] ****************************************************************************************************************\r\nok: [managedhost1]\r\n\r\nTASK [motd : deliver motd file] *******************************************************************************************************\r\nchanged: [managedhost1]\r\n\r\nPLAY RECAP ****************************************************************************************************************************\r\nmanagedhost1 : ok=2 changed=1 unreachable=0 failed=0<\/pre>\n<p>And check if the motd file changed:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost1 -i inventory -a 'cat \/etc\/motd'\r\nmanagedhost1 | SUCCESS | rc=0 &gt;&gt;\r\nThis is the system managedhost1.\r\nToday's date is: 2020-02-08.\r\nOnly use this system with permission.\r\nYou can ask someone@host.example.com for access.<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Example 2.<\/span><\/p>\n<p>In this exercise, you will create two roles that use variables and parameters: <em>myvhost<\/em> and <em>myfirewall<\/em>. The myvhost role will install and configure the Apache service on a host. A template is provided that will be used for <code>\/etc\/httpd\/conf.d\/vhost.conf: vhost.conf.j2<\/code>. The myfirewall role installs, enables, and starts the firewalld daemon. It opens the firewall service port specified by the firewall_service variable.<\/p>\n<p>Create the directory structure for a role called <em>myvhost<\/em>.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ cd \/home\/miro\/ansible\/roles\r\n[miro@controlnode roles]$ ansible-galaxy init myvhost\r\n- myvhost was created successfully\r\n[miro@controlnode roles]$ tree myvhost\r\nmyvhost\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\r\n8 directories, 8 files<\/pre>\n<p>Create the <code>main.yml<\/code> file in the tasks subdirectory of the role. The role should perform four tasks:<br \/>\n\u2022 Install the <em>httpd<\/em> package.<br \/>\n\u2022 Start and enable the <em>httpd<\/em> service.<br \/>\n\u2022 Download the HTML content into the virtual host DocumentRoot directory.<br \/>\n\u2022 Install the template configuration file that configures the webserver.<\/p>\n<p>Use a text editor to create a file called <code>roles\/myvhost\/tasks\/main.yml<\/code>. Include<br \/>\ncode to use the <em>yum<\/em> module to install the <em>httpd<\/em> package. Add additional code to use the service module to start and enable the <em>httpd<\/em> service. Add another stanza to copy the HTML content from the role to the virtual host DocumentRoot directory. Use the copy module and include a trailing slash after the source directory name. This will cause the module to copy the contents of the html directory immediately below the destination directory (similar to <em>rsync<\/em> usage). The <code>ansible_hostname<\/code> variable will expand to the short host name of the managed host. Add another stanza to use the template module to create <code>\/etc\/httpd\/conf.d\/vhost.conf<\/code> on the managed host. It should call a handler to restart the <em>httpd<\/em> daemon when this file is updated. The file contents should look<br \/>\nlike the following:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cd roles\/myvhost\r\n[miro@controlnode myvhost]$ vi tasks\/main.yml\r\n[miro@controlnode myvhost]$ cat tasks\/main.yml\r\n---\r\n# tasks file for myvhost\r\n- name: install httpd\r\n  yum:\r\n    name: httpd\r\n    state: latest\r\n\r\n- name: start and enable httpd service\r\n  service:\r\n    name: httpd\r\n    state: started\r\n    enabled: true\r\n\r\n- name: deliver html content\r\n  copy:\r\n    src: html\/\r\n    dest: \"\/var\/www\/vhosts\/{{ ansible_hostname }}\"\r\n\r\n- name: template vhost file\r\n  template:\r\n    src: vhost.conf.j2\r\n    dest: \/etc\/httpd\/conf.d\/vhost.conf\r\n    owner: root\r\n    group: root\r\n    mode: 0644\r\n    notify:\r\n      - restart httpd<\/pre>\n<p>Create the handler for restarting the httpd service. Use a text editor to create a file called <code>roles\/myvhost\/handlers\/main.yml<\/code>. Include code to use the service module. The file contents should look like the following:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode myvhost]$ cat handlers\/main.yml\r\n---\r\n# handlers file for myvhost\r\n  - name: restart httpd\r\n    service:\r\n      name: httpd\r\n      state: restarted<\/pre>\n<p>Create the HTML content that will be served by the webserver. The role task that called the copy module referred to an html directory as the src.<br \/>\nCreate this directory below the files subdirectory of the role. Create an index.html file below that directory with the contents: \u201csimple index\u201d. Be<br \/>\nsure to use this string verbatim because the grading script looks for it.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode myvhost]$ mkdir -p files\/html\r\n[miro@controlnode myvhost]$ echo 'simple index' &gt; files\/html\/index.html\r\n[miro@controlnode myvhost]$ cat templates\/vhost.conf.j2\r\n\r\n# modified on {{ansible_date_time.date}} {{ansible_date_time.time}} by student on {{ansible_fqdn}}\r\n&lt;VirtualHost *:80&gt;\r\n   ServerAdmin webmaster@{{ansible_fqdn}}\r\n   ServerName {{ansible_fqdn}}\r\n   ErrorLog logs\/{{ ansible_hostname }}-error.log\r\n   CustomLog logs\/{{ ansible_hostname }}-common.log common\r\n   DocumentRoot \/var\/www\/vhosts\/{{ ansible_hostname }}\/\r\n   &lt;Directory \/var\/www\/vhosts\/{{ ansible_hostname }}\/&gt;\r\n      Options +Indexes +FollowSymlinks +Includes\r\n      Order allow,deny\r\n      Allow from all\r\n   &lt;\/Directory&gt;\r\n&lt;\/VirtualHost&gt;<\/pre>\n<p>Write a playbook that uses the role, called <code>use-vhost-role.yml<\/code>. It should have the following content:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode myvhost]$ cd ..\/..\r\n[miro@controlnode ansible]$ cat use-vhost-rule.yml\r\n---\r\n- name: use vhost role playbook\r\n  become: yes\r\n  hosts: webservers\r\n\r\n  pre_tasks:\r\n  - debug:\r\n      msg: 'Beginning web server configuration.'\r\n\r\n  roles:\r\n  - myvhost\r\n\r\n  post_tasks:\r\n  - debug:\r\n      msg: 'Web server has been configured.'<\/pre>\n<p>Run the playbook. Review the output to confirm that Ansible performed the actions on the web server,<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook use-vhost-role.yml\r\n\r\nPLAY [use vhost role playbook] ***************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] ***********************************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [debug] *********************************************************************************************************************************************************\r\nok: [managedhost2] =&gt; {\r\n\"msg\": \"Beginning web server configuration.\"\r\n}\r\n\r\nTASK [myvhost : install httpd] ***************************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : start and enable httpd service] **********************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : deliver html content] ********************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : template vhost file] *********************************************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nRUNNING HANDLER [myvhost : restart httpd] ****************************************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nTASK [debug] *********************************************************************************************************************************************************\r\nok: [managedhost2] =&gt; {\r\n\"msg\": \"Web server has been configured.\"\r\n}\r\n\r\nPLAY RECAP ***********************************************************************************************************************************************************\r\nmanagedhost2 : ok=8 changed=2 unreachable=0 failed=0<\/pre>\n<p>Run ad hoc commands to confirm that the role worked. The httpd package should be installed and the httpd service should be running.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost2 -a 'yum list installed httpd'\r\n[WARNING]: Consider using yum module rather than running yum\r\n\r\nmanagedhost2 | SUCCESS | rc=0 &gt;&gt;\r\nWczytane wtyczki: fastestmirror\r\nZainstalowane pakiety\r\nhttpd.x86_64 2.4.6-93.el7.centos @base<\/pre>\n<p>Run ad hoc commands to confirm that the role worked. The httpd package should be installed and the httpd service should be running.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible managedhost2 -a 'systemctl is-active httpd'\r\nmanagedhost2 | SUCCESS | rc=0 &gt;&gt;\r\nactive\r\n\r\n[miro@controlnode ansible]$ ansible managedhost2 -a 'systemctl is-enabled http'\r\nmanagedhost2 | FAILED | rc=1 &gt;&gt;\r\nFailed to get unit file state for http.service: No such file or directorynon-zero return code\r\n\r\n[miro@controlnode ansible]$ ansible managedhost2 -a 'systemctl is-enabled httpd'\r\nmanagedhost2 | SUCCESS | rc=0 &gt;&gt;\r\nenabled<\/pre>\n<p>Use a web browser on managedhost2 to check if the web content is available locally. It should succeed and display content.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ ansible managedhost2 -a 'curl -s http:\/\/localhost'\r\n[WARNING]: Consider using get_url or uri module rather than running curl\r\n\r\nmanagedhost2 | SUCCESS | rc=0 &gt;&gt;\r\nsimple index<\/pre>\n<p>Check if you can connect from controlnode to the webserver on <em>managedhost2.example.com<\/em>:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ curl managedhost2.example.com\r\ncurl: (7) Failed connect to managedhost2.example.com:80; No route to host<\/pre>\n<p>The issue is due to firewall on <em>managedhost2<\/em> don&#8217;t allow for <em>http<\/em> connection.\u00a0 So we need to configure firewall on <em>managedhost2<\/em>.<\/p>\n<p>Create the role directory structure for a role called <em>myfirewall<\/em>:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cd roles\r\n[miro@controlnode roles]$ ansible-galaxy init myfirewall\r\n- myfirewall was created successfully\r\n[miro@controlnode roles]$ tree myfirewall\r\nmyfirewall\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\r\n8 directories, 8 files<\/pre>\n<p>Create the <code>main.yml<\/code> file in the tasks subdirectory of the role. The role should perform three tasks:<br \/>\n\u2022 Install the <em>firewalld<\/em> package.<br \/>\n\u2022 Start and enable the <em>firewalld<\/em> service.<br \/>\n\u2022 Open a <em>firewall<\/em> service port.<\/p>\n<p>Include code to use the <em>yum<\/em> module to install the <em>firewalld<\/em> package.\u00a0 Add additional code to use the service module to start and enable the firewalld service. Add another stanza to use the firewalld module to immediately, and persistently, open the service port specified by the <em>firewall_service<\/em> variable. The file contents should look like the following:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat myfirewall\/tasks\/main.yml\r\n---\r\n# tasks file for myfirewall\r\n- name: install firewalld\r\n  yum:\r\n    name: firewalld\r\n    state: latest\r\n- name: start and enable firewalld service\r\n  service:\r\n    name: firewalld\r\n    state: started\r\n    enabled: true\r\n- name: firewall services config\r\n  firewalld:\r\n    state: enabled\r\n    immediate: true\r\n    permanent: true\r\n    service: \"{{ firewall_service }}\"<\/pre>\n<p>Create the handler for restarting the firewalld service. Use a text editor to create a file called roles\/myfirewall\/handlers\/main.yml. Include code to use the service module. The file contents should look like the following:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode roles]$ cat myfirewall\/handlers\/main.yml\r\n---\r\n# handlers file for myfirewall\r\n- name: restart firewalld\r\n  service:\r\n    name: firewalld\r\n    state: restarted\r\n<\/pre>\n<p>Create the file that defines the default value for the <em>firewall_service<\/em> variable. It should have a default value of ssh initially. We will override the value to open the port for http when we use the role in a later step. The file <code>myfirewall\/defaults\/main.yml<\/code> should contain the following content:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode roles]$ cat myfirewall\/defaults\/main.yml\r\n---\r\n# defaults file for myfirewall\r\nfirewall_service: ssh<\/pre>\n<p>Modify the <em>myvhost<\/em> role to include the <em>myfirewall<\/em> role as a dependency, then retest the modified role. Use a text editor to modify a file, called myvhost\/meta\/main.yml, that makes\u00a0 myvhost depend on the myfirewall role. The firewall_service variable should be set to http so the correct service port is opened. By using the role in this way, the default value of ssh for firewall_service will be ignored. The explicitly assigned value of http will be used instead. The dependencies section in the file should look like the following:<\/p>\n<pre class=\"lang:sh decode:true \">---\r\ndependencies:\r\n- { role: myfirewall, firewall_service: http }<\/pre>\n<p>Run the playbook again. Confirm the additional <em>myfirewall<\/em> tasks are successfully executed.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook use-vhost-role.yml\r\n\r\nPLAY [use vhost role playbook] *******************************************************************************************\r\n\r\nTASK [Gathering Facts] ***************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [debug] *************************************************************************************************************\r\nok: [managedhost2] =&gt; {\r\n\"msg\": \"Beginning web server configuration.\"\r\n}\r\n\r\nTASK [myfirewall : install firewalld] ************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nTASK [myfirewall : start and enable firewalld service] *******************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myfirewall : firewall services config] *****************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : install httpd] *******************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : start and enable httpd service] **************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : deliver html content] ************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myvhost : template vhost file] *************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [debug] *************************************************************************************************************\r\nok: [managedhost2] =&gt; {\r\n\"msg\": \"Web server has been configured.\"\r\n}\r\n\r\nPLAY RECAP ***************************************************************************************************************\r\nmanagedhost2 : ok=10 changed=1 unreachable=0 failed=0<\/pre>\n<p>Now we can connect from <em>controlnode<\/em> to webserver on <em>managedhost2<\/em>:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ curl -s managedhost2.example.com\r\nsimple index<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Example 3.<\/span><\/p>\n<p>In this lab, you will create two roles that use variables and parameters: <em>student.myenv<\/em> and <em>myapache<\/em>. The <code>myenv<\/code> role customizes a system with required packages and a helpful script for all users. It will customize a user account, specified by the myenv_user variable, with a profile picture and an extra command alias in their ~\/.bashrc file. The <em>myapache<\/em> role will install and configure the Apache service on a host. Two templates are provided which will be used for the \/etc\/httpd\/conf\/httpd.conf and the \/var\/www\/html\/index.html files: apache_httpdconf.j2 and apache_indexhtml.j2 respectively.<\/p>\n<p>Create directories to contain the Ansible roles. They should be contained in the <em>myenv<\/em> and <em>myapache<\/em> directories below ~\/ansible\/roles directory:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ pwd\r\n\/home\/miro\/ansible\/roles\r\n[miro@controlnode roles]$ ansible-galaxy init --offline myenv\r\n- myenv was created successfully\r\nmiro@controlnode roles]$ ansible-galaxy init --offline myapache\r\n- myapache was created successfully<\/pre>\n<p>Install the mkcd.sh.j2 file as a template for the <em>myenv<\/em> role.<br \/>\nThe lab setup script copied the file to the lab-roles working directory. Move it to the roles\/student.myenv\/templates\/ subdirectory.<\/p>\n<pre class=\"lang:sh decode:true\">[student@workstation lab-roles]$ mv mkcd.sh.j2 roles\/myenv\/templates\/<\/pre>\n<p>Define student.myenv role tasks to perform the following steps:<br \/>\n\u2022 Install packages defined by the myenv_packages variable.<br \/>\n\u2022 Copy the standard profile picture to the user&#8217;s home directory as ~\/profile.png. Use the myenv_user variable for the user name.<br \/>\n\u2022 Add the following line to the user&#8217;s ~\/.bashrc file: alias tree=&#8217;tree -C&#8217; . Use the<br \/>\nmyenv_user variable for the user name. Hint: The lineinfile module might be well suited for this task.<br \/>\n\u2022 Install the mkcd.sh.j2 template as \/etc\/profile.d\/mkcd.sh. It should have<br \/>\nuser:group ownership of root:root and have -rw-r&#8211;r&#8211; permissions.<br \/>\nThe role should fail with an error message when the myenv_user variable is an empty string.<br \/>\nModify roles\/myenv\/tasks\/main.yml so that it contains the following:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat myenv\/tasks\/main.yml\r\n---\r\n# tasks file for myenv\r\n\r\n- name: check myenv_user default\r\n  fail:\r\n    msg: You must specify the variable `myenv_user` to use this role!\r\n  when: \"myenv_user == ''\"\r\n\r\n- name: install my packages\r\n  yum:\r\n    name: \"{{ item }}\"\r\n    state: installed\r\n  with_items: \"{{ myenv_packages }}\"\r\n\r\n- name: copy placeholder profile pic\r\n  copy:\r\n    src: profile.png\r\n    dest: \"~{{ myenv_user }}\/profile.png\"\r\n\r\n- name: set an alias in `.bashrc`\r\n  lineinfile:\r\n    line: \"alias tree='tree -C'\"\r\n    dest: \"~{{ myenv_user }}\/.bashrc\"\r\n\r\n- name: template out mkcd function\r\n  template:\r\n    src: mkcd.sh.j2\r\n    dest: \/etc\/profile.d\/mkcd.sh\r\n    owner: root\r\n    group: root\r\n    mode: 0644<\/pre>\n<p>Define the <em>myenv_packages<\/em> variable for the <em>myenv<\/em> role so it contains the following packages: git, tree, and vim-enhanced.<br \/>\nCreate <code>roles\/myenv\/vars\/main.yml<\/code> with the following contents:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode roles]$ cat myenv\/vars\/main.yml\r\n---\r\n# vars file for myenv\r\nmyenv_packages:\r\n- 'git'\r\n- 'tree'\r\n- 'vim-enhanced'<\/pre>\n<p>Assign the empty string as the default value for the <em>myenv_user<\/em> variable.<br \/>\nCreate roles\/myenv\/defaults\/main.yml with the following contents<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode roles]$ cat myenv\/defaults\/main.yml\r\n---\r\n# defaults file for myenv\r\nmyenv_user: ''<\/pre>\n<p>Create a playbook, called <code>use-myenv.yml<\/code>, that runs on all hosts. It should use the myenv role, but do not set the <em>myenv_user<\/em> variable. Test the myenv role and confirm that it fails.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat use-myenv.yml\r\n---\r\n- name: setup my personal environment\r\n  become: yes\r\n  hosts: all\r\n  roles:\r\n  - myenv<\/pre>\n<p>Run the myenv.yml playbook. Check the ansible-playbook output to make sure it fails.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook use-myenv.yml\r\n\r\nPLAY [setup my personal environment] *************************************************************************************************************\r\n\r\nTASK [Gathering Facts] ***************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myenv : check myenv_user default] **********************************************************************************************************\r\nfatal: [managedhost2]: FAILED! =&gt; {\"changed\": false, \"msg\": \"You must specify the variable `myenv_user` to use this role!\"}\r\nto retry, use: --limit @\/home\/miro\/ansible\/use-myenv.retry\r\n\r\nPLAY RECAP ***************************************************************************************************************************************\r\nmanagedhost2 : ok=1 changed=0 unreachable=0 failed=1<\/pre>\n<p>Update the myenv.yml playbook so that it uses the student.myenv role, setting the <em>myenv_user<\/em> variable to student. Test the myenv role and confirm that it works properly.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat use-myenv.yml\r\n---\r\n- name: setup my personal environment\r\n  become: yes\r\n  hosts: managedhost2\r\n  roles:\r\n  - role: myenv\r\n    myenv_user: miro<\/pre>\n<p>Run the use-myenv.yml playbook. Check the ansible-playbook output to make sure the tasks ran properly.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook use-myenv.yml\r\n\r\nPLAY [setup my personal environment] *************************************************************************************************************\r\n\r\nTASK [Gathering Facts] ***************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myenv : check myenv_user default] **********************************************************************************************************\r\nskipping: [managedhost2]\r\n\r\nTASK [myenv : install my packages] ***************************************************************************************************************\r\nok: [managedhost2] =&gt; (item=[u'git', u'tree', u'vim-enhanced'])\r\n\r\nTASK [myenv : set an alias in `.bashrc`] *********************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myenv : template out mkcd function] ********************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nPLAY RECAP ***************************************************************************************************************************************\r\nmanagedhost2 : ok=4 changed=1 unreachable=0 failed=0<\/pre>\n<p>Create a handler that will restart the httpd service. Modify <code>roles\/myapache\/handlers\/main.yml<\/code> so that it contains the following:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat roles\/myapache\/handlers\/main.yml\r\n---\r\n# handlers file for myapache\r\n- name: restart apache\r\n  service:\r\n    name: httpd\r\n    state: restarted<\/pre>\n<p>Define <em>myapache<\/em> role tasks to perform the following steps:<br \/>\n\u2022 Install the httpd and firewalld packages.<br \/>\n\u2022 Copy the apache_httpdconf.j2 template to \/etc\/httpd\/conf\/httpd.conf. The<br \/>\ntarget file should be owned by root with -r&#8211;r&#8211;r&#8211; permissions. Restart Apache using the handler created previously.<br \/>\n\u2022 Copy the apache_indexhtml.j2 template to \/var\/www\/html\/index.html. The<br \/>\ntarget file should be owned by root with -r&#8211;r&#8211;r&#8211; permissions.<br \/>\n\u2022 Start and enable the httpd and firewalld services.<br \/>\n\u2022 Open port 80\/tcp on the firewall.<br \/>\nPackage installation should always occur when this role is used, but the other tasks should only occur when the <em>apache_enable<\/em> variable is set to true. The role should restart the Apache service when the configuration file is updated.<br \/>\nModify roles\/myapache\/tasks\/main.yml so that it contains the following:<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat roles\/myapache\/tasks\/main.yml\r\n---\r\n# tasks file for myapache\r\n- name: install apache package\r\n  yum:\r\n    name: httpd\r\n    state: latest\r\n\r\n- name: install firewalld package\r\n  yum:\r\n    name: firewalld\r\n    state: latest\r\n\r\n- name: template out apache configuration file\r\n  template:\r\n    src: apache_httpdconf.j2\r\n    dest: \/etc\/httpd\/conf\/httpd.conf\r\n    owner: root\r\n    group: root\r\n    mode: 0444\r\n  notify:\r\n    - restart apache\r\n  when: apache_enable\r\n\r\n- name: template out apache index.html\r\n  template:\r\n    src: apache_indexhtml.j2\r\n    dest: \/var\/www\/html\/index.html\r\n    owner: root\r\n    group: root\r\n    mode: 0444\r\n  when: apache_enable\r\n\r\n- name: start and enable apache daemon\r\n  service:\r\n    name: httpd\r\n    state: started\r\n    enabled: true\r\n  when: apache_enable\r\n\r\n- name: start and enable firewalld daemon\r\n  service:\r\n    name: firewalld\r\n    state: started\r\n    enabled: true\r\n  when: apache_enable\r\n\r\n- name: open http firewall port\r\n  firewalld:\r\n    port: 80\/tcp\r\n    immediate: true\r\n    permanent: true\r\n    state: enabled\r\n  when: apache_enable<\/pre>\n<p>Create the default variable values for the myapache role. The apache_enable variable should have a default value of false.<br \/>\nModify roles\/myapache\/defaults\/main.yml so it contains the following:<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ cat roles\/myapache\/defaults\/main.yml\r\n---\r\n# defaults file for myapache\r\napache_enable: false<\/pre>\n<p>Create a playbook, called use-myapache.yml, that runs on serverb. It should use the myapache role, but use the default value of the apache_enable variable. Test the myapache role and confirm that it installs the packages, but does not deploy the web server.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat use-myapache.yml\r\n---\r\n- name: setup apache\r\n  become: yes\r\n  hosts: managedhost2\r\n  roles:\r\n    - myapache<\/pre>\n<p>Run the <code>use-myapache.yml<\/code> playbook. Check the ansible-playbook output to make sure it installs the needed packages, but skips the remaining tasks.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook use-myapache.yml\r\n\r\nPLAY [setup apache] ****************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] *************************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : install apache package] *******************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : install firewalld package] ****************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : template out apache configuration file] ***************************************************************************************************\r\nskipping: [managedhost2]\r\n\r\nTASK [myapache : template out apache index.html] ***********************************************************************************************************\r\nskipping: [managedhost2]\r\n\r\nTASK [myapache : start and enable apache daemon] ***********************************************************************************************************\r\nskipping: [managedhost2]\r\n\r\nTASK [myapache : start and enable firewalld daemon] ********************************************************************************************************\r\nskipping: [managedhost2]\r\n\r\nTASK [myapache : open http firewall port] ******************************************************************************************************************\r\nskipping: [managedhost2]\r\n\r\nPLAY RECAP *************************************************************************************************************************************************\r\nmanagedhost2 : ok=3 changed=0 unreachable=0 failed=0<\/pre>\n<p>Modify the apache.yml playbook so that it uses the <em>myapache<\/em> role, setting the<br \/>\napache_enable variable to true. Test the myapache role and confirm that it works properly.<\/p>\n<pre class=\"lang:sh decode:true\">[miro@controlnode ansible]$ cat use-myapache.yml\r\n---\r\n- name: setup apache\r\n  become: yes\r\n  hosts: managedhost1\r\n  roles:\r\n    - role: myapache\r\n      apache_enable: true<\/pre>\n<p>Run the use-myapache.yml playbook. Check the ansible-playbook output to make sure the tasks ran properly.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ ansible-playbook use-myapache.yml\r\n\r\nPLAY [setup apache] ****************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] *************************************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : install apache package] *******************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : install firewalld package] ****************************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : template out apache configuration file] ***************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nTASK [myapache : template out apache index.html] ***********************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nTASK [myapache : start and enable apache daemon] ***********************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : start and enable firewalld daemon] ********************************************************************************************************\r\nok: [managedhost2]\r\n\r\nTASK [myapache : open http firewall port] ******************************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nRUNNING HANDLER [myapache : restart apache] ****************************************************************************************************************\r\nchanged: [managedhost2]\r\n\r\nPLAY RECAP *************************************************************************************************************************************************\r\nmanagedhost2 : ok=9 changed=4 unreachable=0 failed=0\r\n<\/pre>\n<p>Use a web browser to confirm that serverb is serving web content.<\/p>\n<pre class=\"lang:sh decode:true \">[miro@controlnode ansible]$ curl -S managedhost1\r\nmyapache is running<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When dealing with extensive playbooks, it is easier to split the tasks into roles. This also helps in reusing the roles in the future. Roles are a collection of tasks, which can be moved from one playbook to another, can be run independently but only through a playbook file. In other words, roles are a &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/miro.borodziuk.eu\/index.php\/2020\/02\/08\/ansible-roles\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Ansible Roles&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":3304,"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\/3303"}],"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=3303"}],"version-history":[{"count":50,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3303\/revisions"}],"predecessor-version":[{"id":3574,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/3303\/revisions\/3574"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media\/3304"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=3303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=3303"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=3303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}