Deleting multiple users with Ansible ad-hoc command - ansible

When running Ansible ad-hoc to remove users I was unable to feed multiple items to
module. Like this:
ansible -i my_inv all -m user -a"name={{ users }} state=absent" --check --extra-vars='{"users":["user1","user2"]}'
the output is:
server1 | SUCCESS => {
"changed": false,
"name": "['user1', 'user2']",
"state": "absent"
}
it seems to be not opening array correctly.
Making json file also didnt work.
{
"users":["user1","user2"]
}
Is there any way to do it without writing a role?

No.
name parameter of user module takes a string as an argument, not a list.
You need either to loop (and for that you'd need a play - not necessarily a role), or run ansible executable several times.

Related

AnsibleUnsafeText enhancement to an existing playbook

I have a playbook that includes a set of tasks to obtain the status of an IBM MQ Channel. The playbook is passed the Channel name and I run the runmqsc command on each server
and register the output which unfortunately is on many lines, so i need to get this onto one line
echo "DISPLAY CHS('{{CHANNEL_NAME}}') STATUS RQMNAME"| runmqsc {{QMGR}}|grep -v DISPLAY|sed 's/^[^ ].*$/%/g' | tr -s " " | tr -d "\n" | tr "%" "\n"|grep CHANNEL|sed 's/ CURRENT//g' | sed 's/^ //g'|sed 's/ *$//g'
which gave the output
CHANNEL(CHANNEL) CHLTYPE(CLUSSDR) CONNAME(1.2.3.4(1414)) RQMNAME(QMGR) STATUS(RUNNING) SUBSTATE(MQGET) XMITQ(XMIT_QUEUE)
this I parsed to create a list CHSstatus (when CHANNEL_NAME is a wildcard I can have more than one result). From this list I then set variables to the STATUS of the channel.
- set_fact:
allStopped: "{%if splitStatus.STATUS == 'STOPPED'%}{{allStopped + [splitStatus.CHANNEL]}}{%else%}{{allStopped}}{%endif%}"
allRunning: "{%if splitStatus.STATUS == 'RUNNING'%}{{allRunning + [splitStatus.CHANNEL]}}{%else%}{{allRunning}}{%endif%}"
mixedState: "{%if splitStatus.STATUS != 'STOPPED' and splitStatus.STATUS != 'RUNNING'%}{{mixedState + [splitStatus.CHANNEL]}}{%else%}{{mixedState}}{%endif%}"
with_items: "{{CHSstatus}}"
loop_control:
loop_var: splitStatus
This has been working fine for a while with no issues. The problem with this is that you actually need to know the Channel name and as I wanted to enhance it to get the status of all the channels in a CLUSTER I put a 'wrapper' task in front of it to collect the channel names and use these to pass them.
My playbook now calls the MQ command to get these channel names, we can pass the CLUSTER name and register the result in clusterChannels, and include the previously working task with each channel name
- name: Status of Cluster channels
include: channelstatus.yml CHANNEL_NAME={{item}}
with_items: "{{clusterChannels.stdout_lines}}"
This now fails when trying to access the CHSStatus list, I get the error is ERROR! Unexpected Exception: unhashable type: 'dict'
I have tried both methods and compared some variables for each call of the original tasks
Original
"CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]",
"CHSstatus Type = list",
"CHANNEL_NAME = CHANNEL1",
"CHANNEL_NAME type = AnsibleUnicode"
with the wrapper
"CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]",
"CHSstatus Type = list",
"CHANNEL_NAME = CHANNEL1",
"CHANNEL_NAME type = AnsibleUnsafeText"
I have tried to find information about what AnsibleUnsafeText variable is and found that
'Since you are registering the result of a command in a variable, Ansible can't know what will be the content which becomes delivered. Therefore the registered Text output is marked as Unsafe.'
This has confused me a bit as I am running two commands, one to get the list of Channels in the wrapper and another to get the status of each individual channel in the original unchanged code. The registered variable is of type list but when I pass each channel from the wrapper it becomes an AnsibleUnsafeText type, I noticed that each of the registered variables for both commands when run with_items do indeed have an AnsibleUnsafeText type.
Can i convert this in any way? I have seen answers on how to convert to int, and i have tried item|string but this did not work either.
My original playbook used roles and i created a cutdown version of this using tasks only, this included cut down versions of channelstatus.yml and this worked OK, i then converted this to a role again including the same file and this failed.
One thing i notices in both cases of my MQ command registered results gets parsed successfully but the error occurs when I try to use this list of dict.
Example below is a list which contains a single dict, this can be a list of multiple dict's when run
CHSstatus = [{'STATUS': u'RUNNING', 'CHANNEL': u'CHANNEL1'}]
and a simple debug command is enough for this error to fail the playbook...
- debug:
msg:
- "{{item.CHANNEL}}"
- "{{item.STATUS}}"
with_items: "{{CHSstatus}}"
ERROR! Unexpected Exception: unhashable type: 'dict'
however
- debug:
msg:
- "{{CHSstatus[0].CHANNEL}}"
- "{{CHSstatus[0].STATUS}}"
works fine, which really does not make sense
Any help appreciated

How can I check if a playbook is being used by any job template?

We have an organization with several hundred job templates.
I essentially want to do some housekeeping and identify any unused playbooks in my repository.
Is there a way to get details about each job template (including playbook) so that I can grep for a specific playbook?
Is there a way to get details about each job template (including playbook) so that I can grep for a specific playbook?
The short answers is: yes, of course. The long answer is: someone has to create such task. To do so, one may getting familiar with the Ansible Tower REST API, in detail Job Templates - List Job Templates.
In example, a call to List Job Templates
curl --silent --user ${ACCOUNT}:${PASSWORD} https://${TOWER_URL}/api/v2/job_templates/ --write-out "\n%{http_code}\n" | jq .
would result into an output (example) of
{
"count": 29,
"next": "/api/v2/job_templates/?page=2",
"previous": null,
"results": [
{
...
}
]
}
200
results will contain the list of all Job Templates. For further processing one may look for the values of the key playbook in --raw-output only.
curl --silent --user ${ACCOUNT}:${PASSWORD} https://${TOWER_URL}/api/v2/job_templates/ | jq --raw-output '.results[] | .playbook'
One can then just grep over the output.
Further Q&A
Schedule deletion of unused template
Iterate through dictionaries in shell via jq

How to save Ansible ad-hoc result to JSON fast?

For example,
I have 100 IP, I want to collect their hostnames or product type. I know Saltstack has this feature via a simple --output=json # or yaml.
How should I save this result to JSON format even a CSV file fast?
According to Ansible Callback, I can edit ansible.cfg to use a JSON callback. But can I only use specific callback form time to time? I don't want to use specific callback all the time.
If fact_caching is enabled you can according setup_module Examples just run something like
ansible test --ask-pass -m setup --args 'filter=ansible_fqdn'
and find via cat /tmp/ansible/facts_cache/test.example.com a result like
ansible_fqdn: test.example.com
discovered_interpreter_python: /usr/bin/python
depending on your configuration. See Enabling inventory cache plugins for more information.

Use ansible for manual staged rollout using `serial` and unknown inventory size

Consider an Ansible inventory with an unknown number of servers in a nodes key.
The script I'm writing should be usable with different inventories that should be as simple as possible and are out of my control, so I don't know the number of nodes ahead of time.
My command to run the playbook is pretty vanilla and I can freely change it. There could be two separate commands for both rollout stages.
ansible-playbook -i $INVENTORY_PATH playbooks/example.yml
And the playbook is pretty standard as well and can be adjusted:
- hosts: nodes
vars:
...
remote_user: '{{ sudo_user }}'
gather_facts: no
tasks:
...
How would I go about implementing a staged execution without changing the inventory?
I'd like to run one command to execute the playbook for 50% of the inventory first. Here the result needs to be checked manually by a human. Then I'd like to use another command to execute the playbook for the other half. The author of the inventory should not have to worry about this. All machines below the nodes key are the same.
I've looked into the serial keyword, but it doesn't seem like I could automatically end execution after one batch and then later come back to continue with the second half.
Maybe something creative could be done with variables passed to ansible-playbook? I'm just wondering, shouldn't this be a common use-case? Are all staged rollouts supposed to be fully automated?
Without even using serial here is a possible very simple scenario.
First get a calculation of $half of the inventory by inspecting the inventory itself. The following is enabling the json callback plugin for the ad hoc command and making sure it is the only plugin enabled. It is also using jq to parse the result. You can adapt to any other json parser (or even use the yaml callback with a yaml parser if your prefer). Anyway, adapt to your own needs.
half=$( \
ANSIBLE_LOAD_CALLBACK_PLUGINS=1 \
ANSIBLE_STDOUT_CALLBACK=json \
ANSIBLE_CALLBACK_WHITELIST=json \
ansible localhost -i yourinventory.yml -m debug -a "msg={{ (groups['nodes'] | length / 2) | round(0, 'ceil') | int }}" \
| jq -r ".plays[0].tasks[0].hosts.localhost.msg" \
)
Then launch your playbook limiting to the first $half nodes with whatever vars are needed for human check, and launch it again for the remainder nodes without check.
ansible-playbook -i yourinventory.yml example_playbook.yml -l nodes[0:$(($half-1))] -e human_check=true
ansible-playbook -i yourinventory.yml example_playbook.yml -l nodes[$half:] -e human_check=false

Is there any option to list groups in Ansible?

As far as i know, ansible has an option named --list-hosts for listing hosts. Is there any option for listing host groups? Or any other way to come through?
You can simply inspect the groups variable using the debug module:
ansible localhost -m debug -a 'var=groups.keys()'
The above is using groups.keys() to get just the list of groups. You could drop the .keys() part to see group membership as well:
ansible localhost -m debug -a 'var=groups'
Listing groups
Using tools built-in to Ansible + jq, this gives you something reasonably close:
ansible-inventory --list | jq "keys"
An advantage of this approach vs manually parsing inventory files is that it fully utilizes your ansible.cfg files (which can point to one or multiple inventory files, a directory of inventory files, etc...).
Example output on my machine (local_redis_all is a locally defined Ansible group):
[
"_meta",
"all",
"local_redis_all",
]
If you prefer it in plain-text form, use an approach like ansible-inventory --list | jq -r "keys | .[]". It will give you an output like this:
_meta
all
local_redis_all
Listing hosts
This was not part of the original question, but including it here anyway since it might be useful to some of my readers. Use the following command for JSON output (note: the command actually outputs a JSON array for each group, I haven't yet figured out a way to flatten these with jq - suggestions are welcome):
ansible-inventory --list | jq ".[].hosts | map(.)?
This gives you an output similar to this:
[
"redis-01",
"redis-02",
"redis-03"
]
Likewise, in raw text format (one host per line): ansible-inventory --list | jq -r ".[].hosts | .[]?"
This gives you an output like this:
redis-01
redis-02
redis-03
Note:- For New Ansible Users
Ansible has some special internal variables which are also known as Magic Variables.
From this link you will get full list of magic variables Magic Variables
There is magic variable called "groups" which hold the inventory group information.
we can access the value of any variable ( both user defined and Internal ) using an ansible module called debug .
I am Using Separate Inventory File
$
$ ansible -i inventory.ini all -m debug -a "var=groups"
$
centos-client.ansible.lab | SUCCESS => {
"groups": {
"all": [
"centos-client.ansible.lab",
"ubuntu-client.ansible.lab"
],
"centos": [
"centos-client.ansible.lab"
],
"ubuntu": [
"ubuntu-client.ansible.lab"
],
"ungrouped": []
}
}
ubuntu-client.ansible.lab | SUCCESS => {
"groups": {
"all": [
"centos-client.ansible.lab",
"ubuntu-client.ansible.lab"
],
"centos": [
"centos-client.ansible.lab"
],
"ubuntu": [
"ubuntu-client.ansible.lab"
],
"ungrouped": []
}
}
Method #1 - Using Ansible
If you just want a list of the groups within a given inventory file you can use the magic variables as mentioned in a couple of the other answers.
In this case you can use the groups magic variable and specifically just show the keys() in this hash (keys + values). The keys are all the names of the groups.
NOTE: By targeting the localhost we force this command to just run against a single host when processing the inventory file.
$ ansible -i inventory/rhvh localhost -m debug -a 'var=groups.keys()'
localhost | SUCCESS => {
"groups.keys()": "dict_keys(['all', 'ungrouped', 'dc1-rhvh', 'dc2-rhvh', 'dc3-rhvh', 'dc4-rhvh', 'dc5-rhvh', 'rhvh', 'dc1', 'dc2', 'dc3', 'dc4', 'dc5', 'production'])"
}
Method #2 - using grep & sed
You could of course just grep the contents of your inventory file too:
$ grep -E '^\[' inventory/rhvh
[dc1-rhvh]
[dc2-rhvh]
[dc3-rhvh]
[dc4-rhvh]
[dc5-rhvh]
[rhvh:children]
[dc1:children]
[dc2:children]
[dc3:children]
[dc4:children]
[dc5:children]
[production:children]
You're on the hook though for teasing out of the 2nd method's output or you can use sed to do it:
$ grep -E '^\[' inventory/rhvh | sed 's/:children//'
[dc1-rhvh]
[dc2-rhvh]
[dc3-rhvh]
[dc4-rhvh]
[dc5-rhvh]
[rhvh]
[dc1]
[dc2]
[dc3]
[dc4]
[dc5]
If you're not sure if the host will actually be in the inventory you can use:
ansible -i hosts/ localhost -m debug -a 'var=groups'
-i where your inventory files are kept
-m enable module debug.
-a module arguments.
It will output the group / hosts just once and not for every host in your inventory.
Same goes for just getting the list of groups in the inventory:
ansible -i hosts/ localhost -m debug -a 'var=groups.keys()'
another way to see your hosts is just to press tab after the ansible keyword.
Something like that?
cat ~/inventory/* | grep "\[.*\]"

Resources