Repository: ansible/ansible

Issue:
user module: add parameter for --disabled-password option
From @henridwyer on 2016-11-24T00:59:05Z
##### ISSUE TYPE
 - Feature Idea

##### COMPONENT NAME
user

##### ANSIBLE VERSION
2.2.0.0

##### SUMMARY
Sometimes it can be useful to create a user with the flag --disabled-password, instead of just not setting a password.

##### STEPS TO REPRODUCE


##### EXPECTED RESULTS
Taken from:
http://stackoverflow.com/questions/39013796/create-user-with-option-disabled-password-by-ansible/39018859

Comparison of adduser --disabled-password test1 and - user: name=test2 state=present:
```
# grep test /etc/shadow
test1:*:17031:0:99999:7:::
test2:!:17031:0:99999:7:::
# passwd -S test1
test1 L 08/18/2016 0 99999 7 -1
# passwd -S test2
test2 L 08/18/2016 0 99999 7 -1
```

In one case the password is prefixed with **!** and in the other **\***. The exclamation mark makes the account locked, and can prevent logging in for hardened ssh configuration.

Ideally we would be able to create an account that isn't locker and that doesn't have a password by setting a flag:
user: name=test2 state=present disabled-password=true
Copied from original issue: ansible/ansible-modules-core#5717



Discussion:
From @ansibot on 2016-11-24T00:59:05Z

<!-- @henridwyer: Thanks for filing this feature idea! -->
@ansible ping, this issue is waiting for your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_bug_new --->
From @gundalow on 2016-11-24T00:59:05Z

Hi,
Thanks of the suggestion. This may require some work to make it function across all the distros and platforms the  that the `user` module supports.

This isn't something we (Ansible Core Team) plan to work on, so we'd be looking to the community to implement this with appropriate integration tests.
From @ahammond on 2016-11-24T00:59:05Z

https://stackoverflow.com/questions/39013796/create-user-with-option-disabled-password-by-ansible/39018859 
cc @sfromm
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/system/user.py b/lib/ansible/modules/system/user.py
--- a/lib/ansible/modules/system/user.py
+++ b/lib/ansible/modules/system/user.py
@@ -173,6 +173,14 @@
             - An expiry time for the user in epoch, it will be ignored on platforms that do not support this.
               Currently supported on Linux, FreeBSD, and DragonFlyBSD.
         version_added: "1.9"
+    password_lock:
+        description:
+            - Lock the password (usermod -L, pw lock, usermod -C).
+              BUT implementation differs on different platforms, this option does not always mean the user cannot login via other methods.
+              This option does not disable the user, only lock the password. Do not change the password in the same task.
+              Currently supported on Linux, FreeBSD, DragonFlyBSD, NetBSD.
+        type: bool
+        version_added: "2.6"
     local:
         description:
             - Forces the use of "local" command alternatives on platforms that implement it.
@@ -289,6 +297,7 @@ def __init__(self, module):
         self.update_password = module.params['update_password']
         self.home = module.params['home']
         self.expires = None
+        self.password_lock = module.params['password_lock']
         self.groups = None
         self.local = module.params['local']
 
@@ -520,6 +529,11 @@ def modify_user_usermod(self):
             cmd.append('-e')
             cmd.append(time.strftime(self.DATE_FORMAT, self.expires))
 
+        if self.password_lock:
+            cmd.append('-L')
+        elif self.password_lock is not None:
+            cmd.append('-U')
+
         if self.update_password == 'always' and self.password is not None and info[1] != self.password:
             cmd.append('-p')
             cmd.append(self.password)
@@ -927,6 +941,29 @@ def modify_user(self):
             ]
             return self.execute_command(cmd)
 
+        # we have to lock/unlock the password in a distinct command
+        if self.password_lock:
+            cmd = [
+                self.module.get_bin_path('pw', True),
+                'lock',
+                '-n',
+                self.name
+            ]
+            if self.uid is not None and info[2] != int(self.uid):
+                cmd.append('-u')
+                cmd.append(self.uid)
+            return self.execute_command(cmd)
+        elif self.password_lock is not None:
+            cmd = [
+                self.module.get_bin_path('pw', True),
+                'unlock',
+                '-n',
+                self.name
+            ]
+            if self.uid is not None and info[2] != int(self.uid):
+                cmd.append('-u')
+                cmd.append(self.uid)
+            return self.execute_command(cmd)
         return (rc, out, err)
 
 
@@ -1253,6 +1290,11 @@ def modify_user(self):
             cmd.append('-p')
             cmd.append(self.password)
 
+        if self.password_lock:
+            cmd.append('-C yes')
+        elif self.password_lock is not None:
+            cmd.append('-C no')
+
         # skip if no changes to be made
         if len(cmd) == 1:
             return (None, '', '')
@@ -2152,6 +2194,7 @@ def main():
             ssh_key_passphrase=dict(type='str', no_log=True),
             update_password=dict(type='str', default='always', choices=['always', 'on_create']),
             expires=dict(type='float'),
+            password_lock=dict(type='bool'),
             local=dict(type='bool'),
         ),
         supports_check_mode=True


Test patch: