<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Archives des Ansible - dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/category/ansible/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/category/ansible/</link>
	<description></description>
	<lastBuildDate>Fri, 15 May 2026 19:45:30 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/cropped-favicon_512x512px-min-32x32.png</url>
	<title>Archives des Ansible - dbi Blog</title>
	<link>https://www.dbi-services.com/blog/category/ansible/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Customer case study &#8211; automating SQL Server TLS Encryption with Ansible and Certificates (Architecture)</title>
		<link>https://www.dbi-services.com/blog/customer-case-study-automating-sql-server-tls-encryption-with-ansible-and-certificates-architecture/</link>
					<comments>https://www.dbi-services.com/blog/customer-case-study-automating-sql-server-tls-encryption-with-ansible-and-certificates-architecture/#respond</comments>
		
		<dc:creator><![CDATA[Amine Haloui]]></dc:creator>
		<pubDate>Fri, 15 May 2026 19:35:21 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=44594</guid>

					<description><![CDATA[<p>When working with SQL Server environments, securing client connections can become an important requirement, especially when TLS encryption must be implemented using certificates. In this context, a customer asked us to develop an Ansible playbook and role to automate the configuration of TLS for SQL Server. The certificates are generated from the customer PKI and [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/customer-case-study-automating-sql-server-tls-encryption-with-ansible-and-certificates-architecture/">Customer case study &#8211; automating SQL Server TLS Encryption with Ansible and Certificates (Architecture)</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>When working with SQL Server environments, securing client connections can become an important requirement, especially when TLS encryption must be implemented using certificates. In this context, a customer asked us to develop an Ansible playbook and role to automate the configuration of TLS for SQL Server. The certificates are generated from the customer PKI and provided as PEM files containing the server certificate, the private key, and the certificate chain.</p>



<p>However, some extractions and conversions are required before these certificates can be used on Windows and configured for SQL Server.</p>



<p>Here, the idea is to propose a solution (the architecture) that prepares the certificate, imports it on the SQL Server host, and configures SQL Server to use it.</p>



<p>We will also see how to separate the preparation and activation steps in order to reduce the impact on the SQL Server service.</p>



<p>In this blog post, we will describe the global approach and the Ansible logic used to implement certificate-based TLS encryption for SQL Server.</p>



<h2 class="wp-block-heading" id="h-implementation-logic">Implementation logic</h2>



<p>Before configuring TLS encryption on SQL Server, the first point was to understand the certificate format provided by the customer PKI.</p>



<p>In our case, the generated file is a &lt;machine&gt;.pem file. This file contains the server certificate used for TLS, the private key and the certificate chain with the intermediate and root certificates.</p>



<p>As this format cannot be directly used as-is on the Windows side for SQL Server, some extraction and conversion steps are required.</p>



<p>The general idea is to use the Ansible control node as a working area.</p>



<p>The PEM file is first copied into a temporary folder where the different parts of the certificate are extracted:</p>



<ul class="wp-block-list">
<li>the leaf certificate</li>



<li>the intermediate certificate</li>



<li>the root certificate</li>



<li>the private key</li>
</ul>



<p>These elements are then used to build a PFX file which can be imported on the Windows SQL Server host.</p>



<p>The PFX is installed in the LocalMachine\My certificate store while the intermediate and root certificates are imported into the appropriate Windows certificate stores.</p>



<p>The implementation has been designed around three different execution modes: stage, activate, and full.</p>



<p>The stage mode is used to prepare the certificate without any impact on the SQL Server service. It copies the PEM file, performs the extractions, builds the PFX file, copies it to the managed Windows node and imports the certificates into the Windows certificate stores. No registry change is performed, and the SQL Server service is not restarted. This mode is useful when we want to prepare the server in advance before switching SQL Server to the new certificate.</p>



<p>The activate mode assumes that the certificate is already present on the Windows server. Its role is to configure SQL Server to use the installed certificate and depending on the selected option, restart the SQL Server service or leave the change pending until the next planned reboot.</p>



<p>This can be useful when the certificate activation must be aligned with an existing maintenance window, for example during monthly OS patching.</p>



<p>The full mode executes the complete configuration from end to end. It performs the extraction and conversion steps, imports the certificates, grants the required permissions, configures SQL Server to use the expected certificate, and restarts the SQL Server service only if required. To avoid unnecessary impact, the role relies on the certificate thumbprint. If the expected certificate is already configured, no change is applied and the SQL Server service is not restarted. This behavior is important for idempotency.</p>



<p>For example, if the full mode is executed after an activate mode, nothing should be changed if the certificate is already the correct one. The same logic applies if the playbook is executed by mistake while the certificate has not been renewed.</p>



<p>Another point to manage is the restart of the SQL Server service. SQL Server loads the certificate configuration when the service starts. Therefore, when a new certificate is configured, the change is only effective after a restart of the SQL Server service.</p>



<p>For this reason the role should provide an option to control whether the restart is performed immediately or postponed to the next planned reboot.</p>



<p>We also have to consider DNS aliases. The standard use case is to generate a certificate containing at least the short name and the FQDN of the SQL Server host in the subjectAltName. If DNS aliases are used by client applications, they can also be added to the certificate SAN.</p>



<p>For example:</p>



<pre class="wp-block-code"><code>&#091;alt_names]
DNS.1 = A-WS2022-2.lab.local
DNS.2 = A-WS2022-2</code></pre>



<p>Finally, the customer confirmed that the private key included in the PEM file is not encrypted.</p>



<p>This simplifies the conversion process to PFX, but it also means that the PEM file must be handled carefully during the Ansible execution, especially in temporary folders and during file transfers. With this approach, the role provides a controlled way to prepare, activate, or fully configure TLS encryption for SQL Server while keeping the impact on the SQL Server service under control.</p>



<h2 class="wp-block-heading" id="h-logical-workflow">Logical workflow</h2>



<p>The complete workflow can be represented as follows:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="672" height="1024" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-672x1024.jpeg" alt="" class="wp-image-44607" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-672x1024.jpeg 672w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-197x300.jpeg 197w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-768x1169.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-1009x1536.jpeg 1009w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-1345x2048.jpeg 1345w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/I3_Wite_Background-scaled.jpeg 1681w" sizes="(max-width: 672px) 100vw, 672px" /></figure>



<h2 class="wp-block-heading" id="h-architecture-summary">Architecture summary</h2>



<p>The certificate manipulation is performed on the Ansible control node.</p>



<p>The Windows certificate import and SQL Server configuration are performed on the managed Windows SQL Server host.</p>



<p>This separation is useful because the PEM processing and PFX generation are handled with Linux tools such as OpenSSL while the certificate installation, private key permissions, registry configuration and SQL Server restart are handled through Windows modules and PowerShell. The design also supports a controlled deployment approach.</p>



<p>The certificate can first be staged without service impact then activated later during a maintenance window.</p>



<p>The full mode can be used when the complete implementation must be executed in a single run. The use of the certificate thumbprint is important for idempotency. It allows the role to detect whether SQL Server is already configured with the expected certificate and avoids unnecessary service restarts when no change is required.</p>



<h2 class="wp-block-heading" id="h-remarks">Remarks</h2>



<p>For certain reasons we do not disclose the code of the created role.</p>



<p>Thank you. <a href="https://www.linkedin.com/in/amine-haloui-76968056/">Amine Haloui</a></p>
<p>L’article <a href="https://www.dbi-services.com/blog/customer-case-study-automating-sql-server-tls-encryption-with-ansible-and-certificates-architecture/">Customer case study &#8211; automating SQL Server TLS Encryption with Ansible and Certificates (Architecture)</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/customer-case-study-automating-sql-server-tls-encryption-with-ansible-and-certificates-architecture/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Standardize SQL Server Disks on VMs using Ansible</title>
		<link>https://www.dbi-services.com/blog/how-to-standardize-sql-server-disks-on-vms-using-ansible/</link>
					<comments>https://www.dbi-services.com/blog/how-to-standardize-sql-server-disks-on-vms-using-ansible/#respond</comments>
		
		<dc:creator><![CDATA[Nathan Courtine]]></dc:creator>
		<pubDate>Mon, 23 Mar 2026 17:50:46 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Automation]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=43534</guid>

					<description><![CDATA[<p>INTRODUCTION Today, the benefits of automation no longer need much explanation: saving time, reducing human error, and ensuring every environment remains aligned with internal standards. What is less obvious, however, is how using an Ansible Playbook can provide advantages that more traditional scripting approaches — such as large PowerShell scripts — struggle to offer. That [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-standardize-sql-server-disks-on-vms-using-ansible/">How to Standardize SQL Server Disks on VMs using Ansible</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-introduction">INTRODUCTION</h2>



<p>Today, the benefits of automation no longer need much explanation: saving time, reducing human error, and ensuring every environment remains aligned with internal standards. What is less obvious, however, is how using an Ansible Playbook can provide advantages that more traditional scripting approaches — such as large PowerShell scripts — struggle to offer. That is exactly what I want to explore here.</p>



<p>When you complete an automated deployment of a SQL Server environment on Windows Server, there is a real sense of achievement. You have invested time and effort, and you expect that investment to pay off thanks to the reliability and repeatability of automation.</p>



<p>But everything changes when the next Windows Server upgrade or SQL Server version arrives… or when corporate standards evolve. Suddenly, you need to reopen a multi-thousand‑line PowerShell script and:</p>



<ul class="wp-block-list">
<li>Integrate the required changes while keeping execution stable,</li>



<li>Avoid subtle but potentially critical regressions,</li>



<li>Maintain clear and usable logging,</li>



<li>Retest the entire automation workflow,</li>



<li>Troubleshoot new issues introduced by the modifications.</li>
</ul>



<p>This is precisely the type of situation where Ansible becomes a far better long‑term investment. Its architecture and philosophy offer several advantages:</p>



<ul class="wp-block-list">
<li>Native idempotence, ensuring the same result even after multiple runs,</li>



<li>A declarative YAML approach, focusing on the desired end state rather than the execution steps,</li>



<li>Windows Server and SQL Server modules, providing built‑in idempotence and saving significant time,</li>



<li>Agentless connectivity, simplifying deployment on new machines,</li>



<li>A modular structure (roles, modules, variables), making adaptation and reuse of your automation much easier.</li>
</ul>



<p>In this article, I will give you a concrete overview by walking you through how to configure the disks required for SQL Server using Ansible.</p>



<h2 class="wp-block-heading" id="h-1-map-iscsi-controllers-to-disk-numbers">1-Map iSCSI controllers to disk numbers</h2>



<p>When developing an Ansible Playbook, one fundamental principle is to design for idempotence from the very start—not just rely on idempotent modules.</p>



<p>On Windows, disk numbering is not guaranteed: it depends on several factors – how disks are detected at startup, the firmware, and so on.<br>As a result, disk numbers may change from one reboot to another.</p>



<p>To ensure consistent and reliable execution of your deployment, this behavior must be accounted for directly in the design of your Playbook.<br>Otherwise, it may introduce wrong behaviors, and lead to:</p>



<ul class="wp-block-list">
<li>formatting the wrong disk,</li>



<li>mounting volumes on incorrect devices,</li>



<li>completely breaking the SQL Server provisioning workflow.</li>
</ul>



<p>In other words, idempotence is no longer guaranteed.</p>



<p>To ensure stable and predictable executions, you must determine dynamically the correct disk numbering at each execution.<br></p>



<p>You can use Get-Disk PowerShell command to achieve your goal, by searching iSCSI controller number and LUN position from Location property.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; gutter: false; title: ; notranslate">
$adapter = {{ disk.adapter }}
$lun = {{ disk.lun}}
(Get-Disk | Where-Object {
      $_.Location -match &quot;Adapter $adapter\s+:.*\s+LUN $lun&quot;
    }).number
</pre></div>


<p>We have done our mapping between VM specifications and Windows disk numbers.</p>



<h2 class="wp-block-heading" id="h-2-loop-sql-server-disks">2-Loop SQL Server disks</h2>



<p>Since we often have several disks to configure — Data, Logs, TempDB — we need to perform the same actions repeatedly on each disk:</p>



<ul class="wp-block-list">
<li>dynamically determine the disk number,</li>



<li>initialize it in GPT,</li>



<li>create the partition and format the volume in NTFS with a 64 KB allocation unit size,</li>



<li>assign an access path (drive letter or mountpoint),</li>



<li>apply certain specific configuration settings, such as disabling indexing,</li>



<li>verify the compliance of the disk configuration.</li>
</ul>



<p>As these actions are identical for all disks, the best approach is to factorize the tasks.<br>The Ansible pattern, for such scenario, is to loop that call in a dedicated Task File.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
---

- name: Manage all disk properties based on Location and Target numbers
  ansible.builtin.include_tasks: disks_properties.yml
  loop:
    - name: data
      location: &quot;{{ disk_specs.data.location }}&quot;
      target: &quot;{{ disk_specs.data.target }}&quot;
      label: &quot;{{ disk_specs.data.label }}&quot;
      letter: &quot;{{ disk_specs.data.letter }}&quot;
    - name: logs
      location: &quot;{{ disk_specs.logs.location }}&quot;
      target: &quot;{{ disk_specs.logs.target }}&quot;
      label: &quot;{{ disk_specs.logs.label }}&quot;
      letter: &quot;{{ disk_specs.logs.letter }}&quot;
    - name: tempdb
      location: &quot;{{ disk_specs.tempdb.location }}&quot;
      target: &quot;{{ disk_specs.tempdb.target }}&quot;
      label: &quot;{{ disk_specs.tempdb.label }}&quot;
      letter: &quot;{{ disk_specs.tempdb.letter }}&quot;
  loop_control:
    loop_var: disk

...
</pre></div>


<h2 class="wp-block-heading" id="h-3-implement-sql-server-disk-configuration">3- Implement SQL Server disk configuration</h2>



<p>Since we performed our loop in the previous section on the disks_properties.yml file, we can now implement the configuration actions inside this file.<br>First, we will retrieve the disk number and then begin configuring the disk according to best practices and our internal standards.</p>



<p>To guarantee idempotence, we will mark this step as not changed: this is only a Get action:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
---

- name: Identify the {{ disk.name }} disk number
  ansible.windows.win_shell: |
    $adapter = {{ disk.target }}
    $lun = {{ disk.location }}
    (Get-Disk | Where-Object {
      $_.Location -match &quot;Adapter $adapter\s+:.*\s+LUN $lun&quot;
    }).number
  register: disk_num
  changed_when: false
</pre></div>


<p>Then, we will register the disk number as an Ansible Fact for all this task file execution call.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
- name: Set fact for {{ disk.name }} disk number
  ansible.builtin.set_fact:
    &quot;disk_number_{{ disk.name }}&quot;: &quot;{{ disk_num.stdout | trim | int }}&quot;
</pre></div>


<p>We can now initialize the disk using <em>community.windows</em> module. Of course, use Ansible module if possible.</p>



<p>The parameter <em>disk_bps.partition_style</em> is a variable of my Ansible Role, to guarantee GPT will be used.<br></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
- name: Initialize disks
  community.windows.win_initialize_disk:
    disk_number: &quot;{{ lookup(&#039;vars&#039;, &#039;disk_number_&#039; + disk.name) }}&quot;
    style: &quot;{{ disk_bps.partition_style }}&quot;
</pre></div>


<p>From there, we can create our partition: </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
- name: Create partition with letter {{ disk.letter }} for disk {{ disk.name }}
  community.windows.win_partition:
    drive_letter: &quot;{{ disk.letter }}&quot;
    partition_size: &quot;-1&quot;
    disk_number: &quot;{{ lookup(&#039;vars&#039;, &#039;disk_number_&#039; + disk.name) }}&quot;
</pre></div>


<p>And now format our volume with allocation unit size 64KB:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
- name: Create a partition letter {{ disk.letter }} on disk {{ disk.name }} with label {{ disk.label }}
  community.windows.win_format:
    drive_letter: &quot;{{ disk.letter }}&quot;
    allocation_unit_size: &quot;{{ disk_bps.allocation_unit_size_bytes }}&quot;
    new_label: &quot;{{ disk.label }}&quot;

...
</pre></div>


<p>As I mentioned earlier in previous section, we can also add tasks relative to some specific standards or a tasks to guarantee disk compliance.</p>



<h2 class="wp-block-heading" id="h-4-execute-the-playbook">4- Execute the Playbook</h2>



<p>Now that our Ansible Role <em>windows_disks</em> is ready, we can call it through a Playbook.<br>Of course, we must adjust the reality of the iSCSI configuration of the Virtual Machine.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
---

- name: Configure Disks by detecting Disk Number
  hosts: Raynor
  gather_facts: false
  vars:
    disk_specs:
      data:
        location: 0
        target: 1
        label: SQL_DATA
        letter: E
      logs:
        location: 0
        target: 2
        label: SQL_TLOG
        letter: L
      tempdb:
        location: 0
        target: 3
        label: SQL_TEMPDB
        letter: T
  tasks:
    - name: gather facts
      ansible.builtin.setup:
      changed_when: false
      tags: &#x5B;always]
    - name: Configure Disks
      ansible.builtin.import_role:
        name: windows_disks
      tags: windows_disks


...
</pre></div>


<figure data-wp-context="{&quot;imageId&quot;:&quot;6a0e8a040bbf6&quot;}" data-wp-interactive="core/image" data-wp-key="6a0e8a040bbf6" class="wp-block-image size-large wp-lightbox-container"><img decoding="async" width="1024" height="623" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/03/Ansible_disks-1024x623.png" alt="" class="wp-image-43551" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/03/Ansible_disks-1024x623.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/03/Ansible_disks-300x182.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/03/Ansible_disks-768x467.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/03/Ansible_disks.png 1329w" sizes="(max-width: 1024px) 100vw, 1024px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			aria-label="Enlarge"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.imageButtonRight"
			data-wp-style--top="state.imageButtonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>



<h2 class="wp-block-heading" id="h-conclusion">CONCLUSION</h2>



<p>We have had an overview of how Ansible makes automation easier to maintain and to evolve, by focusing on the logic of our deployment and not on the code to achieve it.<br>Now, updating your standards or upgrading versions will no longer require rewriting scripts, but mainly adapting variables.</p>



<p>However, it is important to be aware that idempotence must also be maintained through design.<br></p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-standardize-sql-server-disks-on-vms-using-ansible/">How to Standardize SQL Server Disks on VMs using Ansible</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-to-standardize-sql-server-disks-on-vms-using-ansible/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Errorhandling in Ansible</title>
		<link>https://www.dbi-services.com/blog/errorhandling-in-ansible/</link>
					<comments>https://www.dbi-services.com/blog/errorhandling-in-ansible/#respond</comments>
		
		<dc:creator><![CDATA[Martin Bracher]]></dc:creator>
		<pubDate>Thu, 18 Sep 2025 13:54:32 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[errorhandling]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=39033</guid>

					<description><![CDATA[<p>Errors in tasks &#8211; abort or ignore? Per default, if a task in a playbook fails, then the execution of the playbook is stopped for that host. As you can see, the 2nd task is not executed. If you want to continue in such a case, the ignore_errors parameter is your friend Custom error conditions [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/errorhandling-in-ansible/">Errorhandling in Ansible</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-errors-in-tasks-abort-or-ignore">Errors in tasks &#8211; abort or ignore?</h2>



<p>Per default, if a task in a playbook fails, then the execution of the playbook is stopped for that host. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
- name: PLAY1
  hosts: localhost
  gather_facts: no
  tasks:
    - name: let this shell-command fail
      ansible.builtin.shell: exit 1

    - name: let this shell-command complete
      ansible.builtin.shell: exit 0
</pre></div>


<figure class="wp-block-image size-full"><img decoding="async" width="847" height="142" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-1.png" alt="" class="wp-image-39042" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-1.png 847w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-1-300x50.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-1-768x129.png 768w" sizes="(max-width: 847px) 100vw, 847px" /></figure>



<p>As you can see, the 2nd task is not executed. If you want to continue in such a case, the <code>ignore_errors</code> parameter is your friend</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: let this shell-command fail
      ansible.builtin.shell: exit 1
      ignore_errors: true
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="847" height="170" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-2.png" alt="" class="wp-image-39043" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-2.png 847w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-2-300x60.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/image-2-768x154.png 768w" sizes="auto, (max-width: 847px) 100vw, 847px" /></figure>



<h2 class="wp-block-heading" id="h-custom-error-conditions">Custom error conditions</h2>



<p>Per default, Ansible evaluates the exit-code of the module, in case of the shell-module, the exit-code of the last command.</p>



<p>But for some commands, that is not adequate. Example: The Oracle commandline tool <code>sqlplus</code> to submit sql-commands will have an exit-code of 0 if it can connect to the database-instance. It is not related to the result of your SQL-commands. Error-messages in Oracle are prefixed by ORA-.</p>



<p>So, if you want to check for application errors, you have to implement it yourself. For that, you can use the <code>failed_when</code> option.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: let this shell-command fail
      ansible.builtin.shell: |
        . ~/.profile
        echo &quot;select count(*) from all_users;&quot; | sqlplus / as sysdba
      register: number_of_users
      failed_when: &quot;&#039;ORA-&#039; in number_of_users.stdout&quot;
</pre></div>


<p>Caution: In this case the exit-code of the shell is no longer evaluated. To also get the exit-code of the sqlplus call (e.g., sqlplus can not connect to the database, or sqlplus binary not found), you have to add this (default) condition: </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
      failed_when: &quot;number_of_users.rc != 0 or &#039;ORA-&#039; in number_of_users.stdout&quot;
</pre></div>


<p>But caution! Not all modules will have an <code>rc</code> field.</p>



<h2 class="wp-block-heading" id="h-tuning-perform-several-checks-at-once">Tuning: Perform several checks at once</h2>



<p>Conceptually, Ansible is not the fastest tool. For each task, it will usually login with ssh to the remote server. If you have to run several checks in the shell, then, instead of running each in a separate task, you can run all these check-commands in one shell-task, and evaluate the result afterwards.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: run many check commands
      ansible.builtin.shell: |
        mount | grep &#039; on /u00 &#039;  #check if /u00 is mounted
        rpm -q ksh 2&gt;&amp;1  #check if ksh is installed
        exit 0 # do not fail if rpm exit != 0; we will do our own errorhandling
      register: checks

    - name: fail if no /u00 is mounted
      fail:
        msg: &quot;/u00 is not mounted&quot;
      when: &quot;&#039; on /u00 &#039; not in checks.stdout&quot;

    - name: No ksh found, try to install it
      yum:
        name: ksh
        state: present
      when: &quot;&#039;package ksh is not installed&#039; in checks.stdout&quot;

</pre></div>


<p>If you only want to throw an error, then you can do it directly in the shell-task:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
when: &quot;&#039; on /u00 &#039; not in checks.stdout&quot; or &#039;package ksh is not installed&#039; in checks.stdout
</pre></div>


<p> But if you parse the output afterwards, you can run tasks to fix the error. </p>



<p>Sometimes it is difficult to parse the output if some commands return the same output, e.g. &#8220;OK&#8221;.<br>If your check-commands always return exactly 1 line, then you can directly parse the output of the command. The output of the 3rd command is in <code>checks.stdout_lines[2]</code>.<br>In the above example that will not work because <code>grep</code> will return the exit-code 0 (not found) or 1 (found) plus the found line. So, expand it as: <code>mount | grep ' on /u00 ' || echo error</code></p>



<h2 class="wp-block-heading" id="h-print-errormessages-more-readable">Print errormessages more readable</h2>



<p>Do not fail the task itself, it is very usually unreadable because all information is printed on one line.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="657" height="124" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-10.png" alt="" class="wp-image-40296" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-10.png 657w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-10-300x57.png 300w" sizes="auto, (max-width: 657px) 100vw, 657px" /></figure>



<p>Instead, use<code> ignore_errors: true </code>and <code>failed_when: false</code> in the task. Do the errorhandling in a separate task with a customized errormessage. To print the multiline list of stdout_lines, use <code>debug:</code> otherwise you can directy use <code>ansible.builtin.fail: </code>with a customized <code>message: </code></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: force sqlplus to throw error because of missing environment
      ansible.builtin.shell: |
        /u00/app/oracle/product/19.7.0/bin/sqlplus -L / as sysdba @myscript.sql 2&gt;&amp;1
      register: checks
      ignore_errors: true
      failed_when: false

    - name: Check for errors of task before
      debug: var=checks.stdout_lines
      failed_when: checks.rc != 0 or &#039;ORA-&#039; in checks.stdout
      when: checks.rc != 0 or &#039;ORA-&#039; in checks.stdout
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="658" height="181" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-11.png" alt="" class="wp-image-40298" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-11.png 658w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-11-300x83.png 300w" sizes="auto, (max-width: 658px) 100vw, 658px" /></figure>



<h2 class="wp-block-heading" id="h-re-run-failed-hosts">Re-run failed hosts</h2>



<p>As an example for this scenario: A customer of mine will do an offline backup of the development databases. And I have to run an Ansible playbook against all databases. If the playbook for this host is run at the backup time, it will fail because the database is down. But after some minutes the database will be restarted. </p>



<p>What we can do now?</p>



<p>Wait until the database is up again. That is possible, see the example of <code>ansible.builtin.wait_for</code> in my blog post <a href="https://www.dbi-services.com/blog/parallel-execution-of-ansible-roles/">Parallel execution of Ansible roles</a>. But for this scenario it is a waste of time. The database can be stopped (not for backup) and will not be restarted within the next few minutes.</p>



<p>Try later after a while. My playbook for all hosts (parallel forks=5) takes about 1 hour. The idea now is to remember the host with the stopped database and to continue with the next host. After the play finished for all hosts, restart the play for the remembered hosts.</p>



<ul class="wp-block-list">
<li>The 1st play running against all database hosts:</li>



<li>gets the status of the databases on the host</li>



<li>assigns the database instances to a <code>is_running</code> and a <code>not_open</code> list</li>



<li>Include the role to run against the running databases</li>



<li>Dynamically add the host to the group <code>re_run_if_not_open</code> if there are <code>not_open</code> databases</li>
</ul>



<ul class="wp-block-list">
<li>The next play only runs for the <code>re_run_if_not_open</code> group</li>



<li>Include the role to run against the (hopefully now running) databases</li>



<li>If the database then is still down, we assume it is stopped permanently.</li>
</ul>



<p></p>
<p>L’article <a href="https://www.dbi-services.com/blog/errorhandling-in-ansible/">Errorhandling in Ansible</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/errorhandling-in-ansible/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Parallel execution of Ansible roles</title>
		<link>https://www.dbi-services.com/blog/parallel-execution-of-ansible-roles/</link>
					<comments>https://www.dbi-services.com/blog/parallel-execution-of-ansible-roles/#respond</comments>
		
		<dc:creator><![CDATA[Martin Bracher]]></dc:creator>
		<pubDate>Tue, 10 Jun 2025 18:56:18 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[DevOps]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=31860</guid>

					<description><![CDATA[<p>Introduction You can run a playbook for specific host(s), a group of hosts, or &#8220;all&#8221; (all hosts of the inventory). Ansible will then run the tasks in parallel on the specified hosts. To avoid an overload, the parallelism &#8211; called &#8220;forks&#8221; &#8211; is limited to 5 per default. A task with a loop (e.g. with_items:) [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/parallel-execution-of-ansible-roles/">Parallel execution of Ansible roles</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-introduction">Introduction</h2>



<p>You can run a playbook for specific host(s), a group of hosts, or &#8220;all&#8221; (all hosts of the inventory). </p>



<p>Ansible will then run the tasks in parallel on the specified hosts. To avoid an overload, the parallelism &#8211; called &#8220;forks&#8221; &#8211; is limited to 5 per default.</p>



<p>A task with a loop (e.g. <code>with_items:</code>) will be executed serially per default. To run it in parallel, you can use the &#8220;async&#8221; mode. </p>



<p>But unfortunately, this async mode will not work to include roles or other playbooks in the loop. In this blog post we will see a workaround to run roles in parallel (on the same host).</p>



<h2 class="wp-block-heading" id="h-parallelization-over-the-ansible-hosts">Parallelization over the ansible hosts</h2>



<p>In this example, we have 3 hosts (dbhost1, dbhost2, dbhost3) in the dbservers group <br>(use <code>ansible-inventory --graph</code> to see all your groups) and we run the following sleep1.yml playbook </p>



<pre class="wp-block-code"><code>- name: PLAY1
  hosts: &#091;dbservers]
  gather_facts: no
  tasks:
    - ansible.builtin.wait_for: timeout=10</code></pre>



<p>The tasks of the playbook will run in  parallel on all hosts of the <code>dbservers</code> group, but not more at the same time as specified with the &#8220;forks&#8221; parameter. (specified in ansible.cfg, shell-variable ANSIBLE_FORKS, commandline parameter &#8211;forks)<br><a href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html">https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html</a></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ time ansible-playbook sleep1.yml --forks 2
...
ok: &#x5B;dbhost1]  #appears after 10sec
ok: &#x5B;dbhost2]  #appears after 10sec
ok: &#x5B;dbhost3]  #appears after 20sec
...
real    0m22.384s
</pre></div>


<p>With forks=2 the results of dbhost1 and dbhost2 will both be returned after 10 seconds (sleep 10 in parallel). dbhost3 has to wait until one of the running tasks is completed. So the playbook will complete after approx. 20 seconds. If forks is 1, then it takes 30s, if forks is 3, it takes 10s (plus overhead).</p>



<h2 class="wp-block-heading" id="h-parallelization-of-loops">Parallelization of loops</h2>



<p>Per default, a loop is not run in parallel</p>



<pre class="wp-block-code"><code>- name: PLAY2A
  hosts: localhost
  tasks:
    - set_fact:
        sleepsec: &#091; 1, 2, 3, 4, 5, 6, 7 ]

    - name: nonparallel loop
      ansible.builtin.wait_for: "timeout={{item}} "
      with_items: "{{sleepsec}}"
      register: loop_result
</code></pre>



<p>This sequential run will take at least 28 seconds. </p>



<p>To run the same loop in parallel, use &#8220;async&#8221;<br><a href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_async.html">https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_async.html</a></p>



<pre class="wp-block-code"><code>- name: PLAY2B
  hosts: localhost
  gather_facts: no
  tasks:
    - name: parallel loop
      ansible.builtin.wait_for: "timeout={{item}}"
      with_items: "{{sleepsec}}"
      register: loop_result
      async: 600  # Maximum runtime in seconds. Adjust as needed.
      poll: 0     # Fire and continue (never poll here)

    # in the meantime, you can run other tasks

    - name: Wait for parallel loop to finish (poll)
      async_status:
        jid: "{{ item.ansible_job_id }}"
      register: loop_check
      until: loop_check.finished
      delay: 1      # Check every 1 seconds
      retries: 600  # Retry up to 600 times. 
                    # delay*retries should be "async:" above
      with_items: "{{ loop_result.results }}"
</code></pre>



<p>In the first task we start all sleeps in parallel. It will timeout after 600 seconds. We will not wait for the result (poll: 0). A later task polls the background processes until all parallel loops are finished. This execution only takes a little bit more than 7 seconds (the longest sleep plus some overhead). Between the loop and the poll you can add other tasks to use the waiting time for something more productive. Or if you know your loop takes at least 1 minute, then you can add to reduce the overhead of the polling loop, an <code>ansible.builtin.wait_for: "timeout=60"</code>.</p>



<p>For example, we have an existing role to create and configure a new useraccount with many, sometimes longer running steps, e.g. add to LDAP, create NFS share, create a certificate, send a welcome-mail, &#8230;.; most of these tasks are not bound to a specific host, and will run on &#8220;localhost&#8221; calling a REST-API.</p>



<p>The following code example is a dummy role for copy/paste to see how it works with parallel execution.</p>



<pre class="wp-block-code"><code># roles/create_user/tasks/main.yml    
    - debug: var=user
    - ansible.builtin.wait_for: timeout=10</code></pre>



<p>Now we have to create many useraccounts and would like to do that  in parallel. We use the code above and adapt it:</p>



<pre class="wp-block-code"><code>- name: PLAY3A
  hosts: localhost
  gather_facts: no
  tasks:
    - set_fact:
        users: &#091; 'Dave', 'Eva', 'Hans' ]

    - name: parallel user creation
      ansible.builtin.include_role: name=create_user
      with_items: "{{users}}"
      loop_control:
        loop_var: user
      register: loop_result
      async: 600
      poll: 0</code></pre>



<p>But unfortunately, Ansible will not accept include_role: <br><code>ERROR! 'poll' is not a valid attribute for a IncludeRole</code></p>



<p>The only solution is to rewrite the role and to run every task with the async mode. </p>



<p>But is there no better solution to re-use existing roles? Let&#8217;s see&#8230;</p>



<h2 class="wp-block-heading" id="h-parallel-execution-of-roles-in-a-loop">Parallel execution of roles in a loop</h2>



<p>As we already know</p>



<ul class="wp-block-list">
<li>Ansible can run playbooks/tasks in parallel over different hosts (hosts parameter of the play).</li>



<li>Ansible can run tasks with a loop in parallel with the async option, but</li>



<li>Ansible can NOT run tasks with a loop in parallel for include_role or include_tasks</li>
</ul>



<p>So, the trick will be to run the roles on &#8220;different&#8221; hosts. There is a special behavior of localhost. Well-known is the localhost IP 127.0.0.1; But also 127.0.0.2 to 127.255.255.254 refer to localhost (check it with &#8216;ping&#8217;). For our create-user script: we will run it on &#8220;different&#8221; localhosts in parallel. For that, we create a host-group at runtime with localhost addresses. The number of these localhost IP&#8217;s is equal to the number of users to create.</p>



<p>users[0] is Dave. It will be created on 127.0.0.1<br>users[1] is Eva. It will be created on 127.0.0.2<br>users[2] is Hans. It will be created on 127.0.0.3<br>&#8230;</p>



<pre class="wp-block-code"><code>- name: create dynamic localhosts group
  hosts: localhost
  gather_facts: no
  vars:
    users: &#091; 'Dave', 'Eva', 'Hans' ]
  tasks:
    # Create a group of localhost IP's; 
    # Ansible will treat it as "different" hosts.
    # To know, which locahost-IP should create which user:
    # The last 2 numbers of the IP matches the element of the {{users}} list:
    # 127.0.1.12 -&gt; (1*256 + 12)-1 = 267 -&gt; users&#091;267]
    # -1: first Array-Element is 0, but localhost-IP starts at 127.0.0.1
    - name: create parallel execution localhosts group
      add_host:
        name: "127.0.{{item|int // 256}}.{{ item|int % 256 }}"
        group: localhosts
      with_sequence:  start=1  end="{{users|length}}" 

- name: create useraccounts
  hosts: &#091;localhosts]  # &#091; 127.0.0.1, 127.0.0.2, ... ]
  connection: local
  gather_facts: no
  vars:
    users: &#091; 'Dave', 'Eva', 'Hans' ]
  # this play runs in parallel over the &#091;localhosts] 
  tasks:
    - set_fact:
        ip_nr: "{{ inventory_hostname.split('.') }}"

    - name: parallel user creation
      ansible.builtin.include_role:
        name: create_user
      vars:
        user: "{{ users&#091; (ip_nr&#091;2]|int*256 + ip_nr&#091;3]|int-1) ] }}"
</code></pre>



<p>In this example: With forks=3 it runs in 11 seconds. With forks=1 (no parallelism) it takes 32 seconds.</p>



<p>The degree of parallelism (forks) depends on your use-case and your infrastructure. If you have to restore files, probably the network-bandwith, disk-I/O or the number of tape-slots is limited. Choose a value of forks that does not overload your infrastructure.</p>



<p>If some tasks or the whole role has to be run on another host than localhost (e.g. create a local useraccount on a server), then you can use <code>delegate_to: "{{remote_host}}"</code>.</p>



<p>This principle can ideally be used for plays that are not bound to a specific host, usually for tasks that will run from localhost and calling a REST-API without logging in with ssh to a server. </p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>Ansible is optimized to run playbooks on different hosts in parallel. The degree of parallelism can be limited by the &#8220;forks&#8221; parameter (default 5).</p>



<p>Ansible can run loops in parallel with the async mode. Unfortunately that does not work if we include a role or tasks.</p>



<p>The workaround to run roles in parallel on the same host is to assign every loop item to a different host, and then to run the role on different hosts. For the different hosts we can use the localhost IP&#8217;s between 127.0.0.1 and 127.255.255.254 to build a dynamic host-group; the number corresponds to number of loop items</p>
<p>L’article <a href="https://www.dbi-services.com/blog/parallel-execution-of-ansible-roles/">Parallel execution of Ansible roles</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/parallel-execution-of-ansible-roles/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ansible: reduce output in loops &#8211; or replace loops</title>
		<link>https://www.dbi-services.com/blog/ansible-reduce-output-in-loops-or-replace-loops/</link>
					<comments>https://www.dbi-services.com/blog/ansible-reduce-output-in-loops-or-replace-loops/#respond</comments>
		
		<dc:creator><![CDATA[Martin Bracher]]></dc:creator>
		<pubDate>Fri, 23 May 2025 10:32:09 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[json_query]]></category>
		<category><![CDATA[loop]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=38400</guid>

					<description><![CDATA[<p>Reduce the output If you do a loop in Ansible, it always prints the item variable for each loop. &#8220;item&#8221; is the current list element. If you are looping over a list with small values, then the output is OK. But if the loop variable is a list of dict, especially with many or big [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/ansible-reduce-output-in-loops-or-replace-loops/">Ansible: reduce output in loops &#8211; or replace loops</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-reduce-the-output">Reduce the output</h2>



<p>If you do a <a href="https://ansible-doc.readthedocs.io/en/latest/rst/playbooks_loops.html">loop</a> in Ansible, it always prints the <code>item</code> variable for each loop. &#8220;item&#8221; is the current list element. If you are looping over a list with small values, then the output is OK.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - set_fact:
        myList: &#x5B; &quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot; ]

   - name: simple loop
      debug:
        msg: &quot;value of item is {{ item }}.&quot;
      with_items: &quot;{{myList}}&quot;
      register: loop_result
</pre></div>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
TASK &#x5B;simple loop] ***********************************************
ok: &#x5B;localhost] =&gt; (item=a) =&gt; {
    &quot;msg&quot;: &quot;value of item is a.&quot;
}
ok: &#x5B;localhost] =&gt; (item=b) =&gt; {
    &quot;msg&quot;: &quot;value of item is b.&quot;
}
ok: &#x5B;localhost] =&gt; (item=c) =&gt; {
    &quot;msg&quot;: &quot;value of item is c.&quot;
}
ok: &#x5B;localhost] =&gt; (item=d) =&gt; {
    &quot;msg&quot;: &quot;value of item is d.&quot;
}
</pre></div>


<p>But if the loop variable is a list of dict, especially with many or big values, then the log-output of Ansible gets very unreadable.</p>



<p>Example: you have to deploy more than one trusted certificate to an <a href="https://www.dbi-services.com/blog/different-types-of-oracle-wallets/">Oracle wallet</a> (a <a href="https://en.wikipedia.org/wiki/PKCS_12">PKCS#12 container file</a>). The size of a certificate is usually 1-4 KB. That means, if you deploy 4 certificates à 3KB, your screen is spammed by approx. 12&#8217;000 characters. And I think you are not interested in the certificate content. Isn&#8217;t it?</p>



<p>Variable trusted_certs (certificate shortened for better readability)</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
       trusted_certs:
          - { issuer: &quot;letsencrypt&quot;,
              certificate: &quot;-----BEGIN CERTIFICATE-----\nMIIFBjCCAu6gAwIBAgI....MEZSa8DA\n-----END CERTIFICATE-----&quot; }
          - { issuer: &quot;verisign&quot;,
              certificate: &quot;-----BEGIN CERTIFICATE-----\nMIICkDCCAXgCAQIwHDq....kMJVW2X=\n-----END CERTIFICATE-----&quot; }
</pre></div>


<p>With the following task we add the certificate to the Oracle wallet</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: Add trusted certificates to wallet
      ansible.builtin.shell:  |
        echo &quot;{{ item.certificate }}&quot; &gt; {{ item.issuer }}.cert.pem
        orapki wallet add -wallet . -cert {{ item.issuer }}.cert.pem -trusted_cert -pwd {{ walletpw }}
      with_items: &quot;{{trusted_certs}}&quot;
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="566" height="617" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/long_cert_output.png" alt="" class="wp-image-38484" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/long_cert_output.png 566w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/long_cert_output-275x300.png 275w" sizes="auto, (max-width: 566px) 100vw, 566px" /></figure>



<p>But how we can get rid of this annoying output? </p>



<p>One possibility is to use the <code>no_log: true</code> option. Then, item will be replaced by &#8220;None&#8221; in the output.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
- name: Add trusted certificates to wallet
  ansible.builtin.shell:  |
    echo &quot;{{ item.certificate }}&quot; &amp;gt; {{ item.issuer }}.cert.pem
    orapki wallet add -wallet . -cert {{ item.issuer }}.cert.pem -trusted_cert -pwd {{ walletpw }}
  with_items: &quot;{{trusted_certs}}&quot;
  no_log: true
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="559" height="63" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/nolog_cert_output.png" alt="" class="wp-image-38912" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/nolog_cert_output.png 559w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/nolog_cert_output-300x34.png 300w" sizes="auto, (max-width: 559px) 100vw, 559px" /></figure>



<p>Nice 🙂 But in case of errors, we have a problem to see what went wrong 🙁</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="559" height="115" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/nolog_errors.png" alt="" class="wp-image-38913" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/nolog_errors.png 559w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/nolog_errors-300x62.png 300w" sizes="auto, (max-width: 559px) 100vw, 559px" /></figure>



<p>Another approach is: Instead of looping over the certificate list, create a list with the index numbers of your certificate list.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: Add trusted certificates to wallet
      ansible.builtin.shell:  |
        echo &quot;{{ trusted_certs&#x5B;item|int].certificate }}&quot; &gt; {{ trusted_certs&#x5B;item|int].issuer }}.cert.pem
        orapki wallet add -wallet . -cert {{ trusted_certs&#x5B;item|int].issuer }}.cert.pem -trusted_cert -pwd {{ walletpw }}
      with_sequence: start=0 end={{ trusted_certs|length-1 }}
</pre></div>


<p>We will create a sequence to identify the list elements using <code>with_sequence:</code> The 1st list element is [0] (<code>start=0</code>) and the last one is the number of elements -1 (`end={{ trusted_certs|length-1 }}`).</p>



<p>For example, to access the 1st certificate (index number 0) we can use <code>{{ trusted_certs[0].certificate }}</code>. In the loop we replace the [0] by [index|int] to access all elements. Caution! Ansible is not able to recognize the sequence as numbers and treats it as string, so you have to explicitly convert it to int.</p>



<p>And now, the output is very compact and readable 🙂</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="570" height="48" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/short_cert_output-1.png" alt="" class="wp-image-38486" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/short_cert_output-1.png 570w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/short_cert_output-1-300x25.png 300w" sizes="auto, (max-width: 570px) 100vw, 570px" /></figure>



<h2 class="wp-block-heading" id="h-replace-loops">Replace loops</h2>



<p>Loops are easy to implement and to understand. It is nice for a few list elements, but if you have thousands of elements, then you get a big amount of output, and it is slow. </p>



<p>If you want to see what certificates are added (only the issuer field), you can do it similar to the task to add certificates:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: Show trusted certificates to add
      ansible.builtin.debug:  var=trusted_certs&#x5B;item|int].certificate
      with_sequence: start=0 end={{ trusted_certs|length-1 }}
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="417" height="168" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/debug_loop.png" alt="" class="wp-image-38629" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/debug_loop.png 417w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/debug_loop-300x121.png 300w" sizes="auto, (max-width: 417px) 100vw, 417px" /></figure>



<p>Or you can use <code>json_query</code></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: Show trusted certificates to add (with json_query)
      ansible.builtin.debug:  var=trusted_certs|json_query(&#039;&#x5B;].issuer&#039;)
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="417" height="117" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/debug_json.png" alt="" class="wp-image-38630" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/debug_json.png 417w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/debug_json-300x84.png 300w" sizes="auto, (max-width: 417px) 100vw, 417px" /></figure>



<p>json_query: <code>[]</code> is the filter criteria. In this case, we will not filter the list elements. And <code>.issuer</code> means, to only return the issuer field and not the certificate field.</p>



<p>If you run a playbook for many servers (e.g. <code>hosts: [dbservers]</code>, a group with all database hosts) and you need information for the hosts from an ldap directory (or a REST web-service), then it is often faster to get once the whole data and then to extract the information for the hosts with json_query, instead of querying each host individually in ldap.</p>



<p>Example: If you have to know all databases on a server and you have stored all the connect strings (orclNetDescString) in an ldap directory, then you can get the information from there. For simplicity, we assume that:</p>



<ul class="wp-block-list">
<li><code>orclNetDescString</code>: contains no spaces, all keywords are in uppercase and <code>HOST=...</code>contains the <code>{{inventory_hostname}}</code></li>



<li><code>cn</code>: contains the database/instance name</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: &quot;get databases on the server&quot;
      community.general.ldap_search:
        server_uri: &quot;{{ ldap_server }}&quot;
        bind_dn: &quot;&quot;
        dn: &quot;cn=OracleContext,dc=domain,dc=com&quot;
        filter: &quot;(&amp;(orclNetDescString=*HOST*{{inventory_hostname}})(objectclass=orclNetService))&quot;
        scope: &quot;children&quot;
        attrs:
          - cn
          - orclNetDescString
        register: ldap1
    - debug: var=ldap1.results
</pre></div>


<p>It will return all databases for this host, in this example exactly one:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
ldap1.results: &#x5B;
  {
    &quot;cn&quot;: &quot;DB1&quot;
    &quot;dn&quot;: &quot;cn=DB1,cn=oracleContext,dc=domain,dc=com&quot;,
    &quot;orclNetDescString&quot;: &quot;(DESCRIPTION =(ADDRESS=(PROTOCOL=TCP)(HOST=srv01)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=DB01.domain.com)))&quot;
  }
]
</pre></div>


<p>You have to do that for the <code>[dbservers]</code> hosts. If you have 1000 database servers, you will run this task 1000 times.</p>



<p>Instead of querying LDAP for each hostname, we can query it for all hosts, and then extract the required data from the result for the current host with json_query.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
    - name: &quot;get databases of all servers&quot;
      run_once: true
      community.general.ldap_search:
        server_uri: &quot;{{ ldap_server }}&quot;
        bind_dn: &quot;&quot;
        dn: &quot;cn=OracleContext,dc=domain,dc=com&quot;
        filter: &quot;(objectclass=orclNetService)&quot;
        scope: &quot;children&quot;
        attrs:
          - cn
          - orclNetDescString
      register: ldap2
</pre></div>


<p>With <code>run_once</code> it will only be executed once on the 1st host, but the result is available for all hosts. All the hosts then can query this result to get the records of the own host:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
- name: show all databases on this host
  debug: var=ldap2.results|json_query(jquery)
  vars:
    jquery: &quot;&#x5B;? contains(orclNetDescString, `HOST={{inventory_hostname}}`)].cn&quot;
</pre></div>


<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>The output of a loop (loop, with_items, &#8230;) is annoying if the list item contains many or big values. To reduce the output you can:</p>



<p>use the <code>no_log: true</code> option. Then the item-values will no longer be printed. But in case of an error, you will no longer get the error-message.</p>



<p>Use the index of the list-elements. In the output you will see the index-number, and in case of errors, you will also see the error-message.</p>



<p>For filtering data, it is more efficient to use json_query instead of looping over all list items.</p>



<p>If you get data from an LDAP directory or a Webservice, try to fetch once all data and then to extract from this result the host-specific data with json_query.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/ansible-reduce-output-in-loops-or-replace-loops/">Ansible: reduce output in loops &#8211; or replace loops</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/ansible-reduce-output-in-loops-or-replace-loops/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>YaK Core – The Holy Grail for Deploying Ansible Code Everywhere</title>
		<link>https://www.dbi-services.com/blog/yak-core-the-holy-grail-for-deploying-ansible-code-everywhere/</link>
					<comments>https://www.dbi-services.com/blog/yak-core-the-holy-grail-for-deploying-ansible-code-everywhere/#respond</comments>
		
		<dc:creator><![CDATA[Hervé Schweitzer]]></dc:creator>
		<pubDate>Tue, 29 Apr 2025 13:52:46 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[OCI]]></category>
		<category><![CDATA[YaK]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=38158</guid>

					<description><![CDATA[<p>YaK core Multi-Platform open source Automation Tool simplifies the deployment of Ansible playbooks through a clean UI and API. It offers an intuitive interface where users can upload playbooks, configure parameters, and deploy them seamlessly across various platforms, and all managed through a centralized inventory stored in a PostgreSQL database. With YaK Core, developers can [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/yak-core-the-holy-grail-for-deploying-ansible-code-everywhere/">YaK Core – The Holy Grail for Deploying Ansible Code Everywhere</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="510" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/yak-core-open-source-multi-platform-1-1024x510.png" alt="" class="wp-image-38228" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/yak-core-open-source-multi-platform-1-1024x510.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/yak-core-open-source-multi-platform-1-300x149.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/yak-core-open-source-multi-platform-1-768x383.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/yak-core-open-source-multi-platform-1.png 1134w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="has-medium-font-size"><strong>YaK core Multi-Platform open source Automation Tool </strong>simplifies the deployment of Ansible playbooks through a clean UI and API. It offers an intuitive interface where users can upload playbooks, configure parameters, and deploy them seamlessly across various platforms, and all managed through a centralized inventory stored in a PostgreSQL database. With YaK Core, developers can focus on writing application code without worrying about infrastructure setup or management.</p>



<p class="has-medium-font-size"><strong>YaK</strong> consists of two parts: <strong>YaK Core</strong>, which is open source, and <strong>YaK Components</strong>, which can be installed on top. These <strong>YaK Components </strong>are platform-agnostic service packages (e.g., PostgreSQL, Oracle DB, MongoDB, Kubernetes, etc.), written in Ansible by experts. They provide essential operational features such as backup, patching, upgrades, and high availability. If you&#8217;d like to learn more about the available YaK components, feel free to <a href="https://yak4all.io/contact">contact us!</a></p>



<p class="has-medium-font-size">But that’s not all. <strong>YaK Core </strong>also lets you create your own <strong>YaK Components</strong> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60a.png" alt="😊" class="wp-smiley" style="height: 1em; max-height: 1em;" />. Once created, your component becomes immediately available for deployment across all platforms supported by YaK Core.</p>



<p class="has-medium-font-size">In this blog, I’ll show you how easy it is to create your own <strong>YaK Component</strong> using Ansible, upload it to <strong>YaK Core</strong>, and deploy it across any supported platform.</p>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h2 class="wp-block-heading has-text-align-center" id="h-yak-demo-platform-provisioning">YaK Demo platform provisioning</h2>



<p>To get started with YaK Core Multi-Platform open source solution, visit <a class="" href="https://yak4all.io">https://</a><a href="https://yak4all.io" target="_blank" rel="noreferrer noopener">yak4all</a><a class="" href="https://yak4all.io">.io</a> and provision your own YaK demo environment (take 5 minutes to be ready).</p>



<figure class="wp-block-embed is-type-wp-embed is-provider-yak wp-block-embed-yak"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="iT6gmuzgX5"><a href="https://yak4all.io/demo/">Demo</a></blockquote><iframe loading="lazy" class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;Demo&#8221; &#8212; YaK" src="https://yak4all.io/demo/embed/#?secret=rhIPnL8eSE#?secret=iT6gmuzgX5" data-secret="iT6gmuzgX5" width="500" height="282" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h2 class="wp-block-heading has-text-align-center" id="h-build-your-yak-component">Build your YaK Component</h2>



<p class="has-medium-font-size">To build a YaK Component, you need to declare at least the following three files </p>



<p class="has-text-align-left"><strong>     1. playbooks/create_linux_users.yml<br>     2. manifest.yml<br>     3. yak_variables_specifications/basic_variables_specifications.yml</strong></p>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-c7ffa4e866b5c2f8365c6699e09153cd" id="h-1-the-ansible-playbook" style="color:#006fb5">1. The Ansible Playbook </h3>



<p class="has-medium-font-size"><strong>playbooks/create_linux_users.yml</strong><br>This file is simply your Ansible playbook, nothing more. The only requirement is that the code uses variables, which will be exposed in the UI for configuration. The example playbook below will create a user and optionally grant them sudo privileges.</p>



<pre class="wp-block-code"><code>---
- name: Create Linux users
  hosts: linux_hosts
  become: true
  gather_facts: true

  tasks:
    - debug:
        var: user

    - name: Create users
      ansible.builtin.user:
        name: "{{ item.username }}"
        create_home: "{{ item.create_home | default(true) }}"
        state: present
      loop: "{{ user }}"

    - name: Add users to sudoers
      community.general.sudoers:
        name: "yak-sudoer-{{ item.username }}"
        user: "{{ item.username }}"
        commands: ALL
        state: present
      loop: "{{ user }}"
      when: item.is_sudoer
        
  post_tasks:
    - name: Update component state
      delegate_to: localhost
      yak.core.yak_component_state_update:
        component_state_name: 'deployed'
...</code></pre>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-9766ac0c91f53fa89ea1455a27f3848d" id="h-2-manifest-file" style="color:#006fb5">2. Manifest file</h3>



<p class="has-medium-font-size"><strong>manifest.yml</strong><br>This file contains the basic information about your component and specifies which playbooks can be executed.</p>



<pre class="wp-block-code"><code>name: linux_users

version:
  major: 1
  minor: 0
  patch: 0

sub_component_types:
  - display_label: Linux users
    name: create_linux_users
    features:
      - display_label: Create Linux users
        name: create_linux_users
        playbook_name: playbooks/create_linux_users.yml

    inventory_maps:
      - group_name: linux_hosts
        group_nicename: Linux hosts
        group_description: Host on which the users will be created
        group_min_hosts: 1
        group_max_hosts: 100
        type: host
        os_type: Linux</code></pre>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-4433a9419864971a558f59cbb6ad037e" id="h-3-variable-specification-file" style="color:#006fb5">3. Variable specification file</h3>



<p class="has-medium-font-size"><strong>yak_variables_specifications/basic_variables_specifications.yml</strong><br>Now you can define and provide all the specifications for the variables you want to make configurable, with all the required settings <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" />.</p>



<pre class="wp-block-code"><code>- variableName: user
  niceName: Users to create
  dataType: array
  children:
    - variableName: username
      niceName: Username
      dataType: string
      mandatory: true
      defaultValue: yak
      isOneOffSetting: false
      usage: Name of the user to create

    - variableName: create_home
      niceName: Create Home directory
      dataType: boolean
      mandatory: true
      defaultValue: true
      isOneOffSetting: false
      usage: Tick the box if you want to create a Home directory for the user (/home/&lt;username&gt;)

    - variableName: is_sudoer
      niceName: Grant sudo privileges
      dataType: boolean
      mandatory: true
      defaultValue: true
      isOneOffSetting: false
      usage: Tick the box if you want to grant "ALL" privileges escalation to the user</code></pre>



<p class="has-medium-font-size">That&#8217;s it! You now have all the necessary files for your first YaK Component. Next, create a ZIP package and upload it to your deployed YaK Demo environment.</p>



<p class="has-medium-font-size">To make things easier, I&#8217;ve created a ZIP file that you can upload directly. : <a href="https://www.swisstransfer.com/d/6db7e854-c74a-4616-be89-bc4375059161">create_linux_user.zip</a> </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1016" height="566" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.10.48.png" alt="" class="wp-image-38208" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.10.48.png 1016w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.10.48-300x167.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.10.48-768x428.png 768w" sizes="auto, (max-width: 1016px) 100vw, 1016px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h2 class="wp-block-heading has-text-align-center" id="h-setup-a-server">Setup a Server</h2>



<p class="has-medium-font-size">For this task, simply follow the documentation below up to Step 4: <em>Deploy your server</em> <a href="https://dbi-services.gitbook.io/yak-user-doc/introduction/yak-demo">https://dbi-services.gitbook.io/yak-user-doc/introduction/yak-demo</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h2 class="wp-block-heading has-text-align-center" id="h-declare-and-deploy-your-component">Declare and deploy your Component </h2>



<p class="has-medium-font-size">You’re now ready to declare and deploy your component!</p>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-aec523cc5c9123a8748ed34598dfbbd4" id="h-1-declare" style="color:#006fb5">1. Declare </h3>



<p class="has-medium-font-size"><strong>YaK UI -&gt; Components -&gt; Declare -&gt; Component_type : linux_users -&gt; Save</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="994" height="872" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.21.47.png" alt="" class="wp-image-38209" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.21.47.png 994w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.21.47-300x263.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.21.47-768x674.png 768w" sizes="auto, (max-width: 994px) 100vw, 994px" /></figure>



<h3 class="wp-block-heading has-text-color has-link-color wp-elements-a421b9f745833b9dda3d46b9d7e21629" id="h-2-deploy" style="color:#006fb5">2 Deploy </h3>



<p class="has-medium-font-size"><strong>YaK UI -&gt; Components -&gt; Select LinuxUser -&gt; Action -&gt; Create Linux User -&gt; Confirm</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="990" height="326" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.23.48.png" alt="" class="wp-image-38212" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.23.48.png 990w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.23.48-300x99.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/Screenshot-2025-04-28-at-15.23.48-768x253.png 768w" sizes="auto, (max-width: 990px) 100vw, 990px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h2 class="wp-block-heading has-text-align-center" id="h-conclusion">Conclusion</h2>



<p class="has-medium-font-size">This component can now be deployed on any cloud platform or integrated on On-Premises environment using the YaK UI, and can also be deployed in parallel on up to 100 servers, as specified in your Manifest file.</p>



<p class="has-medium-font-size">With this solution, you can provide your colleagues with an intuitive and efficient way to work with Ansible playbooks, enhancing their overall experience <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" />.</p>



<p class="has-medium-font-size">For more Information about YaK see the blogs available here : <a href="https://www.dbi-services.com/blog/yak">https://www.dbi-services.com/blog/yak</a></p>



<p></p>
<p>L’article <a href="https://www.dbi-services.com/blog/yak-core-the-holy-grail-for-deploying-ansible-code-everywhere/">YaK Core – The Holy Grail for Deploying Ansible Code Everywhere</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/yak-core-the-holy-grail-for-deploying-ansible-code-everywhere/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Migrating AWX from one Kubernetes cluster to another: A custom approach</title>
		<link>https://www.dbi-services.com/blog/migrating-awx-from-one-kubernetes-cluster-to-another-a-custom-approach/</link>
					<comments>https://www.dbi-services.com/blog/migrating-awx-from-one-kubernetes-cluster-to-another-a-custom-approach/#respond</comments>
		
		<dc:creator><![CDATA[Donovan Winter]]></dc:creator>
		<pubDate>Mon, 14 Oct 2024 08:25:54 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[awx]]></category>
		<category><![CDATA[awxoperator]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[kubernetes]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=35153</guid>

					<description><![CDATA[<p>In this guide, we’ll walk through migrating an AWX instance from one Kubernetes infrastructure to another, with two important considerations. First, both AWX instances are on completely different networks, meaning there’s no direct connectivity between them. Second, we aim to replicate the credentials (including passwords) stored in AWX, which requires careful handling. This approach differs [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/migrating-awx-from-one-kubernetes-cluster-to-another-a-custom-approach/">Migrating AWX from one Kubernetes cluster to another: A custom approach</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this guide, we’ll walk through migrating an AWX instance from one Kubernetes infrastructure to another, with two important considerations. First, both AWX instances are on completely different networks, meaning there’s no direct connectivity between them. Second, we aim to replicate the credentials (including passwords) stored in AWX, which requires careful handling. This approach differs from the <a href="https://ansible.readthedocs.io/projects/awx-operator/en/latest/migration/migration.html" target="_blank" rel="noreferrer noopener">official documentation</a> due to these two specific constraints.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="900" height="600" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/migrating-awx.png" alt="" class="wp-image-35156" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/migrating-awx.png 900w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/migrating-awx-300x200.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/migrating-awx-768x512.png 768w" sizes="auto, (max-width: 900px) 100vw, 900px" /></figure>



<h2 class="wp-block-heading" id="h-step-1-backup-awx-on-the-old-infrastructure">Step 1: Backup AWX on the old infrastructure</h2>



<p>To back up AWX on the old infrastructure, we’ll use the <code>AWXBackup</code> resource provided by the AWX Operator. This will capture all necessary configurations, including credentials, job templates, and database data.</p>



<ul class="wp-block-list">
<li><strong>Create the AWXBackup resource</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: awx.ansible.com/v1beta1
kind: AWXBackup
metadata:
  name: awx-backup
  namespace: &lt;namespace-awx&gt;
spec:
  deployment_name: &lt;awx-instance-name&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Apply the backup configuration</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl apply -f awxbackup.yaml
</pre></div>


<ul class="wp-block-list">
<li><strong>Verify the backup</strong><br>Check the status of the AWXBackup resource to ensure the backup is complete</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get awxbackup -n &lt;namespace-awx&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Access the backup data</strong><br>AWXBackup creates a PVC to store the backup data. We need to retrieve it.</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get pvc -n &lt;namespace-awx&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Mount the backup PVC</strong><br>Create a temporary pod to access the backup files</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: v1
kind: Pod
metadata:
  name: awx-backup-access
  namespace: &lt;namespace-awx&gt;
spec:
  containers:
  - name: backup-container
    image: busybox:latest
    command: &#x5B;&quot;/bin/sh&quot;, &quot;-c&quot;, &quot;sleep 3600&quot;]
    volumeMounts:
    - mountPath: /backup-data
      name: awx-backup-pvc
  volumes:
  - name: awx-backup-pvc
    persistentVolumeClaim:
      claimName: &lt;awx-backup-pvc&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Compress the backup</strong><br>Once inside the pod, go to the backup directory and archive the latest directory</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl exec -it awx-backup-access -n &lt;namespace-awx&gt; -- /bin/sh
cd /backup-data
ls -l
## Find the latest directory
tar -czvf /awx_backup.tar.gz &lt;latest-directory&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Copy the archive locally</strong><br>Use <code>kubectl cp</code> to copy the archive from the pod to your local machine</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl cp &lt;namespace-awx&gt;/awx-backup-access:/backup-data/awx_backup.tar.gz ./awx_backup.tar.gz
</pre></div>


<ul class="wp-block-list">
<li><strong>Clean up the temporary pod</strong><br>Once the backup is copied, delete the temporary pod</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl delete pod awx-backup-access -n &lt;namespace-awx&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Recover the decryption key for secret keys</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get secrets -n &lt;namespace-awx&gt; &lt;awx-instance-name&gt;-secret-key -o jsonpath=&#039;{.data.secret_key}&#039; &amp;&amp; echo;
</pre></div>


<p>Save the base 64 encrypted key, we will need it for during the restoring step.</p>



<h2 class="wp-block-heading" id="h-step-2-setup-the-new-awx-instance">Step 2: Setup the new AWX instance</h2>



<p>On the new infrastructure, we first need to install AWX via the AWX Operator.</p>



<ul class="wp-block-list">
<li><strong>Install the AWX Operator</strong><br>For this step follow the official <a href="https://ansible.readthedocs.io/projects/awx-operator/en/latest/" target="_blank" rel="noreferrer noopener">documentation of AWX Operator</a><br>Maybe you will need to deploy AWX using a local repository, you can read my other article: <a href="https://www.dbi-services.com/blog/deploy-awx-operator-with-helm-using-images-from-a-local-registry/" target="_blank" rel="noreferrer noopener">Deploy awx-operator with Helm using images from a local registry</a></li>
</ul>



<ul class="wp-block-list">
<li><strong>Verify the AWX deployment</strong><br>Check that the new AWX instance is up and running</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get awx -n &lt;new-namespace-awx&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-step-3-backup-awx-on-the-new-infrastructure">Step 3: Backup AWX on the new infrastructure</h2>



<p>Next, we need to create an AWXBackup on the new infrastructure.</p>



<ul class="wp-block-list">
<li><strong>Create an AWXBackup for the backup data</strong><br>Create the <code>awxbackup.yaml</code> file:</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: awx.ansible.com/v1beta1
kind: AWXBackup
metadata:
  name: awx-backup-migration
  namespace: &lt;namespace-awx&gt;
spec:
  deployment_name: &lt;awx-instance-name&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Apply the backup configuration</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl apply -f awxbackup.yaml
</pre></div>


<ul class="wp-block-list">
<li><strong>Verify the backup</strong><br>Check the status of the AWXBackup resource to ensure the backup is complete</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get awxbackup -n &lt;namespace-awx&gt;
</pre></div>


<ul class="wp-block-list">
<li><strong>Identify the backup PVC</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get pvc -n &lt;namespace-awx&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-step-4-transfer-and-restore-the-backup-on-the-new-infrastructure">Step 4: Transfer and restore the backup on the new infrastructure</h2>



<p>Now that the new AWX is set up, we’ll transfer the backup data and restore it.</p>



<ul class="wp-block-list">
<li><strong>Transfer the backup archive</strong><br>Copy the <code>awx_backup.tar.gz</code> file to the new infrastructure by uploading it to the new backup PVC using a temporary pod</li>
</ul>



<ul class="wp-block-list">
<li><strong>Create a temporary pod to restore data</strong><br>Create the awx-restore-access.yaml file</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: v1
kind: Pod
metadata:
  name: awx-backup-restore
  namespace: &lt;new-namespace-awx&gt;
spec:
  containers:
  - name: restore-container
    image: busybox:latest
    command: &#x5B;&quot;/bin/sh&quot;, &quot;-c&quot;, &quot;sleep 3600&quot;]
    volumeMounts:
    - mountPath: /backup-data
      name: awx-backup-pvc
  volumes:
  - name: awx-backup-pvc
    persistentVolumeClaim:
      claimName: &lt;pvc-for-awx-backup&gt;
</pre></div>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl apply -f awx-restore-access.yaml
</pre></div>


<ul class="wp-block-list">
<li><strong>Use <code>kubectl cp</code> to upload the archive to the pod</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl cp ./awx-backup-migration.tar.gz &lt;namespace-awx&gt;/awx-restore-access:/awx_backup.tar.gz
</pre></div>


<ul class="wp-block-list">
<li><strong>Replace the data from archive</strong><br>Inside the pod, extract the archive</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl exec -it awx-backup-restore -n &lt;new-namespace-awx&gt; -- /bin/sh
cd /backup-data
ls -l
## Find the latest directory

rm -rf /backup-data/&lt;latest-backup-directory&gt;/{tower.db,awx-objects}
cd
tar -xzvf /awx_backup.tar.gz

cp backup-data/&lt;backup-directory-from-tar.gz&gt;/tower.db /backup-data/&lt;latest-backup-directory&gt;/.
cp backup-data/&lt;backup-directory-from-tar.gz&gt;/awx-objects /backup-data/&lt;latest-backup-directory&gt;/.

vi /backup-data/&lt;latest-backup-directory&gt;/secret.yml
## Replace the value of the variable
## secrets:
##   secretKeySecret:
##     data: {secret_key: ###insert here the base64 of the decryption key recover at the end of the Step 1### }
</pre></div>


<ul class="wp-block-list">
<li><strong>Create the AWXRestore resource</strong><br>Create an <code>AWXRestore</code> resource to apply the backup</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: awx.ansible.com/v1beta1
kind: AWXRestore
metadata:
  name: awx-backup-restore
  namespace: &lt;new-namespace-awx&gt;
spec:
  deployment_name: &lt;awx-instance-name&gt;
  backup_name: awx-backup-migration
  no_log: false
  force_drop_db: true
</pre></div>


<ul class="wp-block-list">
<li><strong>Apply the AWXRestore</strong></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl apply -f awxrestore.yaml
</pre></div>


<ul class="wp-block-list">
<li><strong>Monitor the restoration</strong><br>Ensure the restoration completes successfully</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
kubectl get awxrestore -n &lt;new-namespace-awx&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-step-5-log-in-to-the-new-infrastructure-awx">Step 5: Log in to the new infrastructure AWX</h2>



<ul class="wp-block-list">
<li>You can log in as admin (the password will be that of the old infrastructure)</li>



<li>Explore the various AWX resources and check that everything has been migrated correctly</li>



<li>Run a template job to validate correct operation</li>
</ul>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p>By following this procedure, we’ve successfully migrated an AWX instance across two isolated Kubernetes clusters while maintaining full fidelity of the AWX credentials and configurations.</p>



<p></p>
<p>L’article <a href="https://www.dbi-services.com/blog/migrating-awx-from-one-kubernetes-cluster-to-another-a-custom-approach/">Migrating AWX from one Kubernetes cluster to another: A custom approach</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/migrating-awx-from-one-kubernetes-cluster-to-another-a-custom-approach/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ansible loops: A guide from basic to advanced examples</title>
		<link>https://www.dbi-services.com/blog/ansible-loops-a-guide-from-basic-to-advanced-examples/</link>
					<comments>https://www.dbi-services.com/blog/ansible-loops-a-guide-from-basic-to-advanced-examples/#respond</comments>
		
		<dc:creator><![CDATA[DevOps]]></dc:creator>
		<pubDate>Tue, 09 Jul 2024 08:22:30 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[devops]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=33446</guid>

					<description><![CDATA[<p>If you are writing roles with Ansible, you must already have thought about implementing a loop, a loop of loops with Ansible, and wonder how. The ability to execute tasks in loops is primordial. This guide will provide multiple loop examples in Ansible, starting with a basic loop and progressing to more advanced scenarios. Basic [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/ansible-loops-a-guide-from-basic-to-advanced-examples/">Ansible loops: A guide from basic to advanced examples</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>If you are writing roles with Ansible, you must already have thought about implementing a loop, a loop of loops with Ansible, and wonder how. The ability to execute tasks in loops is primordial. This guide will provide multiple loop examples in Ansible, starting with a basic loop and progressing to more advanced scenarios.</p>



<h2 class="wp-block-heading" id="h-basic-loop">Basic loop</h2>



<p>Let&#8217;s start with the most basic loop as an introduction. </p>



<p>In the following playbook, called playbook.yml, a list of numbers is created, from 1 to 5. Then a loop on the debug task displays each number.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# playbook.yml
- name: Loop examples
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
  - set_fact:
      numbers: &#x5B;1,2,3,4,5]
  
  - name: Most basic loop
    debug:
      msg: &#039;{{ item }}&#039;
    loop: &#039;{{ numbers }}&#039;
</pre></div>


<p>You can test it by running the command &#8220;ansible-playbook playbook.yml&#8221;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ ansible-playbook playbook.yml 

PLAY &#x5B;Use vars from dbservers] ****************************************************************************************************************************************************************

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Most basic loop] ************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; (item=1) =&gt; {
    &quot;msg&quot;: 1
}
ok: &#x5B;localhost] =&gt; (item=2) =&gt; {
    &quot;msg&quot;: 2
}
ok: &#x5B;localhost] =&gt; (item=3) =&gt; {
    &quot;msg&quot;: 3
}
ok: &#x5B;localhost] =&gt; (item=4) =&gt; {
    &quot;msg&quot;: 4
}
ok: &#x5B;localhost] =&gt; (item=5) =&gt; {
    &quot;msg&quot;: 5
}

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
</pre></div>


<h2 class="wp-block-heading" id="h-loop-of-loops">Loop of loops</h2>



<p>In most use cases, you want to loop multiple tasks sequentially. The first thought is to use a block statement, but a block doesn&#8217;t accept a loop. The solution is to use &#8220;ansible.builtin.include_tasks&#8221; and loop on the task file.</p>



<p>The usage of loop_control is recommended to rename the loop_var name and not use &#8220;item&#8221;. See below the example with the &#8220;playbook.yml&#8221; and &#8220;loop.yml&#8221; files.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# playbook.yml
- name: Loop examples
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
  - set_fact:
      numbers: &#x5B;1,2,3,4,5]

  - name: loop multiple tasks with include_task
    ansible.builtin.include_tasks:
      file: loop.yml
    loop: &#039;{{ numbers }}&#039;
    loop_control:
      loop_var: number
</pre></div>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# loop.yml
- debug:
    msg: &#039;First task of loop.yml&#039;

- debug:
    var: number
</pre></div>


<p>The results of running the playbook should be as below.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ ansible-playbook playbook.yml

PLAY &#x5B;Loop examples] **************************************************************************************************************************************************************************

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;loop multiple tasks with include_task] **************************************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/loop.yml for localhost =&gt; (item=1)
included: /Users/kke/dbi/blog/ansible_loop/loop.yml for localhost =&gt; (item=2)
included: /Users/kke/dbi/blog/ansible_loop/loop.yml for localhost =&gt; (item=3)
included: /Users/kke/dbi/blog/ansible_loop/loop.yml for localhost =&gt; (item=4)
included: /Users/kke/dbi/blog/ansible_loop/loop.yml for localhost =&gt; (item=5)

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;First task of loop.yml&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 1
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;First task of loop.yml&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 2
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;First task of loop.yml&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 3
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;First task of loop.yml&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 4
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;First task of loop.yml&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 5
}

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=16   changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
</pre></div>


<h2 class="wp-block-heading" id="h-conditional-on-include-tasks">Conditional on include_tasks</h2>



<p>Adding a condition on the include_tasks is only evaluated once. It means that if the condition is True, it will iterate until the end even though the condition might become False, which is the intended result of the condition of include_tasks. See the following example.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# playbook.yml
- name: Loop examples
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:

  - set_fact:
      numbers: &#x5B;1,2,3,4,5]

  - set_fact: 
      continue_task: true

  - when: continue_task == true
    name: When on include task only (evaluted at the beginning)
    ansible.builtin.include_tasks:
      file: condition.yml
    loop: &#039;{{ numbers }}&#039;
    loop_control:
      loop_var: number
</pre></div>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# condition.yml
- debug:
    var: number

- when: number &gt;= 3
  set_fact:
    continue_task: false

- debug:
    msg: &#039;current number: {{ number }}. Condition.yml running on number &lt; 3&#039;
</pre></div>


<p>Results of the running playbook.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ ansible-playbook playbook.yml

PLAY &#x5B;Loop examples] **************************************************************************************************************************************************************************

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;When on include task only (evaluted at the beginning)] **********************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=1)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=2)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=3)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=4)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=5)

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 1
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 1. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 2
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 2. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 3
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 3. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 4
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 4. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 5
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 5. Condition.yml running on number &lt; 3&quot;
}

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=20   changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
</pre></div>


<p>If you want the condition to apply on every task, you can either add the statement &#8220;when&#8221; on every task in &#8220;condition.yml&#8221;, or you can use the statement &#8220;apply&#8221; on &#8220;ansible.builtin.include_tasks&#8221;. Choose the solution that suits the best for the role or tasks you are writing.</p>



<p>The below example will use the statement &#8220;apply&#8221;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# playbook.yml

- name: Loop examples
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:

  - set_fact:
      numbers: &#x5B;1,2,3,4,5]

  - set_fact: 
      continue_task: true

  - name: When applied on all tasks included (evaluated each time)
    ansible.builtin.include_tasks:
      file: condition.yml
      apply:  
        when: continue_task == true
    loop: &#039;{{ numbers }}&#039;
    loop_control:
      loop_var: number
</pre></div>


<p>Output of the playbook. The results should print the number until &#8220;3&#8221; but not the second debug message  &#8220;current number 3. Condition.yml running on number &lt; 3&#8221;. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ ansible-playbook playbook.yml

PLAY &#x5B;Loop examples] **************************************************************************************************************************************************************************

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;When applied on all tasks included (evalued each time)] *********************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=1)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=2)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=3)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=4)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=5)

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 1
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 1. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 2
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 2. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 3
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=13   changed=0    unreachable=0    failed=0    skipped=9    rescued=0    ignored=0   
</pre></div>


<h2 class="wp-block-heading" id="h-advanced-loop-do-tasks-until-succeed-or-fail-at-xth-attempt">Advanced loop: do tasks until succeed, or fail at Xth attempt</h2>



<p>In this last example, I want to make a loop of tasks. The problem is that multiple tasks may fail for multiple reasons (network, unavailability of external services, awaiting process from external services, etc.). Therefore I want to retry the task at least 5th times, before exiting the playbook run. </p>



<p>The example will include a commented task, to provide a more concrete real use case example. The real example is the following. Fetch the ID of an item from an external service, use this ID to fetch its status to the external service, and only proceed if the item status is completed (successful, completed) or fail the task if it returns a failure state. The task may fail on the fetch of ID (due to network issues or unavailability of the external service) and on the status fetching if it is still ongoing, which will result in retrying the whole task. Note that it is not completely optimized to make it simpler to understand and read (for example we could ignore the fetching of ID if it was already gotten in a previous iteration).</p>



<p>For the simplicity of the setup, a simple condition on &#8220;number&#8221; is used instead, to showcase the retry of tasks and failure of the play. It causes the playbook&#8217;s execution to fail if the number is above 3.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# playbook.yml

- name: Loop examples
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:

  - set_fact:
      numbers: &#x5B;1,2,3,4,5]

  - set_fact: 
      continue_task: true


  - name: Example of a complex loop. Loop on number, do function(number) until suceed, or fail at the fifth attempt
    ansible.builtin.include_tasks:
      file: function.yml
    loop: &#039;{{ numbers }}&#039;
    loop_control:
      loop_var: number
  
</pre></div>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
# function.yml
- block:
    - when: init_count | default(true)
      ansible.builtin.set_fact:
        retry_count: 0

    - debug:
        msg: &#039;Current number is {{ number }}, and current retry count is {{ retry_count }}&#039;

    # Do an action, use the result to do another action or checks (for example a wget, curl, or another request to get an ID)
    # For the simplicity of the example, I simply do an echo
    - name: API - get ID
      ansible.builtin.shell: &#039;echo {{ number }}&#039;
      register: _api_result

    # Use the result from the precedent task
    - name: Use the ID to check another API if process is succesful 
      ## An exemple of a use case, using the id
      # ansible.builtin.uri:
      #   url: &#039;https://example.com/status?id={{ _api_result.stdout }}&#039;
      #   method: GET
      #   status_code: 200
      # register: _check_status
      # until:
      #   - _check_status.json is defined
      #   - _check_status.json.status in &#x5B;&quot;SUCCESSFUL&quot;, &quot;COMPLETED&quot;, &quot;FAILURE&quot;]
      # failed_when: _check_status.json is not defined or _check_status.json.status in &#x5B;&quot;FAILURE&quot;]
      # delay: &#039;5&#039;
      # retries: &#039;3&#039;

      ## For the simplicity, I just used a failed_when on debug
      debug:
        msg: &#039;Testing that api result is a &gt; 3&#039;
      failed_when: _api_result.stdout|int &gt; 3

  rescue:
    - when: _check_status.json is defined and _check_status.json.status in &#x5B;&quot;FAILURE&quot;]
      name: Fail if process return Failure
      ansible.builtin.fail:
        msg: status failure

    - name: Fail Task in case of total failure after a certain amount of retry
      ansible.builtin.fail:
        msg: &quot;5 retries attempted, failed perform desired result&quot;
      when: retry_count | int &gt;= 5 

    # Pause the playbook if necessary.
    # - ansible.builtin.pause:
    #     seconds: &#039;5&#039;

    - name: Increment Retry Count
      ansible.builtin.set_fact:
        retry_count: &quot;{{ retry_count | int + 1 }}&quot;

    # Retry the function.yml and indicate to increment the counter.
    - name: Retry function
      ansible.builtin.include_tasks: function.yml
      vars:
        init_count: false
</pre></div>


<p>Result of the execution.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ ansible-playbook playbook.yml

PLAY &#x5B;Loop examples] **************************************************************************************************************************************************************************

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;When applied on all tasks included (evalued each time)] *********************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=1)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=2)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=3)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=4)
included: /Users/kke/dbi/blog/ansible_loop/condition.yml for localhost =&gt; (item=5)

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 1
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 1. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 2
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;current number: 2. Condition.yml running on number &lt; 3&quot;
}

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;number&quot;: 3
}

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
skipping: &#x5B;localhost]

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=13   changed=0    unreachable=0    failed=0    skipped=9    rescued=0    ignored=0   

