Dynamically constructing and managing data structures is a crucial skill in Ansible automation, especially for tasks that require flexible and reusable configurations. In this article, we’ll explore this concept using a practical example: managing user accounts and groups on Linux systems.

Scenario: Dynamic User and Group Management

Imagine you have a user_list variable defining multiple users and their associated groups:

``yaml

user_list:

alice:

groups:

- admin

- developers

bob:

groups:

- developers

charlie:

groups:

- admin

- qa

`

Your goal is to:

  • Dynamically construct a user and group data structure.
  • Use this structure to manage user accounts and assign them to the appropriate groups.

The Ansible Playbook

Here’s how you can achieve this using set_fact and Jinja2 templating:

`yaml

  • name: Manage users dynamically

hosts: localhost

gather_facts: false

vars:

user_list:

alice:

groups:

- admin

- developers

bob:

groups:

- developers

charlie:

groups:

- admin

- qa

tasks:

- name: Construct user data

set_fact:

user_data: >

{%- set users = [] -%}

{%- for username, details in user_list.items() -%}

{{ users.append({'name': username, 'groups': details.groups}) }}

{%- endfor -%}

{{ users }}

- name: Debug constructed user data

debug:

var: user_data

- name: Create users and assign groups

ansible.builtin.user:

name: "{{ item.name }}"

groups: "{{ item.groups | join(',') }}"

loop: "{{ user_data }}"

`

Key Components

1. Data Construction with set_fact:

- The set_fact task dynamically builds user_data as a list of dictionaries, each containing:

- name: The username.

- groups: The list of groups.

Example output:

`yaml

user_data:

- name: alice

groups:

- admin

- developers

- name: bob

groups:

- developers

- name: charlie

groups:

- admin

- qa

`

2. Dynamic User Management:

- The ansible.builtin.user module iterates through user_data, creating users and assigning them to their respective groups. The groups field is converted to a comma-separated string using join(',').

3. Debugging:

- The debug task ensures that the user_data structure is correct before applying it.

Benefits of This Approach

  • Dynamic and Reusable:

- Adapts to changes in the input user_list without modifying the playbook logic.

  • Centralized Logic:

- Data construction is isolated in the set_fact` task, making the playbook easier to read and maintain.

  • Scalable:

- Handles any number of users and groups effortlessly.

  • Error Detection:

- Intermediate debugging ensures correctness before execution.

Conclusion

This app