Ansible – multiple statements with the same variables

ansible

I'm new to Ansible, so I might be missing something obvious. I have a playbook doing the following:

- 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:
    - { 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:
     - { user_name: "user1", user_description: "user 1", user_id: "2000" }
     - { user_name: "user2", user_description: "user 2",  user_id: "2001" }

Basically reusing the with_items over and over again. Ideally I would like to store this in an external file with all the fields I'm likely to use.

Is this possible? Where should I start looking?

Ta….
Tom

Best Answer

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.