kke@DBI-LT-KKE ansible_loop % ansible-playbook playbook.yml

PLAY &#x5B;Loop examples] **************************************************************************************************************************************************************************

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;set_fact] *******************************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Example of a complex loop. Loop on number, do function(number) until suceed, or fail at the fifth attempt] ******************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost =&gt; (item=1)
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost =&gt; (item=2)
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost =&gt; (item=3)
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost =&gt; (item=4)
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost =&gt; (item=5)

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 1, and current retry count is 0&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 2, and current retry count is 0&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 3, and current retry count is 0&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 4, and current retry count is 0&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;Fail if process return Failure] *********************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Fail Task in case of total failure after a certain amount of retry] *********************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Increment Retry Count] ******************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Retry function] *************************************************************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 4, and current retry count is 1&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;Fail if process return Failure] *********************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Fail Task in case of total failure after a certain amount of retry] *********************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Increment Retry Count] ******************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Retry function] *************************************************************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 4, and current retry count is 2&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;Fail if process return Failure] *********************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Fail Task in case of total failure after a certain amount of retry] *********************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Increment Retry Count] ******************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Retry function] *************************************************************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 4, and current retry count is 3&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;Fail if process return Failure] *********************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Fail Task in case of total failure after a certain amount of retry] *********************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Increment Retry Count] ******************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Retry function] *************************************************************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 4, and current retry count is 4&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;Fail if process return Failure] *********************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Fail Task in case of total failure after a certain amount of retry] *********************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Increment Retry Count] ******************************************************************************************************************************************************************
ok: &#x5B;localhost]

