Ansible:如何仅为新创建的用户运行后续任务?

Ansible:如何仅为新创建的用户运行后续任务?

我们组织中的用户创建策略是在首次登录时强制更改密码并使用 SSH 密钥进行远程连接;密码仅用于sudo本地运行或登录。

为了强制更改密码,我们通常运行chage -d 0 <username>。因此,我需要在 Ansible 中运行它,但是仅有的如果它只是在前一个任务中的同一个剧本中创建了用户。

我使用模块创建用户ansible.builtin.user。我的计划是将其输出注册到变量中,然后我想运行shell模块,但需要满足一些条件,并且我需要确定精确的条件。

我目前的策略是:

- 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 }}"

到目前为止,我发现user模块返回有用的已更改失败的,所以我的条件肯定会包含item.changed and not item.failed,但我还需要什么其他条件才能正确捕获新创建的用户而不是仅仅更新预先存在的用户?

作为参考,在下面的示例输出中apushkin没有改变,nkhrushchov被更新了,但是不需要需要重置密码年龄,并且只有rraskolnikov刚刚创建且需要重置密码年龄的人。

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"
        }
    }
}

我只是在努力从这个输出中推断出正确的可靠条件。模块手册说的append是当用户存在force状态不存在且用户存在。“存在”是指“模块运行前就存在”吗?使用以下条件是否安全:

    when: item.changed and not item.failed and item.append is not defined and item.force is not defined

或者有更好的方法可以做到这一点?

答案1

使用盖特恩并创建当前用户列表

    - name: Get all users
      getent:
        database: passwd
    - name: Set present users
      set_fact:
        present_users: "{{ ansible_facts.getent_passwd.keys()|list }}"

更新用户后获取新用户列表

    - name: Get all users
      getent:
        database: passwd
    - name: Set new users
      set_fact:
        new_users: "{{ ansible_facts.getent_passwd.keys()|
                       difference(present_users) }}"

完整剧本的示例

- hosts: test_11
  vars:
    users:
      - username: alice
        state: present
        gecos: Alice
        groups: [guest]
      - username: bob
        state: present
        gecos: Bob
        groups: [guest]
  tasks:
    - name: Get all users
      getent:
        database: passwd
    - name: Set present users
      set_fact:
        present_users: "{{ ansible_facts.getent_passwd.keys()|list }}"
    - name: Create users
      user:
        name: "{{ item.username }}"
        state: "{{ item.state }}"
        comment: "{{ item.gecos }}"
        shell: /bin/bash
        groups: "{{ item.groups }}"
      loop: "{{ users }}"
      loop_control:
        label: "{{ item.username }}"
    - name: Get all users
      getent:
        database: passwd
    - name: Set new users
      set_fact:
        new_users: "{{ ansible_facts.getent_passwd.keys()|
                       difference(present_users) }}"
    - debug:
        var: new_users
    - debug:
        msg: "Force change on next login for {{ item }}"
      loop: "{{ new_users }}"

给出

PLAY [test_11] *******************************************************************************

TASK [Get all users] *************************************************************************
ok: [test_11]

TASK [Set present users] *********************************************************************
ok: [test_11]

TASK [Create users] **************************************************************************
changed: [test_11] => (item=alice)
changed: [test_11] => (item=bob)

TASK [Get all users] *************************************************************************
ok: [test_11]

TASK [Set new users] *************************************************************************
ok: [test_11]

TASK [debug] *********************************************************************************
ok: [test_11] => 
  new_users:
  - alice
  - bob

TASK [debug] *********************************************************************************
ok: [test_11] => (item=alice) => 
  msg: Force change on next login for alice
ok: [test_11] => (item=bob) => 
  msg: Force change on next login for bob

PLAY RECAP ***********************************************************************************
test_11: ok=7    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

该剧本是幂等的。

相关内容