<?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>Ansible &#8211; Made For Cloud</title>
	<atom:link href="https://madeforcloud.com/category/ansible/feed/" rel="self" type="application/rss+xml" />
	<link>https://madeforcloud.com</link>
	<description>Just another WordPress site</description>
	<lastBuildDate>Fri, 17 Jul 2026 04:54:18 +0000</lastBuildDate>
	<language>en-AU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>
	<item>
		<title>Molecule + Podman: Why It’s a Pain, but here&#8217;s an Example That Finally Works</title>
		<link>https://madeforcloud.com/2026/07/17/molecule-podman-why-its-a-pain-but-heres-an-example-that-finally-works/</link>
					<comments>https://madeforcloud.com/2026/07/17/molecule-podman-why-its-a-pain-but-heres-an-example-that-finally-works/#comments</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Fri, 17 Jul 2026 04:47:43 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Podman]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=318</guid>

					<description><![CDATA[If you’ve ever tried to run Molecule with Podman instead of Docker, you already know the truth: it never “just works.” Between dynamic inventories, missing Python interpreters, inconsistent container images, and Molecule’s habit of generating YAML that looks like it was written by a committee, you spend more time debugging the framework than testing your&#8230;<p><a class="more-link" href="https://madeforcloud.com/2026/07/17/molecule-podman-why-its-a-pain-but-heres-an-example-that-finally-works/" title="Continue reading &#8216;Molecule + Podman: Why It’s a Pain, but here&#8217;s an Example That Finally Works&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">If you’ve ever tried to run Molecule with Podman instead of Docker, you already know the truth: <strong>it never “just works.”</strong> Between dynamic inventories, missing Python interpreters, inconsistent container images, and Molecule’s habit of generating YAML that looks like it was written by a committee, you spend more time debugging the framework than testing your Ansible roles.</p>



<p class="wp-block-paragraph">This post walks through a <strong>real, working Molecule + Podman setup</strong> that supports <strong>multiple OS containers</strong> (Ubuntu, Rocky, CentOS Stream), uses <strong>Podman natively</strong>, and avoids the usual landmines:</p>



<ul class="wp-block-list">
<li>Python missing inside containers</li>



<li>Fact‑gathering failures</li>



<li>Molecule’s dynamic inventory breaking</li>



<li>Destroy steps leaving orphaned containers</li>



<li>Podman connection plugin quirks</li>
</ul>



<p class="wp-block-paragraph">If you’ve been fighting Molecule + Podman, this example will save you hours.</p>



<h2 class="wp-block-heading"><strong>The Problem: Molecule + Podman Is Not a Drop‑In Replacement for Docker</strong></h2>



<p class="wp-block-paragraph">Molecule’s Docker driver is mature. Podman’s driver… well&#8230; it isn’t.</p>



<p class="wp-block-paragraph">If you’re testing roles across multiple OSes, these problems multiply and you eventually just give up.</p>



<h2 class="wp-block-heading"><strong>The Working Example: Multi‑OS Molecule Scenario Using Podman</strong></h2>



<p class="wp-block-paragraph">Here’s the structure:</p>



<pre class="wp-block-code"><code>molecule/default/
├── create.yml
├── prepare.yml
├── converge.yml
├── verify.yml
├── destroy.yml
├── molecule.yml
└── requirements.yml
</code></pre>



<h3 class="wp-block-heading"><strong>Key idea:</strong></h3>



<p class="wp-block-paragraph">We <strong>start our own containers with podman </strong>and generate our <strong>own dynamic inventory</strong> inside <code>create.yml</code> and force Molecule to use it.</p>



<p class="wp-block-paragraph">This avoids Molecule’s broken Podman inventory plugin entirely.</p>



<h2 class="wp-block-heading"><strong>1. create.yml  Start Containers + Build Inventory</strong></h2>



<p class="wp-block-paragraph">We define our Podman containers, start them and force feed the inventory to molecule:</p>



<pre class="wp-block-code"><code>- name: Create
  hosts: localhost
  gather_facts: false
  vars:
    molecule_inventory:
      all:
        hosts: {}
        molecule: {}
    
    podman:
      - name: ubuntu-2404
        image: ubuntu:24.04
        command: /bin/bash
        tty: true
      - name: rocky9
        image: quay.io/rockylinux/rockylinux:9  
        command: /bin/bash
        tty: true
      - name: rocky10
        image: quay.io/rockylinux/rockylinux:10
        command: /bin/bash
        tty: true
      - name: cs9
        image: quay.io/centos/centos:stream9
        command: /bin/bash
        tty: true
  tasks:

    - name: Start Podman container(s)
      containers.podman.podman_container:
        name: "{{ item.name }}"
        image: "{{ item.image }}"
        state: started
        command: "{{ item.command | default(omit) }}"
        tty: "{{ item.tty | default(omit) }}"
      register: result
      loop: "{{ podman }}"
      loop_control:
        label: "{{ item.name }}"

    - name: Add container to molecule_inventory
      vars:
        inventory_partial_yaml: |
          all:
            children:
              molecule:
                hosts:
                  "{{ item.name }}":
                    ansible_connection: containers.podman.podman
      ansible.builtin.set_fact:
        molecule_inventory: &gt;
          {{ molecule_inventory | combine(inventory_partial_yaml | from_yaml, recursive=true) }}
      loop: "{{ podman }}"
      loop_control:
        label: "{{ item.name }}"

    - name: Dump molecule_inventory
      ansible.builtin.copy:
        content: |
          {{ molecule_inventory | to_yaml }}
        dest: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
        mode: "0600"

    - name: Force inventory refresh
      ansible.builtin.meta: refresh_inventory

    - name: Fail if molecule group is missing
      ansible.builtin.assert:
        that: "'molecule' in groups"
        fail_msg: |
          molecule group was not found inside inventory groups: {{ groups }}
      run_once: true # noqa: run-once&#91;task]

# we want to avoid errors like "Failed to create temporary directory"
- name: Validate that inventory was refreshed
  hosts: molecule
  gather_facts: false
  tasks:
    - name: Check uname
      ansible.builtin.raw: uname -a
      register: result
      changed_when: false

    - name: Display uname info
      ansible.builtin.debug:
        msg: "{{ result.stdout }}"
</code></pre>



<p class="wp-block-paragraph">It&#8217;s important to note, we added our containers to the inventory with the podman ansible_connection,  <code>containers.podman.podman_container</code>.</p>



<p class="wp-block-paragraph">Out<strong> inventory file </strong>under the covers looks like this:</p>



<pre class="wp-block-code"><code>all:
  children:
    molecule:
      hosts:
        ubuntu-2404:
          ansible_connection: containers.podman.podman
        rocky9:
          ansible_connection: containers.podman.podman
        rocky10:
          ansible_connection: containers.podman.podman
        cs9:
          ansible_connection: containers.podman.podman
</code></pre>



<p class="wp-block-paragraph">And note that we <strong>force Molecule to refresh</strong>:</p>



<pre class="wp-block-code"><code>- meta: refresh_inventory
</code></pre>



<p class="wp-block-paragraph">This is the magic step most examples miss.</p>



<h2 class="wp-block-heading"><strong>2. prepare.yml  Only Touch Ubuntu Containers</strong></h2>



<p class="wp-block-paragraph">Instead of conditionals, we use a <strong>host pattern</strong> and fix the missing python3 in the Ubuntu containers.</p>



<pre class="wp-block-code"><code>- hosts: "ubuntu*"
  gather_facts: false
  tasks:
    - name: Refresh apt cache Ubuntu container
      raw: |
        apt-get update
    - name: Install Python inside Ubuntu container
      raw: |
        apt-get install -y python3
    - name: Set correct Python interpreter for Ubuntu
      set_fact:
        ansible_python_interpreter: /usr/bin/python3

</code></pre>



<p class="wp-block-paragraph">Why this works:</p>



<ul class="wp-block-list">
<li>Molecule names your Ubuntu containers <code>ubuntu-*</code></li>



<li>No need for OS detection</li>



<li>No need for fact gathering</li>
</ul>



<p class="wp-block-paragraph">This is the cleanest possible prepare step.</p>



<h2 class="wp-block-heading"><strong>3. converge.yml  Run Tasks on All Containers</strong></h2>



<p class="wp-block-paragraph">This is the thing we&#8217;re testing</p>



<pre class="wp-block-code"><code>- hosts: all
  tasks:
    - shell: "cat /etc/*release*"
      register: result
    - debug: msg="{{ result }}"
</code></pre>



<p class="wp-block-paragraph">Simple, predictable, works across all OSes or well, it does what you want your code to do.</p>



<h2 class="wp-block-heading"><strong>4. verify.yml  OS‑Specific Assertions</strong></h2>



<pre class="wp-block-code"><code>- hosts: rocky9
  tasks:
    - assert:
        that:
          - "'dnf' in ansible_facts.packages"

- hosts: ubuntu-2404
  tasks:
    - assert:
        that:
          - "'apt' in ansible_facts.packages"
</code></pre>



<p class="wp-block-paragraph">This is how you validate OS‑specific behaviour cleanly, again, you do you.</p>



<h2 class="wp-block-heading"><strong>5. destroy.yml  Clean Container Removal</strong></h2>



<pre class="wp-block-code"><code>- name: Destroy molecule containers
  hosts: molecule
  gather_facts: false
  tasks:
    - name: Delete Podman container(s)
      containers.podman.podman_container:
        name: "{{ item }}"
        state: absent
      loop: "{{ groups&#91;'all'] | difference(&#91;'localhost']) }}"
      delegate_to: localhost

- name: Remove dynamic molecule inventory
  hosts: localhost
  gather_facts: false
  tasks:

    - name: Remove dynamic inventory file
      ansible.builtin.file:
        path: "{{ molecule_ephemeral_directory }}/inventory/molecule_inventory.yml"
        state: absent
</code></pre>



<p class="wp-block-paragraph">This avoids:</p>



<ul class="wp-block-list">
<li>Molecule leaving orphaned containers</li>



<li>Podman complaining about missing names</li>



<li>localhost being treated as a container</li>
</ul>



<h2 class="wp-block-heading"><strong>Why This Example Works When Others Don’t</strong></h2>



<h3 class="wp-block-heading"><strong>1. We bypass Molecule’s broken Podman inventory plugin</strong></h3>



<p class="wp-block-paragraph">We generate our own containers and inventory and force Molecule to use it.</p>



<h3 class="wp-block-heading"><strong>2. We use host patterns instead of OS detection</strong></h3>



<p class="wp-block-paragraph">Fact gathering doesn’t work until Python is installed.</p>



<h3 class="wp-block-heading"><strong>3. We use Podman’s connection plugin correctly</strong></h3>



<p class="wp-block-paragraph">Every host gets:</p>



<pre class="wp-block-code"><code>ansible_connection: containers.podman.podman
</code></pre>



<h3 class="wp-block-heading"><strong>4. We refresh inventory explicitly</strong></h3>



<p class="wp-block-paragraph">Without this, Molecule uses stale inventory.</p>



<h2 class="wp-block-heading"><strong>Conclusion: Molecule + Podman Is Painful &#8230;. But Fixable</strong></h2>



<p class="wp-block-paragraph">Podman is great. Molecule is great. Together? They fight.</p>



<p class="wp-block-paragraph">But with:</p>



<ul class="wp-block-list">
<li>explicit container creation</li>



<li>explicit inventory generation</li>



<li>host patterns</li>



<li>correct Podman connection plugin</li>



<li>correct Ubuntu prepare steps</li>



<li>correct destroy logic</li>
</ul>



<p class="wp-block-paragraph">You can run a <strong>multi‑OS Molecule test matrix</strong> under Podman reliably.</p>



<p class="wp-block-paragraph">This example proves it.</p>



<p class="wp-block-paragraph">If you’re building cloud automation, CI pipelines, or multi‑distro Ansible roles, this pattern will save you hours of frustration.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2026/07/17/molecule-podman-why-its-a-pain-but-heres-an-example-that-finally-works/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>The Two Patterns of Ansible Automation</title>
		<link>https://madeforcloud.com/2026/07/12/the-two-patterns-of-ansible-automation/</link>
					<comments>https://madeforcloud.com/2026/07/12/the-two-patterns-of-ansible-automation/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Sun, 12 Jul 2026 02:01:14 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Ansible Tower]]></category>
		<category><![CDATA[AWX]]></category>
		<category><![CDATA[ansible]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=313</guid>

					<description><![CDATA[Pipeline Execution vs Controller Execution Most teams don’t realize they’re choosing an automation architecture every time they run a playbook. They think they’re choosing a tool. They’re actually choosing a pattern. In my opinion, there are only two patterns that matter: Both work. Both fail. Both solve different problems. This article breaks down the patterns,&#8230;<p><a class="more-link" href="https://madeforcloud.com/2026/07/12/the-two-patterns-of-ansible-automation/" title="Continue reading &#8216;The Two Patterns of Ansible Automation&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">Pipeline Execution vs Controller Execution</h3>



<p class="wp-block-paragraph">Most teams don’t realize they’re choosing an automation architecture every time they run a playbook. They think they’re choosing a tool. They’re actually choosing a <strong>pattern</strong>.</p>



<p class="wp-block-paragraph">In my opinion, there are only two patterns that matter:</p>



<ol start="1" class="wp-block-list">
<li><strong>Pipeline‑Driven Execution</strong></li>



<li><strong>Controller‑Driven Execution (AWX/AAP)</strong></li>
</ol>



<p class="wp-block-paragraph">Both work. Both fail. Both solve different problems.</p>



<p class="wp-block-paragraph">This article breaks down the patterns, the guidance for using them, the anti‑patterns that cause outages, and when combining them actually makes sense.</p>



<h1 class="wp-block-heading">Pattern 1: Pipeline‑Driven Execution</h1>



<h3 class="wp-block-heading">“Automation lives in Git. Execution happens in CI.”</h3>



<p class="wp-block-paragraph">This is the alleged cloud‑native. I hate the term &#8216;cloud-native&#8217;, it&#8217;s just a modern pattern and can equally apply to on-premises, but I&#8217;ll use it as it&#8217;s become an industry norm.</p>



<p class="wp-block-paragraph">The pipeline:</p>



<ul class="wp-block-list">
<li>checks out the repo</li>



<li>runs <mark style="background-color:#eee" class="has-inline-color">ansible-playbook</mark> directly</li>



<li>injects secrets at runtime</li>



<li>uses inventories stored in Git</li>



<li>uses vars stored in Git</li>



<li>uses execution environments defined in Git</li>



<li>tests automation before deploying</li>



<li>runs deterministically and reproducibly</li>
</ul>



<p class="wp-block-paragraph">This pattern treats Ansible like any other automation tool: <strong>code &#8212;> test &#8212;> deploy &#8212;> verify</strong>.</p>



<h3 class="wp-block-heading">When to use it</h3>



<p class="wp-block-paragraph">Pipeline execution is the right answer when you need:</p>



<ul class="wp-block-list">
<li>reproducibility</li>



<li>portability</li>



<li>deterministic execution</li>



<li>Git as the single source of truth</li>



<li>cloud‑neutral automation</li>



<li>ephemeral runners</li>



<li>multi‑cloud or hybrid cloud</li>



<li>developer‑friendly workflows</li>



<li>automation that can run anywhere</li>
</ul>



<p class="wp-block-paragraph">This is the pattern used by &#8216;cloud‑native&#8217; teams, platform teams, and anyone who values <strong>Git truth over controller truth</strong>.</p>



<h3 class="wp-block-heading">Strengths</h3>



<ul class="wp-block-list">
<li>Fully reproducible</li>



<li>Fully portable</li>



<li>Fully version‑controlled</li>



<li>No hidden state</li>



<li>No GUI configuration</li>



<li>No controller dependency</li>



<li>Works in any CI/CD system</li>



<li>Works locally</li>



<li>Works in DR</li>



<li>Works in multi‑cloud</li>
</ul>



<h3 class="wp-block-heading">Weaknesses</h3>



<ul class="wp-block-list">
<li>Pipelines must understand environment boundaries</li>



<li>Pipelines must manage secrets securely</li>



<li>Pipelines must enforce guardrails</li>



<li>Pipelines can become too flexible</li>



<li>Pipelines can accidentally bypass governance</li>
</ul>



<p class="wp-block-paragraph">This is why enterprises often avoid this pattern, not because it’s wrong, but because they fear losing control. The fear is real.</p>



<h1 class="wp-block-heading">Pattern 2: Controller‑Driven Execution (AWX/AAP)</h1>



<h3 class="wp-block-heading">“Automation runs inside a governed platform.”</h3>



<p class="wp-block-paragraph">This is the typical enterprise pattern.</p>



<p class="wp-block-paragraph">AWX/AAP:</p>



<ul class="wp-block-list">
<li>pulls playbooks from Git</li>



<li>stores inventories</li>



<li>stores credentials</li>



<li>enforces RBAC</li>



<li>provides audit trails</li>



<li>standardizes execution environments</li>



<li>triggers automation from events</li>



<li>provides multi‑team visibility</li>
</ul>



<p class="wp-block-paragraph">This pattern treats Ansible as a <strong>governed automation platform</strong>, not a CLI tool.</p>



<h3 class="wp-block-heading">When to use it</h3>



<p class="wp-block-paragraph">Controller execution is the right answer when you need:</p>



<ul class="wp-block-list">
<li>RBAC</li>



<li>credential isolation</li>



<li>auditability</li>



<li>inventory synchronization</li>



<li>standardized execution environments</li>



<li>event‑driven automation</li>



<li>multi‑team governance</li>



<li>compliance and regulatory controls</li>
</ul>



<p class="wp-block-paragraph">This is the pattern used by large enterprises, regulated industries, and teams with strict governance requirements.</p>



<h3 class="wp-block-heading">Strengths</h3>



<ul class="wp-block-list">
<li>Centralized governance</li>



<li>Centralized credentials</li>



<li>Centralized inventory</li>



<li>Centralized audit</li>



<li>Standardized execution environments</li>



<li>Event‑driven automation</li>



<li>Multi‑team visibility</li>



<li>Strong guardrails</li>
</ul>



<h3 class="wp-block-heading">Weaknesses</h3>



<ul class="wp-block-list">
<li>Hidden state</li>



<li>GUI configuration</li>



<li>Non‑portable automation</li>



<li>Not fully reproducible</li>



<li>Not fully version‑controlled</li>



<li>Controller dependency</li>



<li>Harder to test automation before deployment</li>



<li>Harder to run automation outside AWX</li>
</ul>



<p class="wp-block-paragraph">This is why &#8216;cloud‑native&#8217; teams avoid this pattern, not because it’s wrong, but because it’s not portable.</p>



<h1 class="wp-block-heading">Anti‑Patterns</h1>



<p class="wp-block-paragraph">These are the patterns that break automation, cause outages, and create drift.</p>



<h2 class="wp-block-heading">Anti‑Pattern 1: AWX as the “automation store”</h2>



<p class="wp-block-paragraph">Pipelines call AWX job templates as if AWX stores automation.</p>



<p class="wp-block-paragraph">It doesn’t.</p>



<p class="wp-block-paragraph">AWX stores <strong>configuration</strong>, not automation.</p>



<p class="wp-block-paragraph">Automation lives in Git. AWX overrides Git with controller‑side state.</p>



<p class="wp-block-paragraph">This creates split‑brain automation, if you think git is the source of truth, surprise, it isn&#8217;t.</p>



<h2 class="wp-block-heading">Anti‑Pattern 2: Pipelines storing credentials</h2>



<p class="wp-block-paragraph">This is a governance failure.</p>



<p class="wp-block-paragraph">Pipelines should inject secrets at runtime, not store them.</p>



<p class="wp-block-paragraph">AWX should manage credentials. Actually, just about anything else should manage credentials.</p>



<h2 class="wp-block-heading">Anti‑Pattern 3: AWX storing inventories that drift from Git</h2>



<p class="wp-block-paragraph">Inventories should be version‑controlled.</p>



<p class="wp-block-paragraph">If AWX is the inventory source of truth, Git is no longer authoritative.</p>



<p class="wp-block-paragraph">This breaks reproducibility.</p>



<h2 class="wp-block-heading">Anti‑Pattern 4: Pipelines bypassing RBAC</h2>



<p class="wp-block-paragraph">If pipelines can run any playbook against any host using any credential, you’ve lost governance.</p>



<p class="wp-block-paragraph">This is dangerous in enterprise environments.</p>



<h2 class="wp-block-heading">Anti‑Pattern 5: AWX storing controller‑side vars that override Git</h2>



<p class="wp-block-paragraph">This creates non‑deterministic execution.</p>



<p class="wp-block-paragraph">Playbooks behave differently depending on controller configuration.</p>



<p class="wp-block-paragraph">This breaks portability.</p>



<h1 class="wp-block-heading">Does the Combination Make Sense?</h1>



<p class="wp-block-paragraph">Here’s the part most teams miss:</p>



<p class="wp-block-paragraph"><strong>The two patterns are not mutually exclusive.</strong> <strong>They are complementary.</strong></p>



<h1 class="wp-block-heading">The Practical Guidance</h1>



<h2 class="wp-block-heading">Use pipeline execution when:</h2>



<ul class="wp-block-list">
<li>automation must be reproducible</li>



<li><strong>automation must be portable</strong></li>



<li>automation must be version‑controlled</li>



<li><strong>automation must run anywhere</strong></li>



<li><strong>automation must be tested before deployment</strong></li>
</ul>



<h2 class="wp-block-heading">Use controller execution when:</h2>



<ul class="wp-block-list">
<li>governance matters</li>



<li>RBAC matters</li>



<li>credential isolation matters</li>



<li>auditability matters</li>



<li>inventory sync matters</li>



<li>event‑driven automation matters</li>
</ul>



<h2 class="wp-block-heading">Use both when:</h2>



<ul class="wp-block-list">
<li>you need reproducibility <strong>and</strong> governance</li>



<li>you need portability <strong>and</strong> control</li>



<li>you need Git truth <strong>and</strong> environment truth</li>



<li>you need cloud‑native execution <strong>and</strong> enterprise guardrails</li>
</ul>



<p class="wp-block-paragraph">This is the two‑layer architecture.</p>



<h1 class="wp-block-heading">Final Takeaway</h1>



<p class="wp-block-paragraph">There are two patterns.</p>



<p class="wp-block-paragraph">Trying to make one do both is how teams create drift, outages, and brittle automation.</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2026/07/12/the-two-patterns-of-ansible-automation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ansible Data Manipulation with Modules</title>
		<link>https://madeforcloud.com/2026/06/06/ansible-data-manipulation-with-a-module/</link>
					<comments>https://madeforcloud.com/2026/06/06/ansible-data-manipulation-with-a-module/#comments</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Sat, 06 Jun 2026 01:45:00 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Proxmox]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=294</guid>

					<description><![CDATA[Ansible loves to pretend that YAML is a programming language. It isn’t. And every engineer who has ever tried to munge data inside a playbook knows the pain. You have filters everywhere, Jinja spaghetti, and tasks that look like they were written during a period of sleep deprivation. How do I know, guilty as charged.&#8230;<p><a class="more-link" href="https://madeforcloud.com/2026/06/06/ansible-data-manipulation-with-a-module/" title="Continue reading &#8216;Ansible Data Manipulation with Modules&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ansible loves to pretend that YAML is a programming language. It isn’t. And every engineer who has ever tried to munge data inside a playbook knows the pain. You have filters everywhere, Jinja spaghetti, and tasks that look like they were written during a period of sleep deprivation. How do I know, guilty as charged.</p>



<h3 class="wp-block-heading"><strong>Just to be clear, what i&#8217;m saying is YAML and Jinja are not intended to be a Data‑Processing stack</strong></h3>



<p class="wp-block-paragraph">The usual Ansible talking point is that “Ansible is declarative, not imperative”. Sure. But then I immediately need to write imperative logic in Jinja because the playbook layer simply isn’t built for data transformation. I do it, you do it, we&#8217;ve all done it.  It&#8217;s usually quick, and depending on the use case, relatively painless, but at some point, you&#8217;ve taken it too far. I know I have, so I&#8217;m talking about it now. Automation should be declarative, but you need imperative to achieve declarative &#8211; stuff needs to be queried and computed to achieve a desired state. Ansible provides all the batteries needed to achieve this.</p>



<p class="wp-block-paragraph">The pain points you run into are real, you end up with:</p>



<ul class="wp-block-list">
<li>Complex list/dict transformations</li>



<li>Conditional logic that becomes unreadable in YAML</li>



<li>Repeated filter chains that break the moment your data shape changes</li>



<li>Playbooks that become untestable because the logic is embedded in templates</li>
</ul>



<p class="wp-block-paragraph"><strong>Fundamentally</strong>, <strong>if you’re doing anything non‑trivial with data, YAML based tasks and Jinja are the wrong tool.</strong></p>



<h3 class="wp-block-heading">Ansible does have a s<strong>olution: Move the logic into a module</strong></h3>



<p class="wp-block-paragraph"><strong>Stop abusing filters, YAML, Jinja. Write a module.</strong> <strong>If your playbook contains more than two chained filters, or chains of set_facts, or complex jinja, you probably should have written a module.</strong></p>



<p class="wp-block-paragraph">I&#8217;ve written my fair share of modules, they aren&#8217;t that difficult, but my mindset has always gone to the convoluted set_fact, conditional, filter, jinja fiasco &#8211; because somehow it seems easier at the time. Perhaps it is when you&#8217;re trying to capture that initial thought process. But at some point, you need to give yourself a reality check, and maybe it&#8217;s just simpler to start with modules than convert later. That&#8217;s the thought experiment i&#8217;d like you to consider. A module gives you:</p>



<ul class="wp-block-list">
<li>Real programming constructs</li>



<li>Real error handling</li>



<li>Real testability</li>



<li>Real maintainability</li>



<li>Real version control and reuse</li>
</ul>



<h3 class="wp-block-heading"><strong>Why is this a better Pattern</strong>?</h3>



<p class="wp-block-paragraph"><strong>Input validation &#8211; YAML doesn’t. Playbooks don&#8217;t, (don&#8217;t say assert to me as i&#8217;ve abused that as well).  Jinja definitely doesn’t.</strong></p>



<p class="wp-block-paragraph">Modules let you validate input before you do your thing with it.</p>



<p class="wp-block-paragraph"><strong>Modules are testable</strong></p>



<p class="wp-block-paragraph">You can unit test a module. You cannot unit test a Jinja filter chain inside a task, and when your processing is a sequence of knitted tasks full of set_facts and recursive playbook calls, you&#8217;ve crossed the line into prayer-based testing.</p>



<p class="wp-block-paragraph"><strong>Modules are reusable across roles and playbooks</strong></p>



<p class="wp-block-paragraph">Copy‑pasting filter chains, or jinja compute, or those wonderful blocks of set_facts and conditionals is how outages happen.</p>



<p class="wp-block-paragraph"><strong>Modules reduce cognitive load</strong></p>



<p class="wp-block-paragraph">A 20‑line Python function is easier to understand than a 20‑line set_fact, conditional, jinja monstrosity.</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">The summary is <strong>Playbooks orchestrate.</strong> <strong>Modules compute.</strong> This is how Ansible should always have been used.</p>



<h3 class="wp-block-heading">So, what is my example problem and how do I fix it with modules. </h3>



<p class="wp-block-paragraph">My ansible role was running Proxmox backups in my home lab. I was only backing up systems in my lab that had been powered on since the last backup, either daily or weekly. My pve_backup role was doing all of the following in YAML:</p>



<ul class="wp-block-list">
<li>Multi‑node API discovery</li>



<li>Cross‑node VM enumeration</li>



<li>Tag parsing and normalization</li>



<li>Per‑VM filtering</li>



<li>Per‑VM state evaluation</li>



<li>Time‑window logic</li>



<li>Task‑history correlation</li>



<li>Backup triggering</li>



<li>UPID polling</li>



<li>Error handling</li>
</ul>



<p class="wp-block-paragraph">This is <strong>imperative logic</strong>. YAML + Jinja is <strong>not</strong> an imperative language. I had effectively built a Python program using a markup language.  Yay me!</p>



<p class="wp-block-paragraph">Based on my thought process that I describe above, I could identify many &#8216;code smells&#8217;:</p>



<p class="wp-block-paragraph"><strong>Excessive </strong><code>set_fact</code></p>



<p class="wp-block-paragraph">This is always a sign the playbook is doing computation, not orchestration.</p>



<p class="wp-block-paragraph"><strong>Nested loops + sub elements</strong></p>



<p class="wp-block-paragraph">This is a red flag that the data model is too complex for YAML.</p>



<p class="wp-block-paragraph"><strong>Repeated REST calls with identical headers</strong></p>



<p class="wp-block-paragraph">Modules handle this cleanly; playbooks do not.</p>



<p class="wp-block-paragraph"><strong>Per‑VM include files</strong></p>



<p class="wp-block-paragraph">This is a workaround for the fact that YAML cannot express real logic.</p>



<p class="wp-block-paragraph"><strong>State accumulation (</strong><code>vms_powered_on_last_week</code><strong>)</strong></p>



<p class="wp-block-paragraph">This is business logic, not orchestration.</p>



<p class="wp-block-paragraph"><strong>UPID polling in YAML</strong></p>



<p class="wp-block-paragraph">This is the worst possible place to do it.</p>



<p class="wp-block-paragraph"><strong>Debug statements everywhere</strong></p>



<p class="wp-block-paragraph">Because debugging YAML logic is hell.</p>



<p class="wp-block-paragraph">My role wasn&#8217;t bad in the terms that it did &#8216;work&#8217;. It’s simply doing something <strong>Ansible playbooks were never designed to do</strong>.</p>



<h3 class="wp-block-heading">How did I refactor this mess?</h3>



<p class="wp-block-paragraph">A good module model I use is:</p>



<p class="wp-block-paragraph"><strong>One module = one conceptual operation</strong></p>



<p class="wp-block-paragraph">My conceptual operations are:</p>



<p class="wp-block-paragraph"><strong>“Given a Proxmox cluster, return the list of VMIDs that should be backed up.”</strong></p>



<p class="wp-block-paragraph"><strong>“Given a VMID, run vzdump and wait for completion.”</strong></p>



<p class="wp-block-paragraph">That’s it. Two modules replace ~300 lines of gnarly YAML.</p>



<h3 class="wp-block-heading">Why this is objectively better (oh and I simply feel better about it)</h3>



<p class="wp-block-paragraph"><strong>Testable</strong></p>



<p class="wp-block-paragraph">You can unit‑test the module logic without running Ansible.</p>



<p class="wp-block-paragraph"><strong>Faster</strong></p>



<p class="wp-block-paragraph">Fewer tasks, leading to fewer forks, leading to fewer HTTP sessions.</p>



<p class="wp-block-paragraph"><strong>Maintainable</strong></p>



<p class="wp-block-paragraph">No more Jinja filter soup.</p>



<p class="wp-block-paragraph"><strong>Debuggable</strong></p>



<p class="wp-block-paragraph">You can print structured Python objects, not YAML hacks.</p>



<p class="wp-block-paragraph"><strong>Reusable</strong></p>



<p class="wp-block-paragraph">Other roles can use the same modules.</p>



<p class="wp-block-paragraph"><strong>Correct abstraction</strong></p>



<p class="wp-block-paragraph">Playbooks orchestrate. Modules compute.</p>



<h2 class="wp-block-heading">In summary</h2>



<p class="wp-block-paragraph">Think of your future self now <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">Train yourself to spot the above code smells sooner rather than later.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2026/06/06/ansible-data-manipulation-with-a-module/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Milestone 0.1.5 for Hyper-V ansible collection</title>
		<link>https://madeforcloud.com/2024/06/10/milestone-0-1-5-for-hyper-v-ansible-collection/</link>
					<comments>https://madeforcloud.com/2024/06/10/milestone-0-1-5-for-hyper-v-ansible-collection/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Mon, 10 Jun 2024 01:53:51 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Hyper-V]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=218</guid>

					<description><![CDATA[Milestone 0.1.5 has been achieved for the gocallag.hyperv collection which you can find in ansible galaxy at Ansible Galaxy &#8211; gocallag.hyperv. Release notes for 0.1.5 can be found over at github at Release Milestone 0.1.5 · gocallag/hyperv (github.com) Further information on upcoming milestones and their associated features can be found at Milestones &#8211; gocallag/hyperv (github.com)&#8230;<p><a class="more-link" href="https://madeforcloud.com/2024/06/10/milestone-0-1-5-for-hyper-v-ansible-collection/" title="Continue reading &#8216;Milestone 0.1.5 for Hyper-V ansible collection&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Milestone 0.1.5 has been achieved for the gocallag.hyperv collection which you can find in ansible galaxy at <a href="https://galaxy.ansible.com/ui/repo/published/gocallag/hyperv/">Ansible Galaxy &#8211; gocallag.hyperv</a>.</p>



<p class="wp-block-paragraph">Release notes for 0.1.5 can be found over at github at <a href="https://github.com/gocallag/hyperv/releases/tag/0.1.5">Release Milestone 0.1.5 · gocallag/hyperv (github.com)</a></p>



<p class="wp-block-paragraph">Further information on upcoming milestones and their associated features can be found at <a href="https://github.com/gocallag/hyperv/milestones">Milestones &#8211; gocallag/hyperv (github.com)</a></p>



<h2 class="wp-block-heading">What&#8217;s Changed</h2>



<ul class="wp-block-list">
<li>Added Feature to vm_info to provide search capability by Name </li>



<li>Closed Issue 2 &#8211; Feature to allow vm info to search by power state and names </li>



<li>BugFIX: Use the convertto-json , convertfrom-json to avoid loop in Exit-Json (limiting depth) </li>



<li>Closed Test Issue: Added verifier for vm_info module checks and cleanups</li>



<li>Closed Test Issue: Added basic asserts for switch_info testing via molecule</li>



<li>Closed Test Issue: Added verifier asserts for switch module as part of molecule testing </li>



<li>Closed Test Issue: Added assert based testing for vm module as part of molecule</li>



<li>BugFIX, Issue 12: bug vm info fails molecule verifier on running vm check </li>
</ul>



<p class="wp-block-paragraph">Feedback / Requests welcome.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2024/06/10/milestone-0-1-5-for-hyper-v-ansible-collection/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ansible Collection for hyperv management</title>
		<link>https://madeforcloud.com/2024/06/01/ansible-collection-for-hyperv-management/</link>
					<comments>https://madeforcloud.com/2024/06/01/ansible-collection-for-hyperv-management/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Sat, 01 Jun 2024 00:58:19 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Hyper-V]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=215</guid>

					<description><![CDATA[Just a quick blog post about a small ansible collection i&#8217;m developing to manage my hyperv lab vm&#8217;s. You can find it over at gocallag/hyperv (github.com)]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Just a quick blog post about a small ansible collection i&#8217;m developing to manage my hyperv lab vm&#8217;s.</p>



<p class="wp-block-paragraph">You can find it over at <a href="https://github.com/gocallag/hyperv">gocallag/hyperv (github.com)</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2024/06/01/ansible-collection-for-hyperv-management/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Fatal glibc error: CPU does not support x86-64-v2</title>
		<link>https://madeforcloud.com/2024/01/27/fatal-glibc-error-cpu-does-not-support-x86-64-v2/</link>
					<comments>https://madeforcloud.com/2024/01/27/fatal-glibc-error-cpu-does-not-support-x86-64-v2/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Sat, 27 Jan 2024 00:10:42 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[AWX]]></category>
		<category><![CDATA[Red Hat]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=163</guid>

					<description><![CDATA[I&#8217;m just writing this down in case anyone has a similar issue. As per Building Red Hat Enterprise Linux 9 for the x86-64-v2 microarchitecture level &#124; Red Hat Developer, back in 2020, AMD, Intel, Red Hat, and SUSE collaborated to define three x86-64 microarchitecture levels on top of the x86-64 baseline. The three microarchitectures group together CPU&#8230;<p><a class="more-link" href="https://madeforcloud.com/2024/01/27/fatal-glibc-error-cpu-does-not-support-x86-64-v2/" title="Continue reading &#8216;Fatal glibc error: CPU does not support x86-64-v2&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I&#8217;m just writing this down in case anyone has a similar issue.</p>



<p class="wp-block-paragraph">As per <a href="https://developers.redhat.com/blog/2021/01/05/building-red-hat-enterprise-linux-9-for-the-x86-64-v2-microarchitecture-level">Building Red Hat Enterprise Linux 9 for the x86-64-v2 microarchitecture level | Red Hat Developer</a>, back in 2020, AMD, Intel, Red Hat, and SUSE <a href="https://lists.llvm.org/pipermail/llvm-dev/2020-July/143289.html">collaborated</a> to define three x86-64 microarchitecture levels on top of the x86-64 baseline. The three microarchitectures group together CPU features roughly based on hardware release dates:</p>



<ul class="wp-block-list">
<li><strong>x86-64-v2</strong>&nbsp;brings support (among other things) for vector instructions up to Streaming SIMD Extensions 4.2 (SSE4.2)&nbsp; and Supplemental Streaming SIMD Extensions 3 (SSSE3), the POPCNT instruction (useful for data analysis and bit-fiddling in some data structures), and CMPXCHG16B (a two-word compare-and-swap instruction useful for concurrent algorithms).</li>



<li><strong>x86-64-v3</strong>&nbsp;adds vector instructions up to AVX2, MOVBE (for big-endian data access), and additional bit-manipulation instructions.</li>



<li><strong>x86-64-v4</strong>&nbsp;includes vector instructions from some of the AVX-512 variants.</li>
</ul>



<p class="wp-block-paragraph">This is a great idea and goal except when you have perfectly good old hardware that, while end-of-life is still working and you find it doesn&#8217;t support the new compile target.</p>



<p class="wp-block-paragraph">This nice little awk script from the fine folks over at <a href="https://unix.stackexchange.com/questions/631217/how-do-i-check-if-my-cpu-supports-x86-64-v2">stackexchange</a> will show you what microarchitecture your cpu supports by looking at the /proc/cpuinfo flags. I&#8217;ve included a local copy here and as you can see it&#8217;s pretty simple.</p>



<pre class="wp-block-code"><code>#!/usr/bin/awk -f

BEGIN {
    while (!/flags/) if (getline &lt; "/proc/cpuinfo" != 1) exit 1
    if (/lm/&amp;&amp;/cmov/&amp;&amp;/cx8/&amp;&amp;/fpu/&amp;&amp;/fxsr/&amp;&amp;/mmx/&amp;&amp;/syscall/&amp;&amp;/sse2/) level = 1
    if (level == 1 &amp;&amp; /cx16/&amp;&amp;/lahf/&amp;&amp;/popcnt/&amp;&amp;/sse4_1/&amp;&amp;/sse4_2/&amp;&amp;/ssse3/) level = 2
    if (level == 2 &amp;&amp; /avx/&amp;&amp;/avx2/&amp;&amp;/bmi1/&amp;&amp;/bmi2/&amp;&amp;/f16c/&amp;&amp;/fma/&amp;&amp;/abm/&amp;&amp;/movbe/&amp;&amp;/xsave/) level = 3
    if (level == 3 &amp;&amp; /avx512f/&amp;&amp;/avx512bw/&amp;&amp;/avx512cd/&amp;&amp;/avx512dq/&amp;&amp;/avx512vl/) level = 4
    if (level > 0) { print "CPU supports x86-64-v" level; exit level + 1 }
    exit 1
}</code></pre>



<p class="wp-block-paragraph">Running the awk script on my test system reveals :</p>



<pre class="wp-block-code"><code>$ ./testarch.awk
CPU supports x86-64-v1</code></pre>



<p class="wp-block-paragraph">The implications of this are annoying for me. I was trying to get <a href="https://github.com/ansible/awx">awx </a>to work on my little play system, but as the awx container is based on centos9 and compiled requiring at least x86-64-v2 then the awx container just wont start &#8211; yes I know there is more to awx than just this container, but it highlights the point nicely in the following command.</p>



<pre class="wp-block-code"><code>$ docker run --rm  ghcr.io/ansible/awx:latest
Fatal glibc error: CPU does not support x86-64-v2</code></pre>



<p class="wp-block-paragraph">This seems to have started somewhere after <a href="https://github.com/ansible/awx/issues/11879">awx release 19.5.0</a> </p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2024/01/27/fatal-glibc-error-cpu-does-not-support-x86-64-v2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ansible Tower provider for Cloudforms / ManageIQ</title>
		<link>https://madeforcloud.com/2020/01/14/ansible-tower-provider-for-cloudforms-manageiq/</link>
					<comments>https://madeforcloud.com/2020/01/14/ansible-tower-provider-for-cloudforms-manageiq/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Tue, 14 Jan 2020 00:54:14 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Ansible Tower]]></category>
		<category><![CDATA[Cloudforms]]></category>
		<category><![CDATA[ManageIQ]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=99</guid>

					<description><![CDATA[Always fun to strike problems in what should be the simplest things. I wanted to add Ansible Tower as a service into ManageIQ. Cloudforms would have a similar result. Following the very simple instructions at https://www.manageiq.org/docs/reference/latest/doc-Managing_Providers/miq/index I proceeded to add the new provider. It&#8217;s all pretty straight forward. Putting in the URL to my Tower&#8230;<p><a class="more-link" href="https://madeforcloud.com/2020/01/14/ansible-tower-provider-for-cloudforms-manageiq/" title="Continue reading &#8216;Ansible Tower provider for Cloudforms / ManageIQ&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Always fun to strike problems in what should be the simplest things.   I wanted to add Ansible Tower as a service into ManageIQ.  Cloudforms would have a similar result.</p>



<p class="wp-block-paragraph">Following the very simple instructions at <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://www.manageiq.org/docs/reference/latest/doc-Managing_Providers/miq/index" target="_blank">https://www.manageiq.org/docs/reference/latest/doc-Managing_Providers/miq/index</a> I proceeded to add the new provider.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://madeforcloud.com/wp-content/uploads/2020/01/index-1024x481.png" alt="" class="wp-image-100"/></figure>



<p class="wp-block-paragraph">It&#8217;s all pretty straight forward.  Putting in the URL to my Tower server I get greeted with a bunch of errors.</p>



<pre class="wp-block-preformatted"><strong>Credential validation was not successful:  {:headers=>{"server"=>"nginx", "date"=>"Mon, 13 Jan 2020  23:42:12 GMT", "content-type"=>"text/html; charset=utf-8",  "content-length"=>"3873", "connection"=>"close",  "vary"=>"Cookie, Accept-Language, Origin",  "content-language"=>"en", "x-api-total-time"=>"0.046s"</strong>
.
.
.
blah blah
.
.
<strong>status=>404</strong></pre>



<p class="wp-block-paragraph">By default ManageIQ and Cloudforms search for /api/v1 on the Tower server.  /api/v1 was deprecated and removed as of Ansible Tower 3.6, see <a href="https://docs.ansible.com/ansible-tower/latest/html/towerapi/conventions.html" target="_blank" rel="noreferrer noopener" aria-label=" (opens in a new tab)">https://docs.ansible.com/ansible-tower/latest/html/towerapi/conventions.html</a></p>



<p class="wp-block-paragraph">So, what is a person to do?   Hit the google.  Eventually I came across this bugzilla item <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://bugzilla.redhat.com/show_bug.cgi?id=1740860" target="_blank">https://bugzilla.redhat.com/show_bug.cgi?id=1740860</a> and it gave a hint as to just specifying the /api/v2 in the URL I gave to ManageIQ rather than just the base hostname.  eg.  https://blah&#8230;./api/v2</p>



<p class="wp-block-paragraph">Tried it,  it worked!  My credential validated and a provider refresh was automatically initiated and all my Ansible Tower templates and inventories were discovered correctly.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2020/01/14/ansible-tower-provider-for-cloudforms-manageiq/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Credentials for Ansible</title>
		<link>https://madeforcloud.com/2019/11/24/azure-credentials-for-ansible/</link>
					<comments>https://madeforcloud.com/2019/11/24/azure-credentials-for-ansible/#comments</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Sun, 24 Nov 2019 03:42:16 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Ansible Tower]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Red Hat]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=72</guid>

					<description><![CDATA[So, you need Ansible to connect to Azure. Congrats, Ansible is awesome for managing Azure resources. The Ansible team has already put together a scenario on how to integrate Ansible with Azure over at https://docs.ansible.com/ansible/latest/scenario_guides/guide_azure.html The section &#8216;Authenticating with Azure&#8216; sounds like the right place, but you can&#8217;t use your AD username / password from&#8230;<p><a class="more-link" href="https://madeforcloud.com/2019/11/24/azure-credentials-for-ansible/" title="Continue reading &#8216;Azure Credentials for Ansible&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">So, you need Ansible to connect to Azure.   Congrats,  Ansible is awesome for managing Azure resources.   The Ansible team has already put together a scenario on how to integrate Ansible with Azure over at <a href="https://docs.ansible.com/ansible/latest/scenario_guides/guide_azure.html">https://docs.ansible.com/ansible/latest/scenario_guides/guide_azure.html</a>   </p>



<p class="wp-block-paragraph">The section &#8216;<strong>Authenticating with Azure</strong>&#8216; sounds like the right place, but you can&#8217;t use your AD username / password from Ansible because you turned on 2FA &#8211;  You turned it on RIGHT?  So the option left to you is to create a Service Principal (SP).</p>



<p class="wp-block-paragraph"><strong>Note:  having 2FA on your account is what you should be doing, so don&#8217;t turn it off.</strong></p>



<p class="wp-block-paragraph">It&#8217;s quite simple to create a credential for Ansible to use when connecting to Azure.  Simply, fire up the Cloud Shell  (awesome feature BTW Microsoft) and create a Service Principal (SP).</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://madeforcloud.com/wp-content/uploads/2019/11/AzureCloudShell-1024x133.png" alt="" class="wp-image-73"/></figure>



<p class="wp-block-paragraph">But <span style="text-decoration: underline;">Hang On</span>,  what is a Service Principal?  The Ansible guide refers you to the Azure documentation over at <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal">https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal</a>  which you will read, and if you&#8217;re like me,  you&#8217;ll wonder what you actually just read.   Have no fear.  As I mentioned above you can use a simple Azure CLI command (via the Cloud Shell you just started) and create the Service Principal.     Think of the Service Principal as a credential an application (in this case Ansible) can use to access the Azure service(s).  </p>



<pre class="wp-block-code"><code>geoff@Azure:~$ az ad sp create-for-rbac --name svc-ansible-azure  # (optional if not specified one will be generated)  --password 'ALovelyComplexPasswor@'
Changing "svc-ansible-azure" to a valid URI of "http://svc-ansible-azure", which is the required format used for service principal names
Creating a role assignment under the scope of "/subscriptions/88888888-4444-4444-4444-cccccccccccc"
  Retrying role assignment creation: 1/36
  Retrying role assignment creation: 2/36
{
  "appId": "appid888-4444-4444-4444-cccccccccccc",
  "displayName": "svc-ansible-azure",
  "name": "http://svc-ansible-azure",
  "password": "password-4444-4444-4444-cccccccccccc",
  "tenant": "tenant88-4444-4444-4444-cccccccccccc"
}
geoff@Azure:~$</code></pre>



<p class="wp-block-paragraph">If you want to see what that command just did in the Azure portal, head over to the Azure Active Directory -> App registrations blade.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://madeforcloud.com/wp-content/uploads/2019/11/aad.png" alt="" class="wp-image-74"/></figure>



<p class="wp-block-paragraph">and then you can see the Service Principal you just created.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://madeforcloud.com/wp-content/uploads/2019/11/appregistrations.png" alt="" class="wp-image-75"/></figure>



<p class="wp-block-paragraph">So what do you do with the new credential.</p>



<p class="wp-block-paragraph">The Ansible Azure scenario guide has a section on what to do, however, it&#8217;s a bit too vague for me.</p>



<h4 class="wp-block-heading">Using Environment Variables</h4>



<p class="wp-block-paragraph">To pass service principal credentials via the environment, define the following variables:</p>



<ul class="wp-block-list"><li>AZURE_CLIENT_ID</li><li>AZURE_SECRET</li><li>AZURE_SUBSCRIPTION_ID</li><li>AZURE_TENANT</li></ul>



<p class="wp-block-paragraph">Azure has given me :</p>



<p class="wp-block-paragraph">&#8220;appId&#8221;: &#8220;appid888-4444-4444-4444-cccccccccccc&#8221;,<br>&#8220;displayName&#8221;: &#8220;svc-ansible-azure&#8221;,<br>&#8220;name&#8221;: &#8220;http://svc-ansible-azure&#8221;,<br>&#8220;password&#8221;: &#8220;password-4444-4444-4444-cccccccccccc&#8221;,<br>&#8220;tenant&#8221;: &#8220;tenant88-4444-4444-4444-cccccccccccc&#8221;</p>



<p class="wp-block-paragraph">For your sanity,  <br>AZURE_CLIENT_ID ==&gt; appId<br>AZURE_SECRET ==&gt; password<br>AZURE_TENANT ==&gt; tenant</p>



<p class="wp-block-paragraph">The remaining item, AZURE_SUBSCRIPTION_ID  is exactly that,  you can also get from the Cloud Shell as follows</p>



<pre class="wp-block-code"><code>geoff@Azure:~$ az account list
[
  {
    "cloudName": "AzureCloud",
    "id": "subscrip-4444-4444-4444-cccccccccccc
    "isDefault": true,
.
.
.</code></pre>



<p class="wp-block-paragraph">In this case AZURE_SUBSCRIPTION_ID ==> id ,   whichever id in your account that is valid for your use case.</p>



<p class="wp-block-paragraph">If you want to add these credentials into Ansible Tower, simply create a Credential of type <strong>Microsoft Azure Resource Manager </strong>and use the values you&#8217;ve deduced above.   Ansible Tower will automatically translate them into Environment Variables for your Tower template execution.</p>



<p class="wp-block-paragraph">Enjoy Ansible and Azure!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2019/11/24/azure-credentials-for-ansible/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Ansible, more than just SSH</title>
		<link>https://madeforcloud.com/2019/11/24/ansible-more-than-just-ssh/</link>
					<comments>https://madeforcloud.com/2019/11/24/ansible-more-than-just-ssh/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Sat, 23 Nov 2019 23:09:39 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<guid isPermaLink="false">https://madeforcloud.com/?p=69</guid>

					<description><![CDATA[I often see the statement Ansible manages clients using SSH or WinRM. While this is a true statement, it is also incomplete. Ansible currently has 26 connection types which you can find at https://docs.ansible.com/ansible/latest/plugins/connection.html For me personally, some of the other interesting connection types are : netconf network_cli netconf and network_cli are commonly used to&#8230;<p><a class="more-link" href="https://madeforcloud.com/2019/11/24/ansible-more-than-just-ssh/" title="Continue reading &#8216;Ansible, more than just SSH&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"> I <strong>often</strong> see the statement <strong><em>Ansible manages clients using SSH or WinRM</em></strong>.  While this is a true statement, it is also incomplete. </p>



<p class="wp-block-paragraph">Ansible currently has 26 connection types which you can find at <a href="https://docs.ansible.com/ansible/latest/plugins/connection.html">https://docs.ansible.com/ansible/latest/plugins/connection.html</a></p>



<p class="wp-block-paragraph">For me personally, some of the other interesting connection types are :  </p>



<ul class="wp-block-list"><li><a href="https://docs.ansible.com/ansible/latest/plugins/connection/netconf.html">netconf</a></li><li><a href="https://docs.ansible.com/ansible/latest/plugins/connection/network_cli.html">network_cli</a><br><br>netconf and network_cli are commonly used to perform<a href="https://docs.ansible.com/ansible/latest/network/index.html"> network device automation</a>.<br></li><li><a href="https://docs.ansible.com/ansible/latest/plugins/connection/psrp.html">psrp</a><br><br>psrp is similar to WinRM however it has the added benefit of being used via a proxy which is very useful when you have to consider bastian hosts.<br></li><li><a href="https://docs.ansible.com/ansible/latest/plugins/connection/vmware_tools.html">vmware_tools</a><br><br>vmware_tools is a relatively new addition to the ansible family and allows you to execute commands, transfer files to vSphere based systems without using the VM network.</li></ul>



<p class="wp-block-paragraph">Most Ansible developers will have also used connection type <strong>local </strong>in many of their playbooks, probably without realizing that it was a different connection type.</p>



<p class="wp-block-paragraph">Ansible is also extensible.  If you need to connect to something weird and wacky (but of great importance to you) then you can develop your own modules and connection plugin (or other sorts of plugins) &#8211; see <a href="https://docs.ansible.com/ansible/latest/plugins/plugins.html">https://docs.ansible.com/ansible/latest/plugins/plugins.html</a></p>



<p class="wp-block-paragraph">Ansible versatility doesn&#8217;t end there though and many newcomers to ansible don&#8217;t realise that you can also manage multiple clouds, container platforms and virtualisation platforms &#8211; it&#8217;s the Swiss Army knife of IT automation.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2019/11/24/ansible-more-than-just-ssh/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Adding a custom credential type in Ansible Tower for ServiceNOW</title>
		<link>https://madeforcloud.com/2019/11/08/adding-a-custom-credential-type-in-ansible-tower-for-servicenow/</link>
					<comments>https://madeforcloud.com/2019/11/08/adding-a-custom-credential-type-in-ansible-tower-for-servicenow/#respond</comments>
		
		<dc:creator><![CDATA[gocallag]]></dc:creator>
		<pubDate>Fri, 08 Nov 2019 04:07:31 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Ansible Tower]]></category>
		<category><![CDATA[ServiceNOW]]></category>
		<guid isPermaLink="false">http://madeforcloud.com/?p=53</guid>

					<description><![CDATA[It&#8217;s been one of those weeks and I needed to get some more experience with the ansible ServiceNOW modules, specifically within Ansible Tower. It looked pretty simple and in fact it really was quite simple. Ansible Tower neatly stores credentials within it &#8211; or externally if that fills you with joy. There isn&#8217;t a ServiceNow&#8230;<p><a class="more-link" href="https://madeforcloud.com/2019/11/08/adding-a-custom-credential-type-in-ansible-tower-for-servicenow/" title="Continue reading &#8216;Adding a custom credential type in Ansible Tower for ServiceNOW&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">It&#8217;s been one of those weeks and I needed to get some more experience with the ansible ServiceNOW  modules, specifically within Ansible Tower.   It looked pretty simple and in fact it really was quite simple.</p>



<p class="wp-block-paragraph">Ansible Tower neatly stores credentials within it &#8211; or externally if that fills you with joy.  There isn&#8217;t a ServiceNow credential type in Ansible Tower.   Undeterred, I thought I would use machine credentials, but tower has an annoying behavior of only allowing 1 instance of each credential type attached to a tower template and I am already using machine credentials in my template.   </p>



<p class="wp-block-paragraph">Fortunately on the left hand side of the tower ui there&#8217;s an entry labelled  <strong>credential types</strong> </p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="http://madeforcloud.com/wp-content/uploads/2019/11/credtype.png" alt="" class="wp-image-54"/></figure></div>



<p class="wp-block-paragraph">When creating the credential type you need to supply two (2) pieces of information.  The first piece is called the <strong>Input configuration</strong> &#8211; or what the fields look like on the web ui when you create a credential of this type and secondly, the <strong>Injector configuration</strong> which details what do do with thew credentials.</p>



<div class="wp-block-image"><figure class="aligncenter"><img decoding="async" src="http://madeforcloud.com/wp-content/uploads/2019/11/credconfig.png" alt="" class="wp-image-55"/></figure></div>



<p class="wp-block-paragraph">In my case,  the new credential type is called SNOW and i&#8217;m providing the instance name, username and password as part of the structure for this credential &#8211; via the Input configuration and then I detail that I want to store this data in environment variables that will be accessible from my playbook when run in tower.</p>



<h4 class="wp-block-heading">Input configuration</h4>



<pre class="wp-block-code"><code>fields:
  - id: instance
    type: string
    label: Instance
  - id: username
    type: string
    label: Username
  - id: password
    type: string
    label: Password
    secret: true
required:
  - instance
  - username
  - password
</code></pre>



<h4 class="wp-block-heading">Injector configuration</h4>



<pre class="wp-block-code"><code>env:
  SN_INSTANCE: '{{instance}}'
  SN_PASSWORD: '{{password}}'
  SN_USERNAME: '{{username}}'</code></pre>



<p class="wp-block-paragraph">The way you use them in your playbook is quite simple.  The following is a snippet of playbook showing that.</p>



<pre class="wp-block-code"><code>
   - name: Create an incident
     snow_record:
       username: '{{ lookup("env", "SN_USERNAME") }}'
       password: '{{ lookup("env", "SN_PASSWORD") }}'
       instance: '{{ lookup("env", "SN_INSTANCE") }}'
       state: present
       data:
         short_description: "This is a test incident opened by Ansible"
         severity: 3
         priority: 2
     register: new_incident</code></pre>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://madeforcloud.com/2019/11/08/adding-a-custom-credential-type-in-ansible-tower-for-servicenow/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