TASK &#x5B;Retry function] *************************************************************************************************************************************************************************
included: /Users/kke/dbi/blog/ansible_loop/function.yml for localhost

TASK &#x5B;ansible.builtin.set_fact] ***************************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;debug] **********************************************************************************************************************************************************************************
ok: &#x5B;localhost] =&gt; {
    &quot;msg&quot;: &quot;Current number is 4, and current retry count is 5&quot;
}

TASK &#x5B;API - get ID] ***************************************************************************************************************************************************************************
changed: &#x5B;localhost]

TASK &#x5B;Use the ID to check another API if process is succesful] ********************************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {
    &quot;msg&quot;: &quot;Testing that api result is a &gt; 3&quot;
}

TASK &#x5B;Fail if process return Failure] *********************************************************************************************************************************************************
skipping: &#x5B;localhost]

TASK &#x5B;Fail Task in case of total failure after a certain amount of retry] *********************************************************************************************************************
fatal: &#x5B;localhost]: FAILED! =&gt; {&quot;changed&quot;: false, &quot;msg&quot;: &quot;5 retries attempted, failed perform desired result&quot;}

PLAY RECAP ************************************************************************************************************************************************************************************
localhost                  : ok=42   changed=9    unreachable=0    failed=1    skipped=16   rescued=6    ignored=0   

