I'm getting a bit lost with next variable types and am hoping for some direction in a specific task please:
The Goal: Based on a list of username:publickey values. I'd like to:
- ensure the user exists on the target system
- if the user does exist then: - ensure the "/home/$user/.ssh/authorized_keys" file exists with the correct permissions through the path.
The Scene:
I have a variable:
ssh_vars:
auth_keys:
bob: "bobs_public_key_string"
anne: "annes_public_key_string"
anon: "anons_public_key_string
I need to iterate over this variable and for each auth_keys item call a tasklist:
- name: loop through the auth_keys and call ssh_dirs.yml for each
ansible.builtin.include_tasks: "ssh_dirs.yaml"
loop: "{{ ssh_vars.auth_keys }}"
However, I only really want to do this when the auth_key(key) is a user which already exists on the host.
I have been playing with getent, within "ssh_dirs.yaml":
- name: "Ensure the user exists on the target system"
ansible.builtin.getent:
database: passwd
key: "{{ item.name }}"
fail_key: false
register: userlookup
which creates what i think is a list of dictionaries:
ok: [ans-client.local] => {
"userlookup": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_facts": {
"getent_passwd": {
"bob": [
"x",
"1003",
"1003",
"",
"/home/bob",
"/usr/bin/bash"
]
}
},
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"database": "passwd",
"fail_key": false,
"key": "bob",
"service": null,
"split": null
}
},
"item": {
"key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDvIZuaBhAIGShw21rkvgqyvNePunbVs6OtOBhYJOY2P anne@ans-server",
"name": "bob"
}
},
{
"ansible_facts": {
"getent_passwd": {
"anne": [
"x",
"1000",
"1000",
"anne",
"/home/anne",
"/bin/bash"
]
}
},
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"database": "passwd",
"fail_key": false,
"key": "anne",
"service": null,
"split": null
}
},
"item": {
"key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKr/76O3hLJlcyZuy7EJxf7sC1z9BSHMuxGsFGBibJY3 anne@ans-server",
"name": "anne"
}
},
{
"ansible_facts": {
"getent_passwd": {
"anon": null
}
},
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"database": "passwd",
"fail_key": false,
"key": "anon",
"service": null,
"split": null
}
},
"item": {
"key": "SOMEKEY",
"name": "anon"
},
"msg": "One or more supplied key could not be found in the database."
}
],
"skipped": false
}
}
But I can't figure out how to isolate this list to ensure the include_tasks: is not called if the user doesn't exist.
- name: loop through the auth_keys and call ssh_dirs.yml for each
ansible.builtin.include_tasks: "ssh_dirs.yaml"
loop: "{{ ssh_vars.auth_keys }}"
when: userlookup.results.???????
How can I figure out how to reference this nested variable, and how best to isolate a non-missing user?
Something like userlookup.results.msg is not defined
might work but it's very loose - is there something better I'm missing?
CodePudding user response:
See registering variables with loop
The global idea is
- loop over you var to get the existing/unavailable users
- loop over the results of that previous task for you next one. The original loop variable is available in the
item
key of each result and you can filter as you like.
For your particular case, in a nutshell (untested):
- name: Ensure the user exists on the target system
ansible.builtin.getent:
database: passwd
key: "{{ item.name }}"
register: userlookup
ignore_errors: true
loop: "{{ ssh_vars.auth_keys }}"
- name: Call ssh_dirs.yml for each existing users
ansible.builtin.include_tasks: ssh_dirs.yaml
loop: "{{ userlookup.results | select('success') }}"
loop_control:
loop_var: user_checked
vars:
userloop: "{{ user_checked.item }}"
CodePudding user response:
I think I've solved it, although maybe there's a better thing to look for in the getent response than just the msg?
The logic and variable reference which works:
- name: "Ensure the user exists on the target system"
ansible.builtin.getent:
database: passwd
key: "{{ item.name }}"
fail_key: false
register: userlookup
loop: "{{ ssh_vars.auth_keys }}"
- name: Build a list of usernames which don't exist on the remote host (missing_users)
ansible.builtin.set_fact:
missing_users: "{{ missing_users | default([]) [usercheck.item.name | string] }}"
loop: "{{ userlookup.results }}"
loop_control:
loop_var: usercheck
when: usercheck.msg is defined
- name: loop through the users and ensure the necessary user folders and files are there
ansible.builtin.include_tasks: "ssh_dirs.yaml"
loop: "{{ ssh_vars.auth_keys }}"
loop_control:
loop_var: userloop
when: userloop.name not in missing_users
Although this is still checking the msg:
output mindlessly so only a partial solution