Search code examples
pythonlinuxansibleansible-role

Ansible user deployment task with loop


I have started an ansible role for user deployment and created some variables in defaults/main.yml:

bootstrap_users:
 - name: test1
   comment: "test user 1"
   shell: "/bin/bash"
   password: "<hashed password>"
   groups: []
   sshpubkey: "<public ssh key"
   home: "/home/test1"
   create_home: yes
 - name: test2
   comment: "test user 2"
   shell: "/bin/bash"
   password: "<hashed password>"
   groups: []
   sshpubkey: "<public ssh key"
   home: "/home/test2"
   create_home: yes

in tasks/main.yml to create users in loop.

- name: "Create users"
  user:
    name: "{{ item.name }}"
    comment: "{{ item.comment|default('test user') }}"
    shell: "{{ item.shell|default('/bin/bash') }}"
    password: "{{ item.password|default('1234') }}"
    groups: "{{ ','.join(item.groups|default([])) }}"
    state: present
    update_password: on_create
    create_home: "{{ item.create_home|default(yes) }}"
    home: "{{ item.home|default('/home/{{ item.name }}') }}"
  loop: "{{ bootstrap_users|default([]) }}"
  loop_control:
    label: "{{ item.name }}"

I want to copy some files to the newly created user homes like .bashrc or profile.

Is there a way to crate a inner and outer loop to copy those files to any newly created user

Update: I've created the task like this:

- name: "Copy user shell settings files"
  copy:
    src: "{{ item[1].src }}"
    dest: "{{ item[0].home | default('/home/' ~ item[0].name) }}/{{ item[1].dest }}"
    owner: "{{ item[0].name }}"
    group: "{{ (item[0].name }}"
    mode: "{{ item[1].mode | default('0600') }}"
  loop: "{{ [ bootstrap_users|default([]), bash_files|default([]) ] }}"
  loop_control:
    label: "{{ item[0].name }}/{{ item[1].name }}"
  vars:
    bash_files:
      - name: bashrc
        src: "bash/bashrc"
        dest: ".bashrc"
        mode: "0600"
      - name: profile
        src: "bash/profile"
        dest: ".profile"
        mode: "0600"
      - name: bash_aliases
        src: "bash/bash_aliases"
        dest: ".bash/bash_aliases"
        mode: "0600"
      - name: bash_functions
        src: "bash/bash_functions"
        dest: ".bash/bash_functions"
        mode: "0600"

But when I run the task I got this error:

TASK [base_role : Copy user shell settings files] ****************************************************************
fatal: [172.20.2.4]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'src'\n\nThe error appears to be in '/home/andre/Dokumente/Ansible Development/Ansible-BaseRole/roles/base_role/tasks/system_setup/os-settings-user_deployment.yml': line 96, 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: \"Copy user shell settings files\"\n  ^ here\n"}


Solution

  • You could use a nested loop [1], something like:

    - name: Install users configuration
      copy:
        dest: "{{ item[0].home | default('/home/' ~ item[0].name) }}/{{ item[1].path }}"
        group: "{{ (item[0].groups|default([]))[0] | default(item[0].name) }}"
        mode: "{{ item[1].mode | default('u=rw') }}"
        owner: "{{ item[0].name }}"
        src: "{{ item[1].name }}"
      loop: "{{ (bootstrap_users|default([])) | product(bash_files|default([])) | list }}"
      loop_control:
        label: "{{ item[0].name }}/{{ item[1].name }}"
    

    Or using a simple loop to include_tasks, while the included task file has its own loop [2] :

    #main.yaml
    - include_tasks: install-config.yaml
      loop: "{{ bootstrap_users|default([]) }}"
      loop_control:
        loop_var: "{{ userentry }}"
    
    #install-config
    - name: Install users configuration
      copy:
        dest: "{{ userentry.home | default('/home/' ~ userentry.name) }}/{{ item.path }}"
        group: "{{ (userentry.groups|default([]))[0] | default(userentry.name) }}"
        mode: "{{ item.mode | default('u=rw') }}"
        owner: "{{ userentry.name }}"
        src: "{{ item.name }}"
      loop: "{{ files_to_install|default([]) }}"
      loop_control:
        label: "{{ item.name }}"
    

    [1] https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#iterating-over-nested-lists

    [2] https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#defining-inner-and-outer-variable-names-with-loop-var