Ansible Unlocking Advanced Automation Techniques

If you’ve already got the basics of Ansible down, it’s time to take your skills to the next level. Building on what we’ve covered in our previous articles:

Imagine being able to manage complex infrastructures effortlessly, secure your sensitive data with a few commands, and automate repetitive tasks like a pro. This guide is designed to transform you from an Ansible user to an Ansible master. Get ready to dive into dynamic host variables, encrypt sensitive data, craft custom modules, and explore advanced handlers. Let’s revolutionize the way you approach automation.

Fine Tuning Your Ansible Configuration

Starting with the basics, let’s ensure your Ansible setup is optimized for performance and efficiency. The ansible.cfg file is a configuration file for Ansible that defines settings and parameters controlling Ansible’s behavior. Optimizing this file can significantly enhance performance and efficiency.

Advanced Setup

To unlock the full potential, you can fine-tune your configuration as follows:

[defaults]
inventory = ./inventory/host.yaml
roles_path = ./roles
host_key_checking = False
log_path = ./log/ansible.log
remote_tmp = $HOME/.ansible/tmp
forks = 150
sudo_user = root
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
scp_if_ssh = True
  • inventory : Specifies the inventory file path.
  • roles_path : Defines the directory for roles.
  • host_key_checking : Disables host key checking for SSH.
  • log_path : Sets the path for Ansible logs.
  • remote_tmp : Sets the remote temporary directory path.
  • forks : Increases the number of parallel processes (tasks).
  • sudo_user : Specifies the sudo user.
  • ssh_args : Sets SSH arguments for controlling master connections and persistence.
  • scp_if_ssh : Enables SCP if using SSH.

After configuring the ansible.cfg file, you won’t need to manually specify the inventory path each time you run a playbook. The command will automatically use the default inventory path( ./inventory/host.yaml ).
Simply run your playbook with:

ansible-playbook playbooks/playbook.yml

Mastering Inventory Management

Effective inventory management is crucial for scaling your automation efforts. Let’s explore how to structure your inventory file for maximum flexibility.

Basic Setup

A basic inventory file might simply list hosts:

[city1]
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5

Advanced Setup

In an advanced setup, we define groups and variables dynamically:

[all:children]
city1
city2
[city1]
192.168.1.[1:5] #Range 1 to 5
[city2]
192.168.3.[1:15] #Range 1 to 15
[city1:vars]
ansible_user=Administrator
ansible_password=your_password
[city2:vars]
ansible_user=Administrator
ansible_password=your_password
  • all:children : Defines group hierarchy.
  • city1 & city2 : Specifies a range of IP addresses.
  • city:vars : Specifies variables specific to the group, like host, user, password and etc.

Securing Sensitive Data with Ansible Vault

When managing infrastructure, it’s essential to handle sensitive data, such as passwords and API keys securely. Ansible Vault is a feature that allows you to encrypt sensitive information within Ansible files, ensuring that your data remains protected.

Encrypting a File
To encrypt a file using Ansible Vault, you can use the ansible-vault encrypt command. This is particularly useful for securing files that contain sensitive information.

Command:

ansible-vault encrypt host.yaml

  • ansible-vault encrypt : This command initiates the encryption process.
  • host.yaml : The file that contains sensitive data you want to encrypt.

When you run this command, Ansible will prompt you to enter a password. This password will be required to decrypt the file in the future. The host.yaml file will be encrypted, making its contents unreadable without the password.

Editing an Encrypted File
Once a file is encrypted, you can still edit its contents securely using the ansible-vault edit command.

Command:

ansible-vault edit host.yaml
  • ansible-vault edit : This command allows you to open the encrypted file for editing and host.yaml is encrypted file you want to edit.\

When you run this command, Ansible will prompt you to enter the password you used during encryption. After entering the correct password, the file will be decrypted in memory, allowing you to make changes. Once you save and close the file, it will be re-encrypted automatically .

Running the Playbook

To run the playbook, use the ansible-playbook command and provide the password when prompted.

ansible-playbook playbook.yaml --ask-vault-pass\

When you run the playbook, Ansible will prompt you for the Vault password. After providing the correct password, Ansible will decrypt host.yaml and use the variables within it during the playbook execution.

Handling Errors Gracefully

In real world scenarios, tasks might fail. However, you don’t want one failed task to halt your entire playbook.

Example Code:

- name: Run a command and ignore errors
command: EXAMPLE_COMMAND
ignore_errors: yes
  • The ignore_errors option allows the task to continue even if the command fails.
  • This approach ensures that non-critical errors do not stop your automation workflows.

Separating Logic from Data

Keeping logic separate from data enhances maintainability and clarity in your playbooks.

Example Code:

