ansibleyaml

How to combine dictionaries directly in var file


I would like to use variable servers.server1 in task without additional steps like step_var. How to substitute variables and add dictionaries to another dictionary outside of tasks in var file?

#var/main.yaml

common_parts_1:
  storage:
    type: nfs
    value: 10GB
common_parts_2:
  cpu:
    cores: 2
  memory:
    value: 2GB
  
servers:
  server1:
    name: superPC
    owner: John 
    "{{ common_parts_1 }}"
    "{{ common_parts_2 }}"

Expected result:

servers:
  server1:
    name: superPC
    owner: John
    storage:
      type: nfs
      value: 10GB
    cpu:
      cores: 2
    memory:
      value: 2GB


Solution

  • I see two simple ways to implement this

    Ansible's combine filter

    # vars/vars1.yml
    ---
    common_parts_1:
      storage:
        type: nfs
        value: 10GB
    common_parts_2:
      cpu:
        cores: 2
      memory:
        value: 2GB
    
    servers_pre:
      server1:
        name: superPC
        owner: John
    
    servers:
      server1: "{{ servers_pre.server1 | combine(common_parts_1) | combine(common_parts_2) }}"
    

    First you do not define servers but e.g. servers_pre. Then you can define servers by combining the data from the other variables, for this you can use the combine filter of Ansible.

    YAML anchors and aliases

    # vars/vars2.yml
    ---
    common_parts_1: &common_parts_1
      storage:
        type: nfs
        value: 10GB
    common_parts_2: &common_parts_2
      cpu:
        cores: 2
      memory:
        value: 2GB
    
    servers:
      server1:
        name: superPC
        owner: John
        <<: [*common_parts_1, *common_parts_2]
      server2:
        name: nanoPC
        owner: James
        <<: *common_parts_2
    

    This variant is based purely on YAML and uses YAML anchors and aliases. You do not only define the key common_parts_1, but also define the data structure as an anchor using &common_parts_1. You can then insert this data structure elsewhere using the alias *common_parts_1.

    To insert a data structures with an alias in a level on which you have already defined data, use <<: followed by the alias (see server2). If you want to insert several anchored data structures, define the aliases as a list (see server1).

    Difference between the two variants

    Attention, the two variants are not identical.

    If you have the same key in common_parts_1 and common_parts_2: combine (in the default configuration list_merge=replace) overwrites the last occurrence of the key. Therefore, the order of the combine variant should be chosen from the most general (left) to the most specific (right). The YAML variant acts slightly differently here.

    Make your own tests to ensure that the result is as desired.