Ansible - multiple statements with the same variables

Solution 1:

I'm new to ansible

I'm listing here some different options, so you can learn a bit more than just the ideal solution (Option 5)

Option 1: Use YAML anchors and references

This is completely unrelated to Ansible but since the files are in YAML format you're able to do something like this:

- name: Create real users
  user: name="{{item.user_name}}" comment="{{item.user_description}}" home="/home/{{item.user_name}}" shell="/bin/bash" uid="{{item.user_id}}"
  with_items: &my_items
    - user_name: user1
      user_description: user 1
      user_id: 2000
    - user_name: user2
      user_description: user 2
      user_id: 2001

- name: Copy SSH keys
  copy:
    src: "keys/{{ item.user_name }}.key"
    dest: "/home/{{ item.user_name }}/.ssh/authorized_keys"
    owner: "{{ item.user_name }}"
    group: "{{ item.user_name }}"
    mode: 0600
  with_items: *my_items

Option 2: Variables in blocks

Blocks are a feature introduced in Ansible 2. You can define variables for blocks and use them in the contained tasks

- vars:
    userlist:
      - user_name: user1
        user_description: user 1
        user_id: 2000
      - user_name: user2
        user_description: user 2
        user_id: 2001
  block:
    - name: Create real users
      user: name="{{item.user_name}}" comment="{{item.user_description}}" home="/home/{{item.user_name}}" shell="/bin/bash" uid="{{item.user_id}}"
      with_items: "{{ userlist }}"

    - name: Copy SSH keys
      copy:
        src: "keys/{{ item.user_name }}.key"
        dest: "/home/{{ item.user_name }}/.ssh/authorized_keys"
        owner: "{{ item.user_name }}"
        group: "{{ item.user_name }}"
        mode: 0600
      with_items: "{{ userlist }}"

Option 3: Apply the loop to an include task and have your tasks in the included file

- include: other_file.yml
  with_items:
    - user_name: user1
      user_description: user 1
      user_id: 2000
    - user_name: user2
      user_description: user 2
      user_id: 2001

In the included file you will be able to access the item and its properties, e.g. item.user_name, just like you already had it:

- name: Create real users
  user: name="{{item.user_name}}" comment="{{item.user_description}}" home="/home/{{item.user_name}}" shell="/bin/bash" uid="{{item.user_id}}"

- name: Copy SSH keys
  copy:
    src: "keys/{{ item.user_name }}.key"
    dest: "/home/{{ item.user_name }}/.ssh/authorized_keys"
    owner: "{{ item.user_name }}"
    group: "{{ item.user_name }}"
    mode: 0600

Option 4: set a fact containing your user list in a separate task

- set_fact:
    userlist:
      - user_name: user1
        user_description: user 1
        user_id: 2000
      - user_name: user2
        user_description: user 2
        user_id: 2001

- name: Create real users
  user: name="{{item.user_name}}" comment="{{item.user_description}}" home="/home/{{item.user_name}}" shell="/bin/bash" uid="{{item.user_id}}"
  with_items: "{{ userlist }}"

- name: Copy SSH keys
  copy:
    src: "keys/{{ item.user_name }}.key"
    dest: "/home/{{ item.user_name }}/.ssh/authorized_keys"
    owner: "{{ item.user_name }}"
    group: "{{ item.user_name }}"
    mode: 0600
  with_items: "{{ userlist }}"

Option 5: Use group_vars

group_vars probably make the most sense here. I guess your hosts are in some group in your inventory file, let's call it foo.

Create a file group_vars/foo relative to your playbook with the content:

userlist:
  - user_name: user1
    user_description: user 1
    user_id: 2000
  - user_name: user2
    user_description: user 2
    user_id: 2001

All hosts which belong to the group foo will now automatically have access to the userlist variable. And you can just use it in your tasks:

- name: Create real users
  user: name="{{item.user_name}}" comment="{{item.user_description}}" home="/home/{{item.user_name}}" shell="/bin/bash" uid="{{item.user_id}}"
  with_items: "{{ userlist }}"

- name: Copy SSH keys
  copy:
    src: "keys/{{ item.user_name }}.key"
    dest: "/home/{{ item.user_name }}/.ssh/authorized_keys"
    owner: "{{ item.user_name }}"
    group: "{{ item.user_name }}"
    mode: 0600
  with_items: "{{ userlist }}"

If you don't have groups or don't want to limit it to certain groups, you can store the vars file as group_vars/all where all hosts have access to.

Solution 2:

Thanks for such a complete answer. In the end I discovered I could use with_dict like this:

1) Create a vars.yml file (in the same directory)

---
usersxxx:
    user1:
      description: User1
      user_id: 2001
      shell: /bin/bash
      ...other options here
    user2:
      description: User2        
      user_id: 2002
      shell: /bin/bash
      ...other options here

2) Create the new playbook:

---
- hosts: home
  vars_files:
    - vars.yml

  become: yes

  tasks: 
    - name: Create real users
      user: name="{{item.key}}" comment="{{item.value.description}}" home="/home/{{item.key}}" uid="{{item.value.user_id}}"
      with_dict: "{{usersxxx}}"

Looks like it works perfectly.

I only changed it to usersxxx to ensure that I wasn't hitting any python/ansible primitives.

Hope this helps someone else!