- name: Gather OS facts
ansible.builtin.setup:
- name: Set webserver fact
set_fact:
webserver: "{{ 'apache2' if ansible_facts['os_family'] == 'Debian' else 'httpd' }}"
- name: Install web server
package:
name: "{{ webserver }}"
state: present
  • set_fact : Sets a variable based on the condition.
  • The package module installs the appropriate web server based on the OS family.

Delegating Tasks to Specific Hosts

In certain cases, you may need to delegate tasks to specific hosts.

Example Code:

- name: Run task on specific host
command: EXAMPLE_COMMAND
delegate_to: other_host
  • The delegate_to option allows running a task on a different host than the one in the playbook’s target.
  • This technique is particularly useful for load balancing and managing distributed systems.

Executing Conditional Tasks

Running tasks conditionally based on specific facts ensures precise control over your
automation.

Example Code:

- name: Gather OS facts
ansible.builtin.setup:
- name: Conditional task for RedHat
command: EXAMPLE_COMMAND
when: ansible_facts['os_family'] == 'RedHat'
- name: Conditional task when not Debian
command: EXAMPLE_COMMAND
when: ansible_facts['os_family'] != 'Debian
- name: Conditional task for specific OS version
command: EXAMPLE_COMMAND
when: ansible_facts['distribution_version'] == '20.04'
- name: Conditional task for 64-bit systems
command: EXAMPLE_COMMAND
when: ansible_facts['architecture'] == 'x86_64'
  • The when clause allows you to execute tasks based on conditions.
  • Conditional tasks enhance the flexibility and precision of your playbooks.

Debugging and Capturing Command Output in Ansible

Troubleshooting playbooks can be challenging, also you might need to execute commands and use their outputs in subsequent tasks, the debug module can help.

Example Code:

- name: Run command and capture output
command: EXAMPLE_COMMAND
register: command_output
- name: Display command output
debug:
var: command_output.stdout
  • The register keyword stores the output of a command, and debug displays it.
  • This technique allows you to make decisions based on the output of commands within your playbooks.

Collecting Custom Facts | Create Custom Modules

Sometimes, built-in facts are not enough, and you need custom facts to manage your infrastructure.

Create library directory and custom_memory_module.py in the library directory with such as below python codes:

from ansible.module_utils.basic import AnsibleModule
import psutil

def main():
module = AnsibleModule(argument_spec={})
memory_info = psutil.virtual_memory()._asdict()
module.exit_json(changed=False, ansible_facts={'memory_info': memory_info})
if __name__ == '__main__':
main()Explanation:
  • Create a custom module to gather additional facts and use them in your playbooks.

Use the custom module in playbook:

- name: Test custom memory module
hosts: localhost
tasks:
- name: Run custom memory module
custom_memory_module:
- name: Show memory info
debug:
var: ansible_facts.memory_info

Also in your project’s ansible.cfg file, add the library path as follows:

[defaults]
library = ./library

Automating Scheduled Tasks with Cron Jobs

Scheduling tasks is essential for recurring activities and maintenance.

Example Code:

- name: Create cron job
cron:
name: "example cron job"
minute: "0"
hour: "0"
job: "EXAMPLE_COMMAND"
  • The cron module allows you to define scheduled tasks easily.
  • Automating cron jobs ensures regular and timely execution of important tasks.

Pausing for User Input

Sometimes, you need user confirmation before proceeding with a task.

Example Code:

- name: Pause for user input
pause:
prompt: "Press Enter to continue"
  • The pause module stops execution and waits for user input, which is useful for manual confirmation.
  • This ensures critical steps are not automated without proper validation.

Configuring Network Interfaces

Ansible can be used to manage and configure network interfaces programmatically.

Example Code:

- name: Configure network interface
nmcli:
conn_name: eth0
ifname: eth0
type: ethernet
ip4: 192.168.1.100/24
  • The nmcli module allows you to configure network interfaces programmatically.
  • This is particularly useful for managing network configurations in large infrastructures.

Creating and Managing Directories & Files

Automating directory & file creation and management can save a lot of time.

Example Code:

- name: Create directories
file:
path: /path/to/directory
state: directory
mode: '0755
- name: Create files
file:
path: /path/to/file
state: touch
mode: '0755'
  • The file module can be used to create directories & files and set permissions.
  • This ensures that required directories & files are always created with the correct permissions.

Creating Files with User Provided Names

Sometimes, you need to create files based on user input during playbook execution.

Full Example Code

---
- name: Request filename from user
hosts: localhost
gather_facts: false
tasks:
- name: Prompt user for filename
pause:
prompt: "Please enter the filename"
register: user_input
- name: Create file with user-provided filename
file:
path: "~/{{ user_input.user_input }}"
state: touch
  • The pause module collects user input, and the file module creates the file with the provided name.
  • This technique is particularly useful for scenarios where user interaction is needed to define dynamic content.

Remember, the key to mastering Ansible is continuous learning and experimentation. Don’t be afraid to dive into the documentation, explore new modules, and adapt these techniques to fit your specific needs.