What's the fastest method to add Linux users with Ansible?

Viewed 73

I have a users.yaml file with information regarding 400+ users. I need Ansible to create these users during provisioning. I tried with the async keyword (if that's the right word to use, tell me if I'm wrong) and poll: 15 but it takes ~10minutes.

    - name: Add FTP users asynchronously
      ansible.builtin.user:
        name: "{{ item.name }}"
        home: "{{ item.home }}"
        shell: /sbin/nologin
        groups: ftp-users
        create_home: yes
        append: no
      loop: "{{ ftp_users }}"
      async: 60
      poll: 15
      tags: users

I also tried using poll:0 but many users aren't created.

1 Answers

Your actual use of async is adapted to a single long running task use case where you want to minimize the chance of getting your connection kicked because of a timeout. You are asking ansible to start a job, disconnect from the target and then reconnect every 15 seconds to check if the job is done (or until you reach the 60 seconds timeout). Nothing will be launched in parallel: the next iteration in the loop will only start when the current is done.

What you want to do instead is run those tasks in parallel as fast as possible and then check back later if they are done. In this case, you have to use poll: 0 on your task and later check for completion with the async_status module as described on the ansible async guide. Note that you also need to cleanup the async job cache as ansible will not do it automagically for you in that case.

In your case, this would give:

- name: Add FTP users asynchronously
  ansible.builtin.user:
    name: "{{ item.name }}"
    home: "{{ item.home }}"
    shell: /sbin/nologin
    groups: ftp-users
    create_home: yes
    append: no
  loop: "{{ ftp_users }}"
  async: 60
  poll: 0
  register: add_user

- name: Wait until all commands are done
  async_status:
    jid: "{{ item.ansible_job_id }}"
  register: async_poll_result
  until: async_poll_result.finished
  retries: 60
  delay: 1
  loop: "{{ add_user.results }}"

- name: clean async job cache
  async_status:
    jid: "{{ item.ansible_job_id }}"
    mode: cleanup
  loop: "{{ add_user.results }}"

Meanwhile, although this is a direct answer on how to use async for parallel jobs, I'm not entirely sure this will fix your actual performance problem which could come from other issues (like slow dns, slow network, pipelining not enabled if that is possible, master ssh connection not configured...)

Related