AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible product vs combine: List Cartesian Product & Dict Merging

By Luca Berton · Published 2024-01-01 · Category: troubleshooting

How to use Ansible product filter for list combinations and combine filter for dictionary merging. Cartesian products, dict merging, and data manipulation.

Introduction

In the realm of Ansible automation, managing complex data structures is often required. Two powerful tools, the product and combine filters, play distinct roles in manipulating lists and dictionaries, respectively. Understanding their use cases can significantly enhance your playbook efficiency.

See also: Ansible Split String: Filter Guide for CSV, Delimiters & Lists

product: Cartesian Product of Lists

The product filter in Ansible generates the Cartesian product of two or more lists, producing all possible combinations of their elements. This is particularly useful when you need to iterate over multiple sets of values to configure systems or environments.

Example: Configuring Hosts Across Environments

- hosts: localhost
  gather_facts: no
  tasks:
    - name: Generate combinations of environments and roles
      ansible.builtin.debug:
        msg: "{{ ['dev', 'test', 'prod'] | product(['web', 'db']) | list }}"

Output:

[
  ["dev", "web"], ["dev", "db"],
  ["test", "web"], ["test", "db"],
  ["prod", "web"], ["prod", "db"]
]

In this example, the product filter creates pairs of environments and roles, enabling scalable and dynamic configuration.

Use Case:

• Generating matrix-style combinations for testing or deployments. • Configuring multiple environments in one go.

combine: Merging Dictionaries

The combine filter allows you to merge multiple dictionaries into one, providing a way to consolidate configurations or override existing values.

Example: Merging Configuration Maps

- hosts: localhost
  gather_facts: no
  tasks:
    - name: Merge default and custom configurations
      ansible.builtin.debug:
        msg: "{{ {'default_key': 'default_value'} | combine({'custom_key': 'custom_value'}) }}"

Output:

{
  "default_key": "default_value",
  "custom_key": "custom_value"
}

You can use the combine filter to dynamically construct configurations or override default values with user-provided data.

Use Case:

• Building dynamic variables from defaults and user inputs. • Consolidating configurations for modular roles or playbooks.

See also: Ansible Transform JSON Data: Filters for Parsing & Manipulating JSON

Combining product and combine for Advanced Scenarios

Sometimes, these filters can be used together to manage complex scenarios, such as creating a dictionary of all possible role-environment pairs.

Example:

- hosts: localhost
  gather_facts: no
  tasks:
    - name: Create a dictionary of environment-role pairs
      ansible.builtin.debug:
        msg: >-
          {{
            dict(
              ['dev', 'test', 'prod'] | product(['web', 'db']) |
              map('join', '_') |
              map('extract', 0, ['web', 'db'])
            )
          }}

This advanced usage combines Cartesian products and dictionary manipulations to build intricate data structures for automation workflows.

Conclusion

Understanding the nuances of the product and combine filters empowers you to handle lists and dictionaries effectively in Ansible. Whether you're iterating over combinations or merging configurations, these tools are essential for robust and flexible automation playbooks.

For more detailed examples and real-world use cases, explore additional Ansible resources or check out my book Ansible by Example.

See also: Automating PostgreSQL Configuration with Ansible Setting Maximum Connections

product Filter (Lists)

Creates all combinations (cartesian product) of two or more lists:

- vars:
    colors: [red, blue]
    sizes: [S, M, L]
  debug:
    msg: "{{ colors | product(sizes) | list }}"
  # [['red','S'], ['red','M'], ['red','L'],
  #  ['blue','S'], ['blue','M'], ['blue','L']]

Real-world: Create directories for all app/env combos

- vars:
    apps: [frontend, backend, api]
    envs: [dev, staging, prod]
  ansible.builtin.file:
    path: "/opt/{{ item.0 }}/{{ item.1 }}"
    state: directory
  loop: "{{ apps | product(envs) | list }}"

Three-way product

- vars:
    regions: [us-east, eu-west]
    envs: [dev, prod]
    services: [web, api]
  debug:
    msg: "{{ regions | product(envs, services) | list }}"
  # All 8 combinations

combine Filter (Dictionaries)

Merges two or more dictionaries (later values win):

- vars:
    defaults:
      port: 8080
      debug: false
      log_level: info
    overrides:
      debug: true
      port: 9090
  debug:
    msg: "{{ defaults | combine(overrides) }}"
  # {port: 9090, debug: true, log_level: info}

Deep merge (recursive)

- vars:
    base:
      database:
        host: localhost
        port: 5432
      cache:
        ttl: 300
    override:
      database:
        host: db.prod.com
  debug:
    msg: "{{ base | combine(override, recursive=True) }}"
  # database: {host: db.prod.com, port: 5432}, cache: {ttl: 300}

Merge multiple dicts

- vars:
    a: { x: 1 }
    b: { y: 2 }
    c: { z: 3 }
  debug:
    msg: "{{ a | combine(b, c) }}"
  # {x: 1, y: 2, z: 3}

product vs combine

| Filter | Input | Output | Use Case | |--------|-------|--------|----------| | product | Lists | List of tuples | All combinations for looping | | combine | Dicts | Merged dict | Override defaults, merge configs |

| Filter | Description | |--------|-------------| | zip | Pair elements at same index | | union | Unique elements from both lists | | intersect | Elements in both lists | | difference | Elements in first but not second |

# zip pairs by index
- vars:
    keys: [name, age]
    vals: [Alice, 30]
  debug:
    msg: "{{ dict(keys | zip(vals)) }}"
  # {name: Alice, age: 30}

FAQ

How is product different from with_nested?

They're equivalent. product is the modern filter approach:

# Old style
with_nested:
  - [a, b]
  - [1, 2]

# Modern style loop: "{{ ['a','b'] | product([1,2]) | list }}"

Does combine overwrite or merge nested dicts?

By default, it overwrites. Use recursive=True for deep merge.

product — Cross-Product of Lists

- vars:
    environments: [dev, staging, prod]
    services: [web, api, worker]
  debug:
    msg: "{{ item.0 }}-{{ item.1 }}"
  loop: "{{ environments | product(services) | list }}"
# dev-web, dev-api, dev-worker, staging-web, staging-api, ...

combine — Merge Dictionaries

- vars:
    defaults:
      port: 80
      workers: 4
      debug: false
    overrides:
      port: 8080
      debug: true
  set_fact:
    config: "{{ defaults | combine(overrides) }}"
# { port: 8080, workers: 4, debug: true }

product Use Cases

Create User+Group Pairs

- vars:
    users: [alice, bob]
    groups: [docker, sudo]
  user:
    name: "{{ item.0 }}"
    groups: "{{ item.1 }}"
    append: true
  loop: "{{ users | product(groups) | list }}"
  become: true

Multi-Region Deployment

- vars:
    regions: [us-east-1, eu-west-1]
    services: [frontend, backend, cache]
  debug:
    msg: "Deploy {{ item.1 }} to {{ item.0 }}"
  loop: "{{ regions | product(services) | list }}"

Three-Way Product

- vars:
    envs: [dev, prod]
    apps: [web, api]
    actions: [deploy, monitor]
  debug:
    msg: "{{ item.0 }}/{{ item.1 }}/{{ item.2 }}"
  loop: "{{ envs | product(apps, actions) | list }}"

combine Use Cases

Environment-Specific Config

- vars:
    base_config:
      log_level: info
      port: 8080
      workers: 4
    prod_overrides:
      log_level: warn
      workers: 16
  set_fact:
    final: "{{ base_config | combine(prod_overrides) }}"

Deep Merge

- vars:
    dict1:
      database:
        host: localhost
        port: 5432
    dict2:
      database:
        host: db.prod.com
  set_fact:
    # Shallow (default) — replaces entire 'database' key
    shallow: "{{ dict1 | combine(dict2) }}"
    # { database: { host: db.prod.com } }  -- port is LOST!

