Ansible passing variables to roles

2019-07-10 04:05发布

问题:

I would like to create folders, users and groups depending on the environment and hostname.

My playbook passes the environment and hostname to the role, which is supposed to pick the correct data from the defaults/main.yml file.

Playbook:

roles:
- { role: myrole, environment: 'xxx', hostname: 'yyy' }

myrole/tasks/main.yml

- name: add the group
  group: name={{ item }} state=present
  with_items: "{{ environment }}_{{ hostname }}_groups"

myrole/defaults/main.yml

xxx_yyy_groups: [ '1', '2', '3', ]

However when I pass variables to my role it fails to use the defaults/main.yml file and I get the following error:

TASK [internal/groups_users_folders : add the group] ******************************

FAILED! => {"failed": true, "msg": "environment must be a dictionary, received xxx ()"}

Why can't Ansible access the variables in the defaults folder anymore? Or is there a more elegant way?

edit:

Apparently Ansible doesn't like the variable to be called "environment" and I can't glue together the with_items variable like this.

- name: set groups variable
  set_fact:
    groups: "{{ env }}_{{ hostname }}_groups" 

- name: add groups
  group: name={{ item }} state=present
  with_items: "{{ groups }}"

Seems to work with arrays, if I use a hash and want to access the variables with item.xxx this error returns:

FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'unicode object' has no attribute 'name'\n\nThe error appears to have been in '.../tasks/main.yml': line 25, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: add users\n ^ here\n"}

回答1:

You use with_items in a bit wrong way with with_items: "{{ env }}_{{ hostname }}_groups". In fact you pass string "xxx_yyy_groups", but you need to pass it as a variable name.

I did some research and it looks like it isn't possible to do it this way. You either need to use fact as you do or load file with variables(e.g. {{ env }}_{{ hostname }}.yml). Actually it how normally people handle different tasks and variables for environments and for default OS families.

I hope this helps!

UPD you can also take a look on this answer:Ansible: how to construct a variable from another variable and then fetch it's value to get some details/thoughts



回答2:

When you write:

  set_fact:
    groups: "{{ env }}_{{ hostname }}_groups"

Its actually constructing a string named groups. So the problem is that now groups is a variable referring to string literal "{{ env }}_{{ hostname }}_groups"(values will ofcourse be substituted) but since it is a value and not the reference, it wont refer to the list defined in default.yml.

Alternate:

  1. Follow the suggestions given by @Andrey Rusanov.
  2. Since you only have a list for each (env,host) pair,defining them in separate file does not look a good choice since each file will have a single list variable. I would suggest you to define all these lists inside a dictionary and place that dictionary inside default.yml to maintain the constraints you have. Following should be the structure of the dictionary:

pairLists: {  
             env_1: {
                 host1_1: [1,2,3,4],
                 host2_1: [2,3,4,5],
             },
             env_2: {
                 host2_1: [3,2,3,4],
                 host2_2: [6,5,4,3],
             },
   }


标签: ansible role