From 29a10962a986dd74b4731703f75f0442f5b05bfe Mon Sep 17 00:00:00 2001
From: Adar Nimrod <nimrod@shore.co.il>
Date: Sun, 26 Apr 2015 00:13:34 +0300
Subject: [PATCH] - Add new entry (Ansible module in Python).

---
 content/ansible-python.rst | 70 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 content/ansible-python.rst

diff --git a/content/ansible-python.rst b/content/ansible-python.rst
new file mode 100644
index 0000000..445250e
--- /dev/null
+++ b/content/ansible-python.rst
@@ -0,0 +1,70 @@
+Using Ansible as a Python module
+================================
+
+:date: 2015-01-01
+:summary: Using Ansible as a Python module when playbooks are not enough.
+
+At my current employer we have several servers in production with various
+providers, some of them with multiple ip addresses. When configuring the
+firewall to allow traffic from other servers I reached for Ansible. The
+obvious solution was to use a nested loop, something like this: ::
+
+    - name: Allow other servers
+      ufw: rule=allow from_ip={{ item[1] }}
+      with_nested:
+        - all_hosts
+        - {{ item.ansible_all_ipv4_addresses }}
+
+However, this syntax is invalid (and other variations I tried). Using 'include'
+with 'with_items' is deprecated and I didn't manage to get it to work with
+registering variables as well. What I had left was programaticaly generating
+a playbook, but investigating further I found that Ansible can be imported as
+a Python module.
+
+Incorperating Ansible in Python
+-------------------------------
+
+To retrieve all of the ip addresses I'd ran the setup module to gather the
+information ::
+
+    from ansible.runner import Runner
+    struct = Runner (module_name='setup', pattern='all_hosts').run()
+
+Now we have a complex data structure that is the output of Ansible's fact
+gathering module. Running it in the interpeter and examining the structure is
+not hard at all and that is how I managed to write the following code to extract
+a list of all of our server's ip addresses. ::
+
+    ipaddresses = []
+    for host in struct['contacted']:
+        for ip in struct['contacted'][host]['ansible_facts']['ansible_all_ipv4_addresses']:
+            ipaddresses.append (ip)
+
+Putting that information to good use
+------------------------------------
+            
+Now that we have a list of the ip addresses, we can start running Ansible
+commands right from with Python (just like we did) or build a playbook by
+outputing a YAML file. I chose the latter. ::
+
+    from yaml import safe_dump
+    doc = {'all_ipv4': ipaddresses}
+    print (safe_dump (doc), file='vars.yml')
+
+This will create a vars.yml file with the all_ipv4 variable already defined
+there to be imported to any playbook and run. For example: ::
+
+    ---
+    - hosts: all_hosts
+      vars_files:
+        - vars.yml
+      tasks:
+        - name: Allow other servers
+          ufw: rule=allow from_ip={{ item }}
+          with_items: all_ipv4
+
+With this much little code we were able to query all of our hosts, extract the
+needed information and output it back to Ansible for further use. I see this as
+a product of the good decisions the Ansible developers choose early on (YAML,
+Python, SSH). As always, for any feedback you may have, `email me <mailto:
+nimrod@shore.co.il>`_.
-- 
GitLab