# Deep merge — preserves nested keys deep: "{{ dict1 | combine(dict2, recursive=true) }}" # { database: { host: db.prod.com, port: 5432 } }

Merge Multiple Dicts

- set_fact:
    final: "{{ defaults | combine(group_vars) | combine(host_vars) | combine(extra_vars) }}"

Merge List of Dicts

- vars:
    config_layers:
      - { port: 80 }
      - { workers: 4 }
      - { debug: true }
  set_fact:
    merged: "{{ config_layers | combine }}"
    # { port: 80, workers: 4, debug: true }

Key Differences

| Feature | product | combine | |---------|-----------|-----------| | Input | Lists | Dictionaries | | Output | List of tuples | Merged dict | | Purpose | Cross-product (all combinations) | Merge/overlay | | Typical use | Nested loops | Config layering | | recursive | N/A | Deep merge option |

FAQ

product vs with_nested?

Same result — loop: "{{ a | product(b) }}" is the modern equivalent of with_nested: [a, b].

combine overwrites or merges?

By default, overwrites — later values replace earlier ones. Use recursive=true for deep merge of nested dicts.

Can I combine with list_merge?

# Control how lists are merged in recursive combine
merged: "{{ dict1 | combine(dict2, recursive=true, list_merge='append') }}"

product (Cartesian Product of Lists)

- vars:
    servers: [web1, web2]
    ports: [80, 443]
  debug:
    msg: "{{ item.0 }}:{{ item.1 }}"
  loop: "{{ servers | product(ports) | list }}"
# web1:80, web1:443, web2:80, web2:443

combine (Merge Dictionaries)

- vars:
    defaults:
      port: 80
      debug: false
      log_level: info
    overrides:
      port: 8080
      debug: true
  debug:
    msg: "{{ defaults | combine(overrides) }}"
# { port: 8080, debug: true, log_level: info }

product with Multiple Lists

- vars:
    envs: [dev, staging, prod]
    regions: [us-east, eu-west]
    sizes: [small, large]
  debug:
    msg: "{{ item.0 }}-{{ item.1 }}-{{ item.2 }}"
  loop: "{{ envs | product(regions, sizes) | list }}"
# dev-us-east-small, dev-us-east-large, dev-eu-west-small, ...

Recursive combine

- vars:
    base:
      database:
        host: localhost
        port: 5432
      cache:
        enabled: false
    env_specific:
      database:
        host: db.prod.example.com
      cache:
        enabled: true
  debug:
    msg: "{{ base | combine(env_specific, recursive=true) }}"
# { database: { host: db.prod.example.com, port: 5432 }, cache: { enabled: true } }

Real-World: Multi-Environment Config

- vars:
    base_config:
      app_name: myapp
      log_level: info
      workers: 2
    env_configs:
      production:
        log_level: error
        workers: 8
      staging:
        log_level: debug
        workers: 4
  set_fact:
    final_config: "{{ base_config | combine(env_configs[env]) }}"

product for Test Matrix

# Generate all test combinations
- vars:
    python_versions: ["3.10", "3.11", "3.12"]
    ansible_versions: ["2.16", "2.17"]
  debug:
    msg: "Test Python {{ item.0 }} + Ansible {{ item.1 }}"
  loop: "{{ python_versions | product(ansible_versions) | list }}"

Combine Multiple Dicts

- vars:
    a: { x: 1 }
    b: { y: 2 }
    c: { z: 3 }
  debug:
    msg: "{{ a | combine(b, c) }}"
# { x: 1, y: 2, z: 3 }

FAQ

combine overwrites or merges nested dicts?

By default, it overwrites nested dicts entirely. Use recursive=true to deep-merge nested dictionaries.

product vs with_nested?

loop: "{{ a | product(b) | list }}" replaces the deprecated with_nested: [a, b]. Same result, modern syntax.

Can I combine lists?

Use + or union for lists: {{ list_a + list_b }} or {{ list_a | union(list_b) }}. combine is for dicts only.

Related Articles

structuring playbooks with Ansible roles

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home