Ansible user module check if a user exists or not? - ansible

The requirement is for Ansible user module just check if a user exists and do not take any action.
Does check_mode help here? How should such a playbook task be written?
This link, Ansible playbook to check user exist or display error message provides an alternative. Is it possible to get this done with builtin user module?

In a nutshell and for the most basic check (i.e. username is there regardless of any other user configuration):
---
- name: Check if users exists
hosts: localhost
gather_facts: false
become: true
vars:
users_to_test:
- daemon # This one should exist, at least on ubuntu
- a_non_existing_user
tasks:
- name: Check if users exist
ansible.builtin.user:
name: "{{ item }}"
loop: "{{ users_to_test }}"
check_mode: true
register: test_users
- name: Report
ansible.builtin.debug:
msg: "User {{ item.item }} {{ 'exists' if item.state | d('') == 'present' else 'does not exist' }}"
loop: "{{ test_users.results }}"
loop_control:
label: "{{ item.item }}"
Which give on my ubuntu 20 local machine:
$ ansible-playbook testuser.yml
PLAY [Check if users exists] ********************************************************************************************************************************************************
TASK [Check if users exist] *********************************************************************************************************************************************************
ok: [localhost] => (item=daemon)
changed: [localhost] => (item=a_non_existing_user)
TASK [Report] ***********************************************************************************************************************************************************************
ok: [localhost] => (item=daemon) => {
"msg": "User daemon exists"
}
ok: [localhost] => (item=a_non_existing_user) => {
"msg": "User a_non_existing_user does not exist"
}

Since Ansible is mainly a Configuration Management Tool in which one can declare a Desired State
The requirement is for Ansible user module just check if a user exists and do not take any action.
this is already and mostly the default behavior of the user module. If the user already exists and there are no changes necessary the module will just return a changed: false and reports OK. So it will not take any action then.
For a simple check only if a user exists you've found already the Ansible playbook to check user exist via getent module. Please take note that it is not an "alternative" compared to the user module.
Does check_mode help here?
Since check_mode is mainly for Validating tasks and
Check mode is just a simulation ... it is great for validating configuration management playbooks that run on one node at a time.
it will mainly depend on what you try to achieve and how a run should behave.
Further Readings and Q&A
An Ansible playbook for solving a new problem from scratch
Ansible: Only disable existing users?
How to lock a user using Ansible?

Related

Ansible playbook to check user exist or display error message

How to check if user is present and
if present, continue with next task
if not present display a message saying Given user does not exist
You can simply use the getent module.
- name: get root user info
getent:
database: passwd
key: root
If the user is present, the play will continue. If the user does not exist, the play will fail.
Depending on what you try to achieve and for further enhancements you could procceed further with reading the account database fully
Runs getent against one of it’s various databases and returns information into the host’s facts, in a getent_ prefixed variable
---
- hosts: localhost
become: yes
gather_facts: false
vars:
USER: "test"
tasks:
- name: Gather available local users
getent:
database: passwd
- name: Show gathered local user names only
debug:
msg: "{{ item }}"
loop: "{{ getent_passwd.keys() | list }}"
when: ansible_check_mode
and later assert if given expression are true
- name: Check if local user account '{{ USER }}' exists
assert:
that:
- USER in getent_passwd.keys()
fail_msg: "Given user not exists!"
success_msg: "Given user exists."
resulting into an output of
TASK [Gather available local users] ****************
ok: [localhost]
TASK [Check if local user account 'test' exists] ***
fatal: [localhost]: FAILED! => changed=false
assertion: USER in getent_passwd.keys()
evaluated_to: false
msg: Given user not exists!
It would also be possible to just end_play by using
- block:
- name: Show message
debug:
msg: "Given user not exists!"
- meta: end_play
when: USER not in getent_passwd.keys()
Thanks to
Is there a way to check that a dictionary key is not defined in Ansible task?
Further Readings
Ansible getent query LDAP users

Ansible - Multiple/ Alternative hostnames for the same host

Assume I have hosts with multiple (DNS) names/IPs, e.g. because they have multiple NICs and thus routes to reach them.
I want to play a playbook in case one of these routes fails. Since I do not know which one works, I would like ansible to try all of them and then play the book only once for this host. It would be easy to put all the host's names into the inventory and let it run, but then the playbook would be executed once for each name of the host.
Question: Is there a way to specify alternative host names or to tell ansible to run the playbook only on one host per group?
It can be implemented
to run the playbook only on one host per group
See example below.
- hosts: jails-01
strategy: linear
vars:
lock_file: /var/lock/my_ansible_hostname.lock
tasks:
- name: delete lock_file
file:
path: "{{ lock_file }}"
state: absent
run_once: true
delegate_to: localhost
- name: select host
shell: "echo {{ ansible_hostname }} > {{ lock_file }}"
args:
creates: "{{ lock_file }}"
delegate_to: localhost
- name: winner takes it all
fail:
msg: "Too late. Other thread is running. End of play."
when: lookup('file', lock_file) != ansible_hostname
- name: go ahead
debug:
msg: "{{ ansible_hostname }} goes ahead ... "
# ansible-playbook playbook.yml | grep msg
fatal: [test_01]: FAILED! => {"changed": false, "msg": "Too late. Other thread is running. End of play."}
fatal: [test_03]: FAILED! => {"changed": false, "msg": "Too late. Other thread is running. End of play."}
"msg": "test_02 goes ahead ... "

ansible playbook run a task only once

I need to run a task ,set a flag and the second time the play runs run the task only if the flag is not set
Play at a later stage
- name: Dump all databases
mysql_db:
state: dump
name: all
target: /root/mysql_all.sql
when: ansible_local.mysql.replication.setup is not defined
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom fact stating mysql is setup
template:
src: mysql.fact.j2
dest: /etc/ansible/facts.d/mysql.fact
The problem is that the fist time this play runs its throwing an error.
FAILED! => {"failed": true, "msg": "The conditional check 'ansible_local.mysql.replication.setup is not defined' failed. The error was: error while evaluating conditional (ansible_local.mysql.replication.setup is not defined): 'ansible_local' is undefined
What is the best way to run a task only in the first run and skip for subsequent runs.
you should make a task prior that registers if /root/mysql_all.sql exists then add it to your when clause.
Example:
- name: check if dump exists
stat:
path: /root/mysql_all.sql
register: mysqldump
- name: Dump all databases
mysql_db:
state: dump
name: all
target: /root/mysql_all.sql
when:
- ansible_local.mysql.replication.setup is not defined
- mysqldump.stat.exists == true
- name: create directory for ansible custom facts
file: state=directory recurse=yes path=/etc/ansible/facts.d
- name: install custom fact stating mysql is setup
template:
src: mysql.fact.j2
dest: /etc/ansible/facts.d/mysql.fact
One option is to utilize the fact cache. When enabled, you can set cacheable facts in your play and check for them.
Fact caching always takes place. There are various fact cache plug-ins available of which the memory plug-in is the default, and json file and redis cache are the most popular. You can only set one plug-in. Refer to
https://docs.ansible.com/ansible/latest/plugins/cache.html
When you want to explore with the json file you can set the environment variables as follows:
export ANSIBLE_CACHE_PLUGIN=jsonfile
export ANSIBLE_CACHE_PLUGIN_CONNECTION="~/ansiblefactcache"
In your play book you can check for facts and set them as cacheable, Refer to
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/set_fact_module.html
A small example playbook.yaml:
- name: Example Fact Cache Playbook
hosts: all
gather_facts: false # the default is true; it gathers various host facts
tasks:
- name: Runs when examplefact equals something
debug:
msg: "Runs when examplefact equals something"
when: ansible_facts['examplefact'] is defined and ansible_facts['examplefact'] == "something"
- name: Does not run when examplefact equals something
debug:
msg: "does not run when examplefact equals something"
when: ansible_facts['examplefact'] is not defined or ansible_facts['examplefact'] != "something"
- name: Set the examplefact to something
set_fact:
examplefact: "something"
cacheable: true
Note the usage of the cacheable instruction. When true the fact goes into the cache.
After having run this small playbook you will notice the creation of a localhost file in your home's subfolder ansiblefactcache which contains your cached facts.
Also note the usage of the gather_facts instruction. The default being true will scan your machine for various details such as environment variables, network details, etc. All are cached. You can play with it and see the localhost file being populated with it.
You can also try to edit the localhost file yourself or even delete it and run the play again.
I used the following inventory file inventory.yaml:
all:
hosts:
localhost:
ansible_connection: local
And I run ansible as follows:
ansible-playbook playbook.yaml -i inventory.yaml -vvv
First run yields the following:
PLAY [Example Fact Cache Playbook] ********************************************************************************************************************************************************************************
TASK [Runs when examplefact equals something] *********************************************************************************************************************************************************************
skipping: [localhost]
TASK [Does not run when examplefact equals something] *************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "does not run when examplefact equals something"
}
TASK [Set the examplefact to something] ***************************************************************************************************************************************************************************
ok: [localhost]
Second run yields the following:
PLAY [Example Fact Cache Playbook] ********************************************************************************************************************************************************************************
TASK [Runs when examplefact equals something] *********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Runs when examplefact equals something"
}
TASK [Does not run when examplefact equals something] *************************************************************************************************************************************************************
skipping: [localhost]
TASK [Set the examplefact to something] ***************************************************************************************************************************************************************************
ok: [localhost]

ansible user current user in configuration

I am using ansible to configure the several computers after installation.
For this I run ansible locally on the machines. The "main" user on the installation has often a different name. I want to use that user for variables like become_user. The "main" user is also the user, who calls ansible-playbook.
So can I somehow set "become_user" to the user who called ansible-playbook?
Not sure why you need to set become_user to user you are already running your playbook with, but you can use env lookup to get USER environment variable:
- hosts: localhost
tasks:
- debug: msg="{{ lookup('env','USER') }}"
You can logon locally on control host as 'nathan', but want to connect to other servers as user 'ansible' (better in ansible.cfg)
remote_user = ansible
If you want on remote host connect as 'ansible' and perform one task as root or apache -- then sudo to root (apache or other user) you should use become_user for this particular task.
Please note also, than remote server may NOT have such user as on control host! (In common way)
In your particular case if you logon locally as 'nathan' and want to connect to 'remote' server as 'nathan' you should omit both remote_user and become_user: just logon with your current credentials!
For example, there's two sysadminst in organization: nathan and peter -- so, there's two workstation (heidelberg-nathan and berlin-peter) as ansible control host and thousands clients. Both nathan and peter connect to remote side as nathan or peter and perform tasks. Each of them can non-password sudoers to perform admin tasks.
PS Ok, let's test both solution (first - from Konstantin Suvorov's answer, second -- from knowhy's answer).
My control host berlin-ansible-01, i'm logged in as 'nathan'. Remote client is host berlin-client-01. I will log into client host as user 'ansible'.
My ansible.cfg is:
[defaults]
sudo_flags=-HE
hash_behaviour = merge
retry_files_enabled = false
log_path = ./main.log
ask_vault_pass=true
remote_user = ansible
Playbook is simple:
- name: test
hosts: '{{ target }}'
tasks:
- debug: msg="step 1 = {{ lookup('env','USER') }}"
- setup:
- debug: msg="step 2 = {{ hostvars[target].ansible_env.USER }}"
#more than one client in taget needs iterate items:
# - debug: msg="step 2 = {{ hostvars[item].ansible_env.USER }}"
# with_items: "{{ hostvars }}"
Let's run it:
[nathan#berlin-ansible-01 stackoverflow]$ ansible-playbook -i hosts_staging test.yml --extra-vars "target=berlin-client-01"
Vault password:
PLAY [test] ********************************************************************
TASK [setup] *******************************************************************
ok: [berlin-client-01]
TASK [debug] *******************************************************************
ok: [berlin-client-01] => {
"msg": "step 1 = nathan"
}
TASK [setup] *******************************************************************
ok: [berlin-client-01]
TASK [debug] *******************************************************************
ok: [berlin-client-01] => {
"msg": "step 2 = ansible"
}
PLAY RECAP *********************************************************************
berlin-client-01 : ok=4 changed=0 unreachable=0 failed=0
ansible-playbook provides the --become-user CLI flag along with --ask-become-pass (if needed).
In most cases, this is a bad setup. You should standardize the user on all of your machines else you'll have to maintain certs/passwords for each user separately.
There is no need to set become_user when the playbook should run with the user who started ansible-playbook
become is for privilege escalation. If I got this question right privilege escalation is not needed.
The name of the user which runs the playbook is available as an ansible fact {{ ansible_env.username }}

Ansible - Reference 'ansible_ssh_pass' in a task?

I understand that the ansible_ssh_pass (and similarly ansible_become_pass) variables are settable via inventories. E.g.:
[some_group:vars]
ansible_ssh_pass=some_password
But is the same referencable from a task without explicitly setting it in an inventory? E.g. if I simply provide the password with --ask-pass?
The use case would be to mount a CIFS share with an authorized account (which would simply be a user's SSH account as we have Active Directory in our environment). I've tried using the documented variables, e.g.:
- name: Mount a drive
sudo: true
mount: state="mounted" fstype="cifs" opts="username={{ ansible_ssh_user }}, password={{ ansible_ssh_pass }} src=..."
But this results in an error:
fatal: [some.machine] => One or more undefined variables: 'ansible_ssh_pass' is undefined
Regarding your question
But is the same referencable from a task without explicitly setting it in an inventory? E.g. if I simply provide the password with --ask-pass?
the short answer is yes. A test remote playbook
---
- hosts: test
become: yes
gather_facts: no
tasks:
- name: Show variables
debug:
msg:
- "Provided user: {{ ansible_user }}"
- "Provided password: {{ ansible_password }}"
called via
ansible-playbook --user ${ADMIN_USER} --ask-pass remote.yml
results into an output of
TASK [Show variables] ***********
ok: [test.example.com] =>
msg:
- 'Provided user: admin_user'
- 'Provided password: 12345678'
just providing the given password.

Resources