dictionaryvariablesansibleundefined

Error "Vars in a Task must be specified as a dictionary, or a list of dictionaries"



'data_list' consists of the values in the csv file. I want to use the values in 'data_list' to loop through the parameters in the 'Create user' section of the playbook, but I am getting this error after running my playbook:
TASK [Create Multiple Users : Create multiple users] ***************************
fatal: [10.16.220.30]: FAILED! => {"reason": "Vars in a Task must be specified as a dictionary, or a list of dictionaries\n\nThe error appears to be in '/runner/project/Windows AD/roles/Create Multiple Users/tasks/Create_multiple_users.yml': line 14, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n      - \"{{ item.groups }}\"\n  vars: data_list\n        ^ here\n"}

This is my playbook:

---
- name: Read Users 
  hosts: localhost
  vars:
    data_list: []
  tasks:
  - read_csv:
      path: user.csv
      key: name  
      fieldnames: name,firstname,surname,displayName,groups
      delimiter: ','
    register: userdata


  - name: Extract the list
    set_fact:
      data_list: "{{ data_list + [{ 'name': item.value.name, 'firstname': item.value.firstname, 'surname': item.value.surname, 'displayName': item.value.displayName, 'groups': item.value.groups }] }}"
    loop: "{{ userdata.dict|dict2items }}"


- name: Create user accounts
  hosts: "{{ hostname }}"
  gather_facts: false
  any_errors_fatal: false
  become: yes
  become_method: runas
  become_user: admin
  roles:
    - { role: Create Multiple Users }


- name: Create users
  community.windows.win_domain_user:
    name: "{{ item.name }}"
    firstname: "{{ item.firstname }}"
    surname: "{{ item.surname }}"
    attributes:
      displayName: "{{ item.firstname + ' ' + item.surname }}"
    groups:
      - "{{ item.groups }}"
  vars: data_list 
  with_items: 
    - "{{ data_list }}"

What is the correct vars that I should write?


Solution

  • This is the line causing the error in your task

        vars: data_list
    

    As mentioned in your error message, the vars section should look like:

        vars:
          var1: value1
          var2: value2
    

    But this is not the only problem in your above script. You are gathering your csv data in a separate play on localhost and setting that info as a fact in variable data_list. When your first play is over, that var will only be known from the localhost target. If you want to reuse it in a second play targeting other hosts, you'll have to get that var from the hostvars magic variable

    {{ hostvars.localhost.data_list }}
    

    This is not the best approach here as you can easily shorten your playbook to a single play. The trick is to delegate your csv gathering task to localhost and set run_once: true so that the registered var is calculated only once and distributed to all hosts with the same value. You can also drop the set fact which basically copies the same key: value to a new var.

    Here is an (untested) example playbook to show you the way:

    ---
    - name: Create multiple Windows AD user accounts from CSV
      hosts: "{{ hostname }}"
      gather_facts: false
    
      tasks:
        - name: read csv from localhost (single run same registered var for all hosts)
          read_csv:
            path: user.csv
            key: name  
            fieldnames: name,firstname,surname,displayName,groups
            delimiter: ','
          register: userdata
          run_once: true
          delegate_to: localhost
    
        - name: Create users
          community.windows.win_domain_user:
            name: "{{ item.name }}"
            firstname: "{{ item.firstname }}"
            surname: "{{ item.surname }}"
            attributes:
              displayName: "{{ item.firstname + ' ' + item.surname }}"
            groups:
              - "{{ item.groups }}"
          # This will work on any number of hosts as `userdata`
          # now has the same value for each hosts inside this play.
          # we just have to extract the values from each keys from
          # `userdata` and loop on that list
          loop: "{{ userdata.dict | dict2items | map(attribute='value') }}"