Ansible: multiple vars_files with same yaml structure do not merge

Viewed 2718

I have an Ansible playbook that uses two var files; one for general properties and others for me specific properties. However, there are some properties from both files that share the same root YAML structure (aws.ec2) but when I run my playbook, it seems like the properties do not merge into one tree, but the last listed file overwrites the previous for any props using aws.ec2.

Playbook

- name: Play 1. Create AWS Environment
  hosts: localhost

  vars_files:
    - var_files/aws_management_vars.yml
    - var_files/aws_general_vars.yml

aws_management_vars.yml

aws:
  ec2:
    node:
      name: "Management Node"
      instance_type: "t2.micro"
      ...

aws_general_vars.yml

aws:
  region: us-west-1
  ec2:
    env: mih-env
    vpc_id: vpc-abc12345
    ...

When I run my playbook, if I have the vars file in the order here, it complains that it cannot find aws.ec2.node.

"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'node'

If I swap the order, it complains that it cannot find aws.region.

"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'region'

Is this a limitation of vars_files and is there something else I can use that will parse the yml files and merge properties that have the same structure? I know I could just rename the structure in the files, but I would like it keep it this way if possible.

Ansible v2.7.8

1 Answers

Q: "Is this a limitation of vars_files? What can I use that will parse the yml files and merge properties that have the same structure?"

A: Yes. It's a limitation of vars_files. It's possible to control how variables merge. See DEFAULT_HASH_BEHAVIOUR. But this is not recommended. Quoting: "We generally recommend not using this setting unless you think you have an absolute need for it."

There is a simple solution. Use include_vars, put the included data into dictionaries, and combine them. Set recursive=True to merge the keys. For example,

    - include_vars:
        file: var_files/aws_management_vars.yml
        name: management
    - include_vars:
        file: var_files/aws_general_vars.yml
        name: general
    - set_fact:
        my_vars: "{{ management|combine(general, recursive=True) }}"
    - debug:
        var: my_vars.aws

give

  my_vars.aws:
    ec2:
      env: mih-env
      node:
        instance_type: t2.micro
        name: Management Node
      vpc_id: vpc-abc12345
    region: us-west-1

Q: "include_vars complained that it cannot be used at playbook level. Would I need to add this to the top of all tasks being run in a playbook?"

A: Yes. include_vars is a task. Put it at the top of the tasks. See Variable precedence. include_vars (precedence 18.) will overwrite task's, block's and roles' vars (17,16,15). When putting it at the top of the tasks, then there is practically no difference between include_vars and vars_files (precedence 14). As a consequence, use include_role instead of roles if any.

Related