Skip to content

Automating Command Execution with Private IP Addresses in Ansible Using Facts and Shell Commands

In this guide, we’ll explore how to use ansible facts along with a simple shell command for obtaining the private IP address of each host within your inventory. This information can then be used dynamically when constructing commands that interact over SSH or another protocol at runtime on multiple hosts simultaneously using zip and join.

Here’s what you will learn in this guide:

Understanding the Playbook Structure with Facts and Commands for Private IP Addresses (Ansible)

Below is a detailed example of an Ansible role designed to fetch private network interface details using facts, which can then be used in subsequent tasks:

---
- hosts: all
  become: yes # This flag enables privilege escalation if necessary for certain commands.
  
  vars_prompt:   
      - name: "privateips"
          prompt: 'What are the private IPs?'
        
     roles/setup/_tasks/main.yml (This task fetches facts and assigns them to variables):
       tasks:
           # The setup of each host retrieves its network interface information, assigning it into a dictionary called `privateips`.
          - name: Get Facts with Private IP Address Information for Each Host  
            set_fact: 
                privateip: "{{ item.address }}" when loop through the facts from 'ansible_eth0' and get only non-loopback networks that have a netmask of `255.255.255.254` to filter out external IPs, which are usually assigned by DHCP
                item: "{{ ansible_default_ipv4 }}" when loop through facts from 'ansible_eth0' and extract each hostname with its respective non-loopback private network address using `ifconfig`. Here we take the first result (nonlocal) for simplicity. 
            loop: "{{ groups['all'] | default([])| map(attribute='hosts') }}"    # Ensures all hosts in inventory are iterated over if not specified otherwise                      with_items: ["em0"]        this line is redundant, and we can simplify using `with_items` for specific network interfaces
            loop_control: 
                label: "{{ item.name }}"    # Here 'item' refers to each host in the inventory list; use it as a reference within tasks below            
          - name: Debugging Tasks                    
              debug: var=privateip         (display facts for confirmation)  
           tags: fact               this tag allows us to run only when necessary or manually invoke using 'ansible-playbook your_role.yml --tags="fact"'     
      
    # Additional task in the same playbook file which executes a shell command on each host with their respective private IP addresses provided by Ansible facts: 
   - name: Command Execution Task (either SSH or similar remote commands)          
     block:              this keyword specifies that we're grouping tasks together under one logical unit.   
        # Here, 'privateips[item]|join(',') creates a comma-separated list of the private IP addresses from our facts variable `privateip` for each host using Ansible Jinja templating language (J2). 
     - name: Run your_command   this is where you define what command to run on every node; here, we're imagining a generic remote execution placeholder.   
       with_items: "{{ groups['all'] | default([])| map(attribute='hosts') }}" # Ensures all hosts in inventory are included for the loop          this line is redundant and can be removed when using `with_items` above; we have no specific list to iterate over here.
       run_once: true     assure that task runs only once, despite being part of a block used by multiple other tasks or roles   
        tags: cmnd               these are selectable for conditional execution with 'ansible-playbook -i hosts --extra-vars "tags=cmnd"' to execute commands.     

This playbook snippet demonstrates the structuring and grouping of Ansible facts extraction alongside shell command deployment, which will run only once per block across all host groups for both fact gathering (fact) tags usage:

  1. The set_fact task gathers private IP information from each node’s network interface details using ifconfig by mapping and filtering relevant data to create a dictionary with the name of hosts as keys corresponding to their non-loopback local networks (private addresses).
  2. Debug tasks are used for clarity, showing where facts can be reviewed in case they need verification or adjustments made before proceedthy final commands run on all nodes within your Ansible inventory file (groups['all']).
  3. The with_items is applied to the block task with a default list of hosts as if there was no specific subset, ensuring we’re looping over each host present in our playbook or role scope:
  4. Tags can be added for more granular control when executing tasks—this allows us to run this command-based part only under cmnd (command execution) conditions based on a variable that is set at runtime using the --extravars.
  5. For actual commands, let’s assume there’m an imaginary generic placeholder where we would input your desired remote operation or script; you can replace this with SSH tasks to manage each node according to their private IP address fetched from facts:
  6. The run_once keyword is crucial for preventing repetition, ensuring that the associated command block within a playbook doesn’t run multiple times and possibly interrupt our automation flow—perfect when we want it executed just once per target environment during one Ansible inventory loop execution:
  7. The role itself would not actually execute ansible-playbook but should be integrated into the larger configuration management system for scalable, dynamic IP address usage in your tasks without unnecessary redundancy and with full control over when this action happens using tags or playbooks variable assignments (not shown here).
  8. The use of Jinja templating language within Ansible ({{ privateips[item]|join(',') }}) makes the construction versatile, where privateip is a dynamically generated comma-separated list based on each host’s IP facts for remote execution tasks.
  9. By using ‘default([])’, we ensure that even without specifying hosts in command line arguments or variables during playbook invocation (e.g., --extravars "tags=cmnd"), the block will execute, making this flexible and reusable across inventory lists of varying sizes—whether empty, partial, full set, etc.:
    10.-endmarkup

This example assumes a certain level of familiarity with Ansible playbooks. If you’re new to automating infrastructure management or need assistance crafting your own tasks and roles based on these guidelines:

Remember this playbook structure is meant for illustrative purposes; tailor the tasks and tags appropriately according to your specific needs in automating device management or configuration deployments within a clustered environment using Ansible’s powerful, yet simple syntax—keeping it robust against diverse network topologies with varying requirements.

Additional Instru0ng Exercise: Create an Ansible playbook that dynamically uses the gathered facts to perform system health checks across multiple machines and generate a report on each host’s status regarding memory, CPU load average (MAD) using shell commands like free -m,vmstat | grep mem|awk '{print $3}'. Integrate these results into an Ansible handler that sends email notifications if any server’s usage exceeds 70% for all metrics. Ensure the playbook supports a tagging mechanism to execute this specific task only when triggered with --tags memory_monitoring, and use Jinja2 templating within tasks, including proper error handling where possible:

source=“produce Ansible role or template that meets these requirements using your current knowledge.”


Previous Post
Implications of Running Too Old Kubernetes k8s
Next Post
Setting Up Keycloak with MySQL via Docker Compose