<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ansible Training – 5. Ansible Roles - Basics</title><link>/docs/05/</link><description>Recent content in 5. Ansible Roles - Basics on Ansible Training</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="/docs/05/index.xml" rel="self" type="application/rss+xml"/><item><title>Docs: 5.1 Ansible Roles - Handlers and Blocks</title><link>/docs/05/01/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/docs/05/01/</guid><description>
&lt;p>We start to use handlers and blocks as well.&lt;/p>
&lt;h3 id="task-1">Task 1&lt;/h3>
&lt;ul>
&lt;li>Create a playbook &lt;code>myhandler.yml&lt;/code> which applies a role &lt;code>handlerrole&lt;/code> on &lt;code>node2&lt;/code>.&lt;/li>
&lt;li>The role &lt;code>handlerrole&lt;/code> should do the following:&lt;/li>
&lt;li>Create a directory &lt;code>newdir&lt;/code> in the folder &lt;code>/home/ansible&lt;/code>.&lt;/li>
&lt;li>If the folder didn&amp;rsquo;t exist before,
then do create a file &lt;code>README.TXT&lt;/code> in this folder containing the text &amp;ldquo;This folder was created at &lt;code>&amp;lt;timestamp&amp;gt;&lt;/code>&amp;rdquo;.&lt;/li>
&lt;li>The value of &lt;code>&amp;lt;timestamp&amp;gt;&lt;/code> should contain a quite accurate timestamp of when &lt;code>ansible-playbook&lt;/code> was run.&lt;/li>
&lt;li>Run the playbook several times to see if it is really idempotent.&lt;/li>
&lt;/ul>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">Hint&lt;/h4>
There is an ansible fact for when ansible was run.
&lt;/div>
&lt;details >
&lt;summary>Solution Task 1&lt;/summary>
&lt;p>Below is a possible solution:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ cat myhandler.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- hosts: node2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> become: true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> roles:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - handlerrole
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ansible-galaxy init roles/handlerrole
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ cat roles/handlerrole/tasks/main.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- name: create directory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ansible.builtin.file:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path: /home/ansible/newdir
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> state: directory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode: &lt;span style="color:#e6db74">&amp;#34;0755&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> notify: timestamp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ cat roles/handlerrole/handlers/main.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- name: create readme with timestamp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ansible.builtin.copy:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dest: /home/ansible/newdir/README.TXT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> content: &lt;span style="color:#e6db74">&amp;#34;This folder was created at {{ ansible_date_time.iso8601 }}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode: &lt;span style="color:#e6db74">&amp;#34;0644&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> listen: timestamp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ansible-playbook myhandler.yml &lt;span style="color:#75715e">#&amp;lt;-- some changes when run the first time&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ansible all -a &lt;span style="color:#e6db74">&amp;#34;cat /home/ansible/newdir/README.TXT&amp;#34;&lt;/span> &lt;span style="color:#75715e">#&amp;lt;-- show created files with it&amp;#39;s content&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ansible-playbook myhandler.yml &lt;span style="color:#75715e">#&amp;lt;-- no changes here, idempotent!&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/details>
&lt;h3 id="task-2">Task 2&lt;/h3>
&lt;ul>
&lt;li>Write a playbook &lt;code>download.yml&lt;/code> which runs a role &lt;code>downloader&lt;/code> on &lt;code>node2&lt;/code>.&lt;/li>
&lt;li>The role &lt;code>downloader&lt;/code> should try to download a file from a non-existing URL.&lt;/li>
&lt;li>The role should be able to handle errors.
Let it write the message &lt;em>&amp;ldquo;Download failed!&amp;rdquo;&lt;/em> to standard output if the download task failed.
The playbook must keep on running and shall not exit after this message.&lt;/li>
&lt;li>Regardless of download success,
output a message at the end of the play informing that the download attempt has finished.&lt;/li>
&lt;li>Use a &lt;code>block:&lt;/code> to do these tasks.&lt;/li>
&lt;li>Add another &lt;code>ansible.builtin.debug&lt;/code> task after the block to confirm the run continues even after a download failed.&lt;/li>
&lt;li>Run the playbook &lt;code>download.yml&lt;/code>.&lt;/li>
&lt;/ul>
&lt;details >
&lt;summary>Solution Task 2&lt;/summary>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ cat download.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- hosts: node2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> become: true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> roles:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - downloader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ansible-galaxy init roles/downloader
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ cat roles/downloader/tasks/main.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>---
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># tasks file for roles/downloader&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- block:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: Download things from the internet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ansible.builtin.get_url:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url: https://www.not_existing_url.com/not_existing_file
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dest: /tmp/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mode: &lt;span style="color:#e6db74">&amp;#34;0400&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> rescue:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: &lt;span style="color:#e6db74">&amp;#34;Print an error message&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ansible.builtin.debug:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> msg: &lt;span style="color:#e6db74">&amp;#34;Download failed!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> always:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: &lt;span style="color:#e6db74">&amp;#34;Print confirmation&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ansible.builtin.debug:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> msg: &lt;span style="color:#e6db74">&amp;#34;Download attempt finished.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>- name: &lt;span style="color:#e6db74">&amp;#34;A task after the block&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ansible.builtin.debug:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> msg: &lt;span style="color:#e6db74">&amp;#34;This gets executed after the block&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ansible-playbook download.yml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">Note&lt;/h4>
&lt;p>Note the failed download task, but the playbook finished nonetheless.&lt;/p>
&lt;/div>
&lt;/details>
&lt;h3 id="all-done">All done?&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://galaxy.ansible.com/ui/standalone/namespaces/2492/">Have a look at Jeff Geerling&amp;rsquo;s roles&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.jeffgeerling.com/">jeffgeerling.com&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>