</pre></div>


<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p>Loop is a necessity in IT, the base for automation. At first, it might be disorientating with Ansible, but it becomes quite easy to understand and use with a little practice. The <a href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html">official documentation</a> provided by Ansible also covers a lot and explains well the concept of loop, it will be your best friend in your journey to master Ansible.</p>



<h2 class="wp-block-heading" id="h-links">Links</h2>



<p>Ansible &#8211; <a href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html">Official documentation</a><br>Blog &#8211; <a href="https://www.dbi-services.com/blog/faster-ansible/">Faster Ansible</a><br>Blog &#8211; <a href="https://www.dbi-services.com/blog/ansible-automates-event-driven-lightspeed/">Ansible Automates – Event Driven &amp; Lightspeed</a><br>Blog &#8211; <a href="https://www.dbi-services.com/blog/specify-hosts-in-ansible-playbook-command-line/">Specify hosts in ansible-playbook command line</a><br>Blog &#8211; <a href="https://www.dbi-services.com/blog/ansible-event-driven-automation/">Ansible Event Driven Automation</a><br>Blog &#8211; <a href="https://www.dbi-services.com/blog/create-and-manage-ansible-execution-environments/">Create and manage Ansible Execution Environments</a></p>
<p>L’article <a href="https://www.dbi-services.com/blog/ansible-loops-a-guide-from-basic-to-advanced-examples/">Ansible loops: A guide from basic to advanced examples</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/ansible-loops-a-guide-from-basic-to-advanced-examples/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Using WebHooks with GitLab to launch external tools</title>
		<link>https://www.dbi-services.com/blog/using-webhooks-with-gitlab-to-launch-external-tools/</link>
					<comments>https://www.dbi-services.com/blog/using-webhooks-with-gitlab-to-launch-external-tools/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Mon, 27 May 2024 15:02:10 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[DevOps]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=33303</guid>

					<description><![CDATA[<p>by Alexandre Nestor Introduction WebHooks are HTTP callbacks that are triggered by events. In case of GitlLab these can be events like pushing some code, or creating an issue, or pushing some comments, create a merge request and so on. Somehow, is a way for an application to send data, trigger events, in real time [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/using-webhooks-with-gitlab-to-launch-external-tools/">Using WebHooks with GitLab to launch external tools</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>by Alexandre Nestor</p>



<h2 class="wp-block-heading" id="h-introduction">Introduction</h2>



<p>WebHooks are HTTP callbacks that are triggered by events. In case of GitlLab these can be events like pushing some code, or creating an issue, or pushing some comments, create a merge request and so on.</p>



<p>Somehow, is a way for an application to send data, trigger events, in real time to another application, even if the target application don&#8217;t ask for the data. </p>



<p>In this post I will explain how to add a WebHook to a project and how to execute some external code when en event is triggered.</p>



<p>I will use an <a href="https://flask.palletsprojects.com/">Flask</a> application, which will be triggered by GitLab WebHook.</p>



<h2 class="wp-block-heading" id="h-vm-configuration">VM configuration</h2>



<p>The test VM is created on the OCI cloud. It is a RedHat VM but that is not very important. </p>



<p>First I have to open the port <code>8200</code> (used  by Flask). RedHat use by default <code>firewalld</code> as firewall:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
# add 8200 port using tcp to the public zone
&#x5B;root@wb ~]# firewall-cmd --zone=public --add-port=8200/tcp
success
# list opened ports
&#x5B;root@wb ~]# firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: ens3
sources:
services: dhcpv6-client http ssh
ports: 8200/tcp
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
</pre></div>


<h2 class="wp-block-heading" id="h-the-flask-application">The Flask application </h2>



<p>For example I create a small <code>Flask</code> application which is waiting for an event on <code>8200</code> port. The easiest application is to retrieve the <code>HTTP</code> frame. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: python; title: ; notranslate">
# app.py 
from flask import Flask, request, abort
import json

app = Flask(__name__)

@app.route(&#039;/my_webhook&#039;, methods=&#x5B;&#039;POST&#039;])
def my_webhook():
    if request.method == &#039;POST&#039;:
        print(json.dumps(request.json, indent=2))
        return &#039;success&#039;, 200
    else:
        abort(400)

if __name__ == &#039;__main__&#039;:
    app.run(host=&#039;0.0.0.0&#039;, port=8200, debug=True)
</pre></div>


<p>The Flask will launch a http server which is listen on all interfaces (<code>0.0.0.0</code>) en on port <code>8200</code><br>The path to be used in the HTTP POST call is <code>my_webhook</code><br>Also I used the debug mode to get the most traces.<br>The Flask application is waiting a POST request (line 10). And answer <code>success</code>.<br>Otherwise, return <code>400</code> HTTP code.</p>



<h3 class="wp-block-heading" id="h-start-the-flask-application">Start the Flask application </h3>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
(venv) &#x5B;opc@wb ~]$ python app.py
 * Serving Flask app &#039;app&#039; (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on all addresses.
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://172.168.0.40:8200/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 295-390-357
</pre></div>


<h2 class="wp-block-heading" id="h-gitlab-configuration">GitLab configuration </h2>



<p>You must have the role  <code>maitainer</code> on your project, to have the rights to manage WebHooks.</p>



<p>Select Settings -&gt; WebHooks from the left Menu</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="435" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-45-1024x435.png" alt="" class="wp-image-33305" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-45-1024x435.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-45-300x127.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-45-768x326.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-45-1536x652.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-45-2048x869.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Once The WebHook was saved it can be tested. From <code>Test</code> button you can choose an event type to test. </p>



<p>Let&#8217;s try with a <code>push</code> event:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="583" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-46-1024x583.png" alt="" class="wp-image-33306" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-46-1024x583.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-46-300x171.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-46-768x438.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-46-1536x875.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-46-2048x1167.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>When the event is triggered in GitLab, the Flask application must dump the http frame on the screen: </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
 * Running on http://172.168.0.40:8200/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 295-390-357
.....
{
  &quot;object_kind&quot;: &quot;push&quot;,   
  &quot;event_name&quot;: &quot;push&quot;,
  &quot;before&quot;: &quot;6aa30335b09*****53183fb28bb84aada5cf&quot;,
  &quot;after&quot;: &quot;a48e187ef217a******62498378e5023d355&quot;,
  &quot;ref&quot;: &quot;refs/heads/main&quot;,
  &quot;checkout_sha&quot;: &quot;a48e187ef21********9490562498378e5023d355&quot;,
  &quot;message&quot;: null,
  &quot;user_id&quot;: 11774125,
  &quot;user_name&quot;: &quot;your username &quot;,
  &quot;user_username&quot;: &quot;your.usernam&quot;,
  &quot;user_email&quot;: &quot;&quot;,
  &quot;user_avatar&quot;: &quot;https://gitlab.com/uploads/-/system/user/avatar/11774125/avatar.png&quot;,
  &quot;project_id&quot;: 38878393,
  &quot;project&quot;: {
    &quot;id&quot;: 38878393,
    .....
</pre></div>


<p>On line 7 we can see the <code>push</code> event from GitLab.</p>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p>The use of WebHooks from GitLab to run external code represents another facility in DevOps tools.</p>



<p>Their implementation is easy and immediate. </p>



<p>An example of use case is the integration with Ansible Automation Controller, to run Ansible jobs.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/using-webhooks-with-gitlab-to-launch-external-tools/">Using WebHooks with GitLab to launch external tools</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/using-webhooks-with-gitlab-to-launch-external-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Faster Ansible</title>
		<link>https://www.dbi-services.com/blog/faster-ansible/</link>
					<comments>https://www.dbi-services.com/blog/faster-ansible/#respond</comments>
		
		<dc:creator><![CDATA[Middleware Team]]></dc:creator>
		<pubDate>Mon, 08 Apr 2024 15:03:21 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[YaK]]></category>
		<category><![CDATA[Performance Tuning]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[yak]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=32335</guid>

					<description><![CDATA[<p>Even if Ansible is powerful and flexible, it can be considered &#8220;slow&#8221;. It will be anyway faster, and more consistent, than doing the same steps manually. Nevertheless, we will experiment to make it even faster. I found few of them on the Internet, but rarely with figures of what to expect. In this blog post, [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/faster-ansible/">Faster Ansible</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Even if Ansible is powerful and flexible, it can be considered &#8220;slow&#8221;. It will be anyway faster, and more consistent, than doing the same steps manually. Nevertheless, we will experiment to make it even faster. I found few of them on the Internet, but rarely with figures of what to expect.</p>



<p>In this blog post, I will cover one of them and run different scenarios. We will also dig inside some internal mechanism used by Ansible.</p>



<h2 class="wp-block-heading" id="h-ssh-connections">SSH Connections</h2>



<p>Ansible is connection intensive as it opens, and closes, many ssh connections to the targeted hosts.</p>



<p>I found two possible ways to count the amount of connections from control to agents nodes:</p>



<ul class="wp-block-list">
<li>Add <code>-vvv</code> option to the ansible-playbook command.</li>



<li>grep audit.log file:</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
tail -f /var/log/audit/audit | grep USER_LOGIN
</pre></div>


<p>First option is really too much verbose, but I used it with the first playbook below to confirm the second option give the same count.</p>



<h2 class="wp-block-heading" id="h-simple-playbook">Simple Playbook</h2>



<p>To demonstrate that, let&#8217;s start with a very minimal playbook <span style="text-decoration: underline">without</span> fact gathering:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
---
- name: Test playbook
  hosts: all
  gather_facts: false
  pre_tasks:
    - name: &quot;ping&quot;
      ansible.builtin.ping:
...
</pre></div>


<p>This playbook triggered <strong>8</strong> ssh connections to the target host. If I enable facts gathering, count goes to <strong>14</strong> connections. Again, this is quiet a lot knowing that playbook does not do much beside check target is alive.</p>



<p>To summarize:</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td><strong>gather_facts</strong></td><td><strong>connections</strong></td><td><strong>timing (s)</strong></td></tr><tr><td>false</td><td>8</td><td>1,277</td></tr><tr><td>true</td><td>14</td><td>1,991</td></tr></tbody></table><figcaption class="wp-element-caption">ping playbook results</figcaption></figure>



<h2 class="wp-block-heading" id="h-what-are-all-these-connection-for">What are All These Connection For?</h2>



<p>To determine what are these connections doing, we can analyze verbose (really verbose!!) output of Ansible playbook without fact gathering.</p>



<h3 class="wp-block-heading" id="h-first-connection">First Connection</h3>



<p>First command of first connection is <code>echo ~opc &amp;&amp; sleep 0</code> which will return the home directory of ansible user.</p>



<h3 class="wp-block-heading" id="h-second">Second</h3>



<p>Second command is already scary:</p>



<pre class="wp-block-code"><code>( umask 77 &amp;&amp; mkdir -p "` echo /home/opc/.ansible/tmp `"&amp;&amp; mkdir "` echo /home/opc/.ansible/tmp/ansible-tmp-1712570792.3350322-23674-205520350912482 `" &amp;&amp; echo ansible-tmp-1712570792.3350322-23674-205520350912482="` echo /home/opc/.ansible/tmp/ansible-tmp-1712570792.3350322-23674-205520350912482 `" ) &amp;&amp; sleep 0</code></pre>



<ol class="wp-block-list">
<li>It set the umask for the commands to follow.</li>



<li>Create tmp directory to store any python script on target</li>



<li>In that directory, create a directory to store the script for this specific task</li>



<li>Makes this ssh command return the temporary variable with full path to the task script directory</li>



<li>sleep 0</li>
</ol>



<p>This one is mainly to ensure directory structure exists on the target.</p>



<h3 class="wp-block-heading" id="h-third">Third</h3>



<p>I will not paste this one here as it is very long and we can easily guess what it does with log just before:</p>



<pre class="wp-block-code"><code>Attempting python interpreter discovery</code></pre>



<p>Roughly, what it does, it tries many <a href="https://github.com/ansible/ansible/blob/61e18572bbee2431b71bcfdb8e5f74dacda98325/lib/ansible/config/base.yml#L1511C1-L1511C28" target="_blank" rel="noreferrer noopener">versions</a> of python.</p>



<h3 class="wp-block-heading" id="h-fourth">Fourth</h3>



<p>Next, it will run a python script with discovered python version to determine Operating System type and release.</p>



<h3 class="wp-block-heading" id="h-fifth">Fifth</h3>



<p>Fifth connection is actually a sftp command to copy module content (AnsiballZ_ping.py). AnsiballZ is a framework to embed module into script itself. This allows to be run modules with a single Python copy.</p>



<h3 class="wp-block-heading" id="h-seventh">Seventh</h3>



<p>This one is simply ensuring execution permission is set on temporary directory (ie. ansible-tmp-1712570792.3350322-23674-205520350912482) as well the python script (ie. AnsiballZ_ping.py).</p>



<h3 class="wp-block-heading" id="h-eighth-and-last-connection">Eighth and Last Connection</h3>



<p>Lastly, the execution of the ping module itself:</p>



<pre class="wp-block-code"><code>/usr/bin/python3.9 /home/opc/.ansible/tmp/ansible-tmp-1712570792.3350322-23674-205520350912482/AnsiballZ_ping.py &amp;&amp; sleep 0</code></pre>



<h2 class="wp-block-heading" id="h-optimization">Optimization</h2>



<p>To reduce the amount of connection, there is one possible option: Pipelining</p>



<p>To enable that, I simply need to add following line in ansible.cfg:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
pipelining = true
</pre></div>


<p>Or set <code>ANSIBLE_PIPELINING</code> environment variable to <mark class="has-inline-color has-luminous-vivid-orange-color">true</mark>.</p>



<p>How does it improve our playbook execution time:</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td><strong>gather_facts</strong></td><td><strong>connections</strong></td><td><strong>timing (s)</strong></td></tr><tr><td>false</td><td>3 (-62%)</td><td>0,473 (-63%)</td></tr><tr><td>true</td><td>4 (-71%)</td><td>1,275 (-36%)</td></tr></tbody></table><figcaption class="wp-element-caption">ping playbook results with pipelining</figcaption></figure>



<p>As we can see there is a significant reduction on the amount of ssh connections as well as a reduction of the playbook duration.</p>



<p>In this configuration, only 3 connections are made:</p>



<ul class="wp-block-list">
<li>python interpreter discovery (connection #3)</li>



<li>OS type discovery (connection #4)</li>



<li>python module execution (connection #8). AnsiballZ data is piped to that process.</li>
</ul>



<p>With the pipelining option, I also noticed that the Ansible temporary directory is not created.</p>



<p>Of course, we can&#8217;t expect such big speed-up on a real life playbook. So, we should do it now.</p>



<h2 class="wp-block-heading" id="h-deploy-weblogic-server-playbook">Deploy WebLogic Server Playbook</h2>



<p>Let&#8217;s use the WebLogic <a href="https://www.dbi-services.com/products/yak/">YaK</a> component to deploy a single WebLogic instance. It includes dbi service best practices, latest CPU patches and SSL configuration. The &#8220;normal&#8221; run takes 13 minutes 30 seconds when the pipelined run takes 12 minutes 13 seconds. This is <mark class="has-inline-color has-luminous-vivid-orange-color">10% faster</mark>.</p>



<p>This is nice, but not as good as previous playbook. Why is that? Because most of the time is not spent in ssh connections, but with actual work (running WebLogic installer, starting services, patching with OPatch, etc).</p>



<h2 class="wp-block-heading" id="h-what-next">What Next?</h2>



<p>With such results, you might wonder why isn&#8217;t it enabled by default? As per documentation, there is a limitation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>This can conflict with privilege escalation (become). For example, when using sudo operations you must first disable ‘requiretty’ in the sudoers file for the target hosts, which is why this feature is disabled by default.</p>
<cite><a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#ansible-pipelining">Ansible documentation</a></cite></blockquote>



<p>Until now, with all tests I have made, I never encountered that limitation. <strong>Did you?</strong></p>
<p>L’article <a href="https://www.dbi-services.com/blog/faster-ansible/">Faster Ansible</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/faster-ansible/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: www.dbi-services.com @ 2026-05-21 06:28:52 by W3 Total Cache
-->