How to do an Ansible condition test of user/group existence? - ansible

As new nodes (CentOS 7.6) are added, there are basic groups and users that need to be created. Some of the nodes have some of the groups and users. I would like to only create the groups and users on the nodes where they don't exist via my Ansible (version 2.8.0) basic role file.
Currently, I'm testing for the group/user, but the there's always a "fatal" printed and my conditionals don't appear to work.
roles/basic/tasks/main.yml
- name: "Does k8s group exist?"
shell: grep -q "^k8s" /etc/group
register: gexist
- name: "Create k8s group"
shell: groupadd -g 8000 k8s
when: gexist.rc != 0
- name: "Does k8s user exist?"
shell: id -u k8s > /dev/null 2>&1
register: uexist
- name: "Create k8s user"
shell: useradd -g 8000 -d /home/k8s -s /bin/bash -u 8000 -m k8s
when: uexist.rc != 0
which yields:
TASK [basic : Does k8s group exist?] *****************************************************************************************************************************
fatal: [master]: FAILED! => {"changed": true, "cmd": "grep -q \"^k8s:\" /etc/group", "delta": "0:00:00.009424", "end": "2019-05-29 14:42:17.947350", "msg": "non-zero return code", "rc": 1, "start": "2019-05-29 14:42:17.937926", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
fatal: [node3]: FAILED! => {"changed": true, "cmd": "grep -q \"^k8s:\" /etc/group", "delta": "0:00:00.012089", "end": "2019-05-29 06:41:36.661356", "msg": "non-zero return code", "rc": 1, "start": "2019-05-29 06:41:36.649267", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
fatal: [node1]: FAILED! => {"changed": true, "cmd": "grep -q \"^k8s:\" /etc/group", "delta": "0:00:00.010104", "end": "2019-05-29 14:42:17.990460", "msg": "non-zero return code", "rc": 1, "start": "2019-05-29 14:42:17.980356", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
changed: [node2]
There has got to be a better to do conditionals (if-then-else) than the way I'm doing it.

See user and group. The code below is probably what you're looking for.
- name: "Create k8s group"
group:
gid: 8000
name: k8s
- name: "Create k8s user"
user:
group: k8s
home: /home/k8s
shell: /bin/bash
uid: 8000
name: k8s
The only if-then-else in Ansible I'm aware of is the ternary filter (for other options see jinja). The control flow is rather poor in Ansible compared to other procedural languages. It's because of the code rather defines a state of the system then a procedure.
To answer your question:
How to do an Ansible condition test of user/group existence?
Your code does it correctly, but the purpose of Ansible is to define the state of the system. It's not important a user or group existed before or not. After successfully having run the code they will exist (definition of a state) and running the code again makes sure they still exist (audit).

Related

How to repeat the same command in creating the openstack backup?

I took volumes 'in-use' of OpenStack instance and filtered those volume ids into a file from which it has to make a backup
shell: openstack volume list | grep 'in-use' | awk '{print $2}' > /home/volumeid
shell: openstack volume backup create {{ item }}
with_items:
- /home/volumeid
error shows like
**failed: [controller2] (item=volumeid) => {"ansible_loop_var": "item", "changed": true, "cmd": "openstack volume backup create volumeid", "delta": "0:00:03.682611", "end": "2022-09-26 12:01:59.961613", "item": "volumeid", "msg": "non-zero return code", "rc": 1, "start": "2022-09-26 12:01:56.279002", "stderr": "No volume with a name or ID of 'volumeid' exists.", "stderr_lines": ["No volume with a name or ID of 'volumeid' exists."], "stdout": "", "stdout_lines": []}
failed: [controller1] (item=volumeid) => {"ansible_loop_var": "item", "changed": true, "cmd": "openstack volume backup create volumeid", "delta": "0:00:04.020051", "end": "2022-09-26 12:02:00.280130", "item": "volumeid", "msg": "non-zero return code", "rc": 1, "start": "2022-09-26 12:01:56.260079", "stderr": "No volume with a name or ID of 'volumeid' exists.", "stderr_lines": ["No volume with a name or ID of 'volumeid' exists."], "stdout": "", "stdout_lines": []}**
Can someone say how to create the volume backup from that file (which has volume ids) in the ansible playbook?
Currently, you are supplying only one element to the with_items, that is, /home/volumeid, meaning your loop will iterate only once for the file name and not its contents.
You need to use the file lookup if you are on localhost or the slurp module on the remote host. Example:
For the localhost:
- name: Show the volume id from the file
debug:
msg: "{{ item }}"
loop: "{{ lookup('file', '/home/volumeid').splitlines() }}"
For the remote host:
- name: Alternate if the file is on remote host
ansible.builtin.slurp:
src: /home/volumeid
register: vol_data
- name: Show the volume id from the file
debug:
msg: "{{ item }}"
loop: "{{ (vol_data['content'] | b64decode).splitlines() }}"
Just one line shell command:
openstack volume list --status in-use -c ID -f value | xargs -n1 openstack volume backup create
One advice, don't use the hardcode command like this grep 'in-use' or awk '{print $2}', openstack has it's optional arguments and output formatters, check it by openstack command [sub command] -h.

Ansible unzip error/unarchive not fitting

i try to get the version within an .war file. In a script this was done by:
/usr/bin/unzip -p tomcat.war inside/path/to/version.txt | /bin/grep "version"
This is working. When i use the shell command in Ansible like this:
- name: Check .war Version
ignore_errors: yes
shell: /usr/bin/unzip -p tomcat.war inside/path/to/version.txt | /bin/grep "version"
register: war_version
It's telling me:
fatal: [localhost]: FAILED! => {"changed": true, "cmd":
"/usr/bin/unzip -p tomcat.war inside/path/to/version.txt | /bin/grep
"version"", "delta": "0:00:00.005014", "end": "2021-11-01
07:36:49.885688", "msg": "non-zero return code", "rc": 1, "start":
"2021-11-01 07:36:49.880674", "stderr": "", "stderr_lines": [],
"stdout": "", "stdout_lines": []}
With the builtin unarchive i think it's not possible to do this clean in one step. I would need to unarchive to a temp folder, grep the version, and delete the folder again. Is there a workaround with the unarchive module, or does anyone know how to fix the shell error? When i set ignore_errors to yes, it's throwing the error in my {{ war_version }}.
Regards

unable to fetch a binary path with ansible shell module

Team,
Works locally on my laptop manually but fails only when calling via ansible. Is ansible looking at it in different shell? I have ansible controller as MAC book.
I am trying to store the path of a binary in register variable but shell command is not executing.
- name: "Find kubectl binary"
register: kubectl_path
shell: which kubectl
args:
executable: /bin/bash
output:
TASK [Find kubectl binary] ***************************************************************************************************************
fatal: [target1]: FAILED! => {"changed": true, "cmd": "which kubectl", "delta": "0:00:00.007541", "end": "2019-10-01 17:17:45.515963", "msg": "non-zero return code", "rc": 1, "start": "2019-10-01 17:17:45.508422", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

how to extract string from ansible regsiter variable in ansible

I have written the following ansible playbook to find the disk failure on the raid
- name: checking raid status
shell: "cat /proc/mdstat | grep nvme"
register: "array_check"
- debug:
msg: "{{ array_check.stdout_lines }}"
Following is the output I got
"msg": [
"md0 : active raid1 nvme0n1p1[0] nvme1n1p1[1]",
"md2 : active raid1 nvme1n1p3[1](F) nvme0n1p3[0]",
"md1 : active raid1 nvme1n1p2[1] nvme0n1p2[0]"
]
I want to extract the disk name which is failed from the register variable array_check.
How do I do this in the ansible? Can I use set_fact module in ansible? Can I use grep, awk, sed command on the register variable array_check
This is the playbook I am using to check the health status of a drive using smartctl
- name: checking the smartctl logs
shell: "smartctl -H /dev/{{ item }}"
with_items:
- nvme0
- nvme1
And I am facing the following error
(item=nvme0) => {"changed": true, "cmd": "smartctl -H /dev/nvme0", "delta": "0:00:00.090760", "end": "2019-09-05 11:21:17.035173", "failed": true, "item": "nvme0", "rc": 127, "start": "2019-09-05 11:21:16.944413", "stderr": "/bin/sh: 1: smartctl: not found", "stdout": "", "stdout_lines": [], "warnings": []}
(item=nvme1) => {"changed": true, "cmd": "smartctl -H /dev/nvme1", "delta": "0:00:00.086596", "end": "2019-09-05 11:21:17.654036", "failed": true, "item": "nvme1", "rc": 127, "start": "2019-09-05 11:21:17.567440", "stderr": "/bin/sh: 1: smartctl: not found", "stdout": "", "stdout_lines": [], "warnings": []}
The desired output should be something like this,
=== START OF SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED
Below is the complete playbook including the logic to execute multiple commands in a single task using with_items,
---
- hosts: raid_host
remote_user: ansible
become: yes
become_method: sudo
tasks:
- name: checking raid status
shell: "cat /proc/mdstat | grep 'F' | cut -d' ' -f6 | cut -d'[' -f1"
register: "array_check"
- debug:
msg: "{{ array_check.stdout_lines }}"
- name: checking the samrtctl logs for the drive
shell: "/usr/sbin/smartctl -H /dev/{{ item }} | tail -2|awk -F' ' '{print $6}'"
with_items:
- "nvme0"
- "nvme1"
register: "smartctl_status"

Ansible task fails, when creating extensions

While provisioning via Vagrant and Ansible I keep running into this issue.
TASK [postgresql : Create extensions] ******************************************
failed: [myapp] (item=postgresql_extensions) => {"changed": true, "cmd": "psql myapp -c 'CREATE EXTENSION IF NOT EXISTS postgresql_extensions;'", "delta": "0:00:00.037786", "end": "2017-04-01 08:37:34.805325", "failed": true, "item": "postgresql_extensions", "rc": 1, "start": "2017-04-01 08:37:34.767539", "stderr": "ERROR: could not open extension control file \"/usr/share/postgresql/9.3/extension/postgresql_extensions.control\": No such file or directory", "stdout": "", "stdout_lines": [], "warnings": []}
I'm using a railsbox.io generated playbook.
Turns out that railsbox.io is still using a deprecated syntax in the task.
- name: Create extensions
sudo_user: '{{ postgresql_admin_user }}'
shell: "psql {{ postgresql_db_name }} -c 'CREATE EXTENSION IF NOT EXISTS {{ item }};'"
with_items: postgresql_extensions
when: postgresql_extensions
The last line should use full jinja2 syntax.
when: '{{postgresql_extensions}}'

Resources