diff --git a/changelogs/fragments/user-home-dir-with-no-parents.yaml b/changelogs/fragments/user-home-dir-with-no-parents.yaml new file mode 100644 index 00000000000..744b49b6d46 --- /dev/null +++ b/changelogs/fragments/user-home-dir-with-no-parents.yaml @@ -0,0 +1,2 @@ +bugfixes: + - user - create parent directories when the specified home path has parent directiries that do not exist (https://github.com/ansible/ansible/issues/41393) diff --git a/lib/ansible/modules/system/user.py b/lib/ansible/modules/system/user.py index 0e8e6f9285e..b34e8996165 100644 --- a/lib/ansible/modules/system/user.py +++ b/lib/ansible/modules/system/user.py @@ -628,6 +628,12 @@ class User(object): cmd.append(self.comment) if self.home is not None: + # If the specified path to the user home contains parent directories that + # do not exist, first create the home directory since useradd cannot + # create parent directories + parent = os.path.dirname(self.home) + if not os.path.isdir(parent): + self.create_homedir(self.home) cmd.append('-d') cmd.append(self.home) @@ -2883,7 +2889,24 @@ def main(): if not user.user_exists(): if module.check_mode: module.exit_json(changed=True) + + # Check to see if the provided home path contains parent directories + # that do not exist. + path_needs_parents = False + if user.home: + parent = os.path.basename(user.home) + if not os.path.isdir(parent): + path_needs_parents = True + (rc, out, err) = user.create_user() + + # If the home path had parent directories that needed to be created, + # make sure file permissions are correct in the created home directory. + if path_needs_parents: + info = user.user_info() + if info is not False: + user.chown_homedir(info[2], info[3], user.home) + if module.check_mode: result['system'] = user.name else: diff --git a/test/integration/targets/user/tasks/main.yml b/test/integration/targets/user/tasks/main.yml index 4f64b8fe7e2..9d32ac446fb 100644 --- a/test/integration/targets/user/tasks/main.yml +++ b/test/integration/targets/user/tasks/main.yml @@ -154,6 +154,49 @@ when: ansible_facts.system != 'Darwin' +# https://github.com/ansible/ansible/issues/41393 +# Create a new user account with a path that has parent directories that do not exist +- name: Create user with home path that has parents that do not exist + user: + name: ansibulluser2 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_with_no_parent_1 + +- name: Create user with home path that has parents that do not exist again + user: + name: ansibulluser2 + state: present + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: create_home_with_no_parent_2 + +- name: Check the created home directory + stat: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + register: home_with_no_parent_3 + +- name: Ensure user with non-existing parent paths was created successfully + assert: + that: + - create_home_with_no_parent_1 is changed + - create_home_with_no_parent_1.home == user_home_prefix[ansible_facts.system] ~ '/in2deep/ansibulluser2' + - create_home_with_no_parent_2 is not changed + - home_with_no_parent_3.stat.uid == create_home_with_no_parent_1.uid + - home_with_no_parent_3.stat.gr_name == default_user_group[ansible_facts.distribution] | default('ansibulluser2') + +- name: Cleanup test account + user: + name: ansibulluser2 + home: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/ansibulluser2" + state: absent + remove: yes + +- name: Remove testing dir + file: + path: "{{ user_home_prefix[ansible_facts.system] }}/in2deep/" + state: absent + + ## user check - name: run existing user check tests diff --git a/test/integration/targets/user/vars/main.yml b/test/integration/targets/user/vars/main.yml index 603fcc59a0d..4b328f7184b 100644 --- a/test/integration/targets/user/vars/main.yml +++ b/test/integration/targets/user/vars/main.yml @@ -7,3 +7,7 @@ user_home_prefix: status_command: OpenBSD: "grep ansibulluser /etc/master.passwd | cut -d ':' -f 2" FreeBSD: 'pw user show ansibulluser' + +default_user_group: + openSUSE Leap: users + MacOSX: admin