The user creation policy in our organization is to force changing password on first login and use SSH keys for remote connections; the password is only used to run sudo
or login locally.
To force password change we commonly run chage -d 0 <username>
. So, I need to run this in Ansible, but only if it just created user in the same play in previous task.
I create users with ansible.builtin.user
module. My plan is to register it's output into the variable, then I want to run shell
module but with some condition, and I need to determine the precise condition.
My playbook so far is:
- hosts: hosts
vars_files:
- users-config.yaml
tasks:
- name: create users
user:
name: "{{ item.username }}"
state: "{{ item.state }}"
comment: "{{ item.gecos }}"
shell: /bin/bash
groups: "{{ item.groups }}"
loop: "{{ users }}"
register: users_processed
- name: set password age to 0 to force change on next login for new users
debug:
var: "{{ item }}"
loop: "{{ users_processed.results }}"
So far, I found out the user
module returns useful changed and failed, so my condition will definitely contain item.changed and not item.failed
, but what other conditions I need to to properly catch only newly created users and not just updated pre-existing?
For reference, in the example output below apushkin
was not changed, nkhrushchov
was updated but does not need the password age to be reset, and only rraskolnikov
was just created and needs their password age to be reset.
ok: [host3] => (item={'comment': 'Alexander Pushkin', 'shell': '/bin/bash', 'group': 1000, 'name': 'apushkin', 'changed': False, 'state': 'present', 'groups': 'wheel', 'invocation': {'module_args': {'comment': 'Alexander Pushkin', 'ssh_key_bits': 0, 'update_password': 'always', 'non_unique': False, 'force': False, 'ssh_key_type': 'rsa', 'create_home': True, 'password_lock': None, 'ssh_key_passphrase': None, 'uid': None, 'home': None, 'append': False, 'skeleton': None, 'ssh_key_comment': 'ansible-generated on host3', 'group': None, 'system': False, 'state': 'present', 'role': None, 'hidden': None, 'local': None, 'authorization': None, 'profile': None, 'shell': '/bin/bash', 'expires': None, 'ssh_key_file': None, 'groups': ['wheel'], 'move_home': False, 'password': None, 'name': 'apushkin', 'seuser': None, 'remove': False, 'login_class': None, 'generate_ssh_key': None}}, 'home': '/home/apushkin', 'move_home': False, 'append': False, 'uid': 1000, 'failed': False, 'item': {'username': 'apushkin', 'gecos': 'Alexander Pushkin', 'state': 'present', 'groups': 'wheel'}, 'ansible_loop_var': 'item'}) => {
"<class 'dict'>": "VARIABLE IS NOT DEFINED!",
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"append": false,
"changed": false,
"comment": "Alexander Pushkin",
"failed": false,
"group": 1000,
"groups": "wheel",
"home": "/home/apushkin",
"invocation": {
"module_args": {
"append": false,
"authorization": null,
"comment": "Alexander Pushkin",
"create_home": true,
"expires": null,
"force": false,
"generate_ssh_key": null,
"group": null,
"groups": [
"wheel"
],
"hidden": null,
"home": null,
"local": null,
"login_class": null,
"move_home": false,
"name": "apushkin",
"non_unique": false,
"password": null,
"password_lock": null,
"profile": null,
"remove": false,
"role": null,
"seuser": null,
"shell": "/bin/bash",
"skeleton": null,
"ssh_key_bits": 0,
"ssh_key_comment": "ansible-generated on host3",
"ssh_key_file": null,
"ssh_key_passphrase": null,
"ssh_key_type": "rsa",
"state": "present",
"system": false,
"uid": null,
"update_password": "always"
}
},
"item": {
"gecos": "Alexander Pushkin",
"groups": "wheel",
"state": "present",
"username": "apushkin"
},
"move_home": false,
"name": "apushkin",
"shell": "/bin/bash",
"state": "present",
"uid": 1000
}
}
ok: [host3] => (item={'comment': '', 'shell': '/bin/bash', 'group': 1001, 'name': 'nhrushchov', 'changed': True, 'state': 'present', 'groups': 'wheel', 'invocation': {'module_args': {'comment': 'Nikita Khrushchov', 'ssh_key_bits': 0, 'update_password': 'always', 'non_unique': False, 'force': False, 'ssh_key_type': 'rsa', 'create_home': True, 'password_lock': None, 'ssh_key_passphrase': None, 'uid': None, 'home': None, 'append': False, 'skeleton': None, 'ssh_key_comment': 'ansible-generated on host3', 'group': None, 'system': False, 'state': 'present', 'role': None, 'hidden': None, 'local': None, 'authorization': None, 'profile': None, 'shell': '/bin/bash', 'expires': None, 'ssh_key_file': None, 'groups': ['wheel'], 'move_home': False, 'password': None, 'name': 'nhrushchov', 'seuser': None, 'remove': False, 'login_class': None, 'generate_ssh_key': None}}, 'home': '/home/nhrushchov', 'move_home': False, 'append': False, 'uid': 1001, 'failed': False, 'item': {'username': 'nhrushchov', 'gecos': 'Nikita Khrushchov', 'state': 'present', 'groups': 'wheel'}, 'ansible_loop_var': 'item'}) => {
"<class 'dict'>": "VARIABLE IS NOT DEFINED!",
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"append": false,
"changed": true,
"comment": "",
"failed": false,
"group": 1001,
"groups": "wheel",
"home": "/home/nhrushchov",
"invocation": {
"module_args": {
"append": false,
"authorization": null,
"comment": "Nikita Khrushchov",
"create_home": true,
"expires": null,
"force": false,
"generate_ssh_key": null,
"group": null,
"groups": [
"wheel"
],
"hidden": null,
"home": null,
"local": null,
"login_class": null,
"move_home": false,
"name": "nhrushchov",
"non_unique": false,
"password": null,
"password_lock": null,
"profile": null,
"remove": false,
"role": null,
"seuser": null,
"shell": "/bin/bash",
"skeleton": null,
"ssh_key_bits": 0,
"ssh_key_comment": "ansible-generated on host3",
"ssh_key_file": null,
"ssh_key_passphrase": null,
"ssh_key_type": "rsa",
"state": "present",
"system": false,
"uid": null,
"update_password": "always"
}
},
"item": {
"gecos": "Nikita Khrushchov",
"groups": "wheel",
"state": "present",
"username": "nhrushchov"
},
"move_home": false,
"name": "nhrushchov",
"shell": "/bin/bash",
"state": "present",
"uid": 1001
}
}
ok: [host3] => (item={'invocation': {'module_args': {'comment': 'Rodion Raskolnikov', 'ssh_key_bits': 0, 'update_password': 'always', 'non_unique': False, 'force': False, 'ssh_key_type': 'rsa', 'create_home': True, 'password_lock': None, 'ssh_key_passphrase': None, 'uid': None, 'home': None, 'append': False, 'skeleton': None, 'ssh_key_comment': 'ansible-generated on host3', 'group': None, 'system': False, 'state': 'present', 'role': None, 'hidden': None, 'local': None, 'authorization': None, 'profile': None, 'shell': '/bin/bash', 'expires': None, 'ssh_key_file': None, 'groups': ['wheel'], 'move_home': False, 'password': None, 'name': 'rraskolnikov', 'seuser': None, 'remove': False, 'login_class': None, 'generate_ssh_key': None}}, 'changed': True, 'failed': False, 'item': {'username': 'rraskolnikov', 'gecos': 'Rodion Raskolnikov', 'state': 'present', 'groups': 'wheel'}, 'ansible_loop_var': 'item'}) => {
"<class 'dict'>": "VARIABLE IS NOT DEFINED!",
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"changed": true,
"failed": false,
"invocation": {
"module_args": {
"append": false,
"authorization": null,
"comment": "Rodion Raskolnikov",
"create_home": true,
"expires": null,
"force": false,
"generate_ssh_key": null,
"group": null,
"groups": [
"wheel"
],
"hidden": null,
"home": null,
"local": null,
"login_class": null,
"move_home": false,
"name": "rraskolnikov",
"non_unique": false,
"password": null,
"password_lock": null,
"profile": null,
"remove": false,
"role": null,
"seuser": null,
"shell": "/bin/bash",
"skeleton": null,
"ssh_key_bits": 0,
"ssh_key_comment": "ansible-generated on host3",
"ssh_key_file": null,
"ssh_key_passphrase": null,
"ssh_key_type": "rsa",
"state": "present",
"system": false,
"uid": null,
"update_password": "always"
}
},
"item": {
"gecos": "Rodion Raskolnikov",
"groups": "wheel",
"state": "present",
"username": "rraskolnikov"
}
}
}
I am just struggling to deduce the proper reliable condition out of this output. The module manual says that append
is defined when user exists and force
is defined when state is absent and user exists. Does "exists" mean "existed before module run"? Is it safe to use the following condition:
when: item.changed and not item.failed and item.append is not defined and item.force is not defined
or is there a better way of doing this?