I would like to count the number of lines in a file with playbook, but the playbook should run remotely. It should be the equivalent of the linux command:
cat /etc/passwd |wc -l
Now I use this as workaround:
- name: Count number of users
shell: 'cat /etc/passwd |wc -l'
register: usercount
- name: Write the user count
debug:
msg: "{{ usercount.stdout }}"
but it will be nice if it should work with an Ansible builtin command.
Lookup of files execute on the controller, so, I would say that your best bet would be to slurp the files then count the lines.
So, with the string representation of the files, you can use the splitlines function of Python to end up with a list you can then feed to a simple length filter.
Here would be an example playbook:
- hosts: node1
gather_facts: no
tasks:
- slurp:
src: /etc/passwd
register: passwd
- debug:
var: (passwd.content | b64decode).splitlines() | length
This would yield the recap:
PLAY [node1] ******************************************************************************************************
TASK [slurp] ******************************************************************************************************
ok: [node1]
TASK [debug] ******************************************************************************************************
ok: [node1] =>
(passwd.content | b64decode).splitlines() | length: '27'
PLAY RECAP ********************************************************************************************************
node1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Related
I tried lookup on machine installed on it ansible and it works, but when uploading playbook to awx it does not work.
- name: get file
set_fact:
policer: "{{ lookup('file', 'file.txt') }}"
it gives An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup"
although the file in the same repo of playbook, and have worked on machine but not awx. And if there is something specific to remote hosts how to know the path
Ansible Lookup plugins "execute and are evaluated on the Ansible control machine."
The play works fine when you run it at the localhost (control machine) where the file is located
shell> hostname
test_11
shell> cat /tmp/file.txt
content of file /tmp/file.txt
shell> cat pb1.yml
- hosts: localhost
vars:
policer: "{{ lookup('file', '/tmp/file.txt') }}"
tasks:
- debug:
var: policer
shell> ansible-playbook pb1.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When you move to another controller (might be AWX) the file remains at the remote host (test_11 in the example) and is not available at the localhost controller. You can test it. See the block below
shell> hostname
awx
shell> cat /tmp/file.txt
cat: /tmp/file.txt: No such file or directory
shell> ssh admin#test_11 cat /tmp/file.txt
content of file /tmp/file.txt
shell> cat pb2.yml
- hosts: test_11
vars:
policer: "{{ lookup('file', '/tmp/file.txt') }}"
tasks:
- block:
- stat:
path: /tmp/file.txt
register: st
- debug:
var: st.stat.exists
delegate_to: localhost
- debug:
var: policer
shell> ansible-playbook pb2.yml
PLAY [test_11] *******************************************************************************
TASK [stat] **********************************************************************************
ok: [test_11 -> localhost]
TASK [debug] *********************************************************************************
ok: [test_11 -> localhost] =>
st.stat.exists: false
TASK [debug] *********************************************************************************
[WARNING]: Unable to find '/tmp/file.txt' in expected paths (use -vvvvv to see paths)
fatal: [test_11]: FAILED! =>
msg: 'An unhandled exception occurred while templating ''{{ lookup(''file'', ''/tmp/file.txt'') }}''. Error was a <class ''ansible.errors.AnsibleError''>, original message: An unhandled exception occurred while running the lookup plugin ''file''. Error was a <class ''ansible.errors.AnsibleError''>, original message: could not locate file in lookup: /tmp/file.txt. could not locate file in lookup: /tmp/file.txt'
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
There are many options for how to fix it. The trivial one is moving also the file to the localhost controller. If the file remains at the remote host you can either read it by command or slurp, or fetch it. The module command always reports changed. The modules slurp and fetch are idempotent.
Read the file by command
shell> cat pb3.yml
- hosts: test_11
vars:
policer: "{{ out.stdout }}"
tasks:
- command: cat /tmp/file.txt
register: out
- debug:
var: policer
shell> ansible-playbook pb3.yml
PLAY [test_11] *******************************************************************************
TASK [command] *******************************************************************************
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Read the file by slurp. This should be used for smaller files only because (quoting from slurp): "This module returns an ‘in memory’ base64 encoded version of the file, take into account that this will require at least twice the RAM as the original file size."
shell> cat pb4.yml
- hosts: test_11
vars:
policer: "{{ out.content|b64decode }}"
tasks:
- slurp:
path: /tmp/file.txt
register: out
- debug:
var: policer
shell> ansible-playbook pb4.yml
PLAY [test_11] *******************************************************************************
TASK [slurp] *********************************************************************************
ok: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: |-
content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The next option is to fetch the file to the local directory dest (will be created). By default, the file(s) is stored in the directory named by the remote host
shell> cat pb5.yml
- hosts: test_11
vars:
file_path: "/tmp/fetched_files/{{ inventory_hostname }}/tmp/file.txt"
policer: "{{ lookup('file', file_path) }}"
tasks:
- fetch:
src: /tmp/file.txt
dest: /tmp/fetched_files
- debug:
var: policer
shell> ansible-playbook pb5.yml
PLAY [test_11] *******************************************************************************
TASK [fetch] *********************************************************************************
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> cat /tmp/fetched_files/test_11/tmp/file.txt
content of file /tmp/file.txt
Notes
An absolute path to the file is used in the examples. See The magic of ‘local’ paths on how to use relative paths locally.
I'm new to Ansible I'm trying to run this command to verify AIDE is installed by running in Ansible. when I run my playbook I get non-zero return code error. I'm not sure if single quotes or double quotes needed for my playbook to run correctly. Please I need assistance.
- name: "Verify AIDE to cryptographic mechanisms are set to protect the integrity of audit tools"
become: true
shell: egrep '(\/usr\/sbin\/(audit|au|rsys))' /etc/aide.conf
register: AIDE_Status
I tried to add double quotes between parenthesis of the command returned the same error. Moved quotes between egrep and /etc/aide.conf.
The egrep works as expected. For example, given the file for testing
shell> cat /tmp/aide.conf
\usr\sbin\audit
the playbook
shell> cat pb.yml
- hosts: localhost
tasks:
- command: egrep '\usr\sbin\(audit|au|rsys)' /tmp/aide.conf
register: aide_status
- debug:
var: aide_status
gives
shell> ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [command] *******************************************************************************
changed: [localhost]
TASK [debug] *********************************************************************************
ok: [localhost] =>
aide_status:
changed: true
cmd:
- egrep
- \usr\sbin\(audit|au|rsys)
- /tmp/aide.conf
delta: '0:00:00.003672'
end: '2022-10-26 22:14:13.735346'
failed: false
msg: ''
rc: 0
start: '2022-10-26 22:14:13.731674'
stderr: ''
stderr_lines: []
stdout: \usr\sbin\audit
stdout_lines:
- \usr\sbin\audit
PLAY RECAP ***********************************************************************************
localhost: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I am using the below Ansible code to get the file system details (NAME,MOUNTPOINT,FSTYPE,SIZE) from node servers to control server. I am not getting any issues while running the playbook. But the CSV file is not copied to control machine.
Can anyone please help me on this?
tasks:
- name: Fsdetails
shell: |
lsblk -o NAME,MOUNTPOINT,FSTYPE,SIZE > $(hostname).csv
register: fsdetails_files_to_copy
- name: Fetch the fsdetails
fetch:
src: "{{ item }}"
dest: /data3/deployments/remediation
flat: yes
with_items: "{{ fsdetails_files_to_copy.stdout_lines }}"
Output:
PLAY [all] ************************************************************************************************
TASK [Gathering Facts] ************************************************************************************
ok: [10.xxx.xxx.xx]
TASK [Fsdetails] ******************************************************************************************
changed: [10.xxx.xxx.xx]
TASK [Fetch the fsdetails] ********************************************************************************
PLAY RECAP ************************************************************************************************
10.xxx.xxx.xx : ok=2 changed=1 unreachable=0 failed=0
Your shell command is not returning anything, since it is writing the output to the CSV file. Because of this, your fetch task has nothing to loop on (stdout_lines is an empty list).
What you could do is make your shell task echo the CSV name $(hostname):
- name: Fsdetails
shell: |
lsblk -o NAME,MOUNTPOINT,FSTYPE,SIZE > $(hostname).csv && echo $(hostname).csv
register: fsdetails_files_to_copy
This way, your fetch task will pick the correct filename to download.
I want to save the hosts name and results of linux command in the dictionary format. The problem is I cannot successfully get the dictionary format, and the new line stored in the results.txt file will replace the previous lines.
---
- hosts: "{{variable_host | default('lsbxdmss001')}}"
tasks:
- name: Check Redhat version for selected servers
shell:
cmd: rpm --query redhat-release-server
warn: False
register: myshell_output
- debug: var=myshell_output
- name: set fact
set_fact: output = "{{item.0}}:{{item.1}}"
with_together:
- groups['{{variable_host}}']
- "{{myshell_output.stdout}}"
register: output
- debug: var=output
- name: copy the output to results.txt
copy:
content: "{{output}}"
dest: results.txt
delegate_to: localhost
Looks like you have four issues:
Your dictionary creation seems odd, a dictionary in python is enclosed in curly bracers { ... }
So you line dictionary
"{{item.0}}:{{item.1}}"
Should rather be
"{{ {item.0: item.1} }}"
Adding to a file, in Ansible, is done via the module lineinfile rather than the copy one.
Your loop doesn't really makes sense, as Ansible is already executing each task on all hosts, so the reason you have only one information is also coming from this: you keep on overriding the output fact, in your trial to do it.
For the same reason as above, instead of your with_together loop, you should use the special variable inventory_hostname, as this is
The inventory name for the ‘current’ host being iterated over in the play
Source: https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html#special-variables
So after your shell command, here should be the next task:
- lineinfile:
path: results.txt
line: "{{ {inventory_hostname: myshell_output.stdout} | string }}"
create: yes
delegate_to: localhost
Mind that the string filter was added in order to convert the dictionary into a string to write it into the file without Ansible issuing a warning.
An example of playbook would be:
- hosts: all
gather_facts: no
tasks:
- name: Creating a fake shell result
shell:
cmd: echo 'Linux Vlersion XYZ {{ inventory_hostname }}'
register: shell_output
- lineinfile:
path: results.txt
line: "{{ {inventory_hostname: shell_output.stdout} | string }}"
create: yes
delegate_to: localhost
Which gives the recap:
PLAY [all] ******************************************************************************************************************************************
TASK [Creating a fake shell result] *****************************************************************************************************************
changed: [host2]
changed: [host1]
TASK [lineinfile] ***********************************************************************************************************************************
changed: [host1 -> localhost]
changed: [host2 -> localhost]
PLAY RECAP ******************************************************************************************************************************************
host1 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And fill the results.txt file with:
{'host1': 'Linux Vlersion XYZ host1'}
{'host2': 'Linux Vlersion XYZ host2'}
I need to store in variable & print the output of "ls -ltr" for a set of files on remote host. This variable with be used by another play in the same yml.
I tried the following however, it just prints the filename and not the complete results of "ls -ltr" command.
My lstest.yml looks like below:
- name: Play 4- Configure nodes
hosts: remotehost
tasks:
- name: "Collecting APP file information"
command: ls -ltr /app/{{ vars[item.split('.')[1]] }}/{{ item | basename }}
register: fdetails_APP
when: Layer == 'APP'
with_fileglob:
- "{{ playbook_dir }}/tmpfiles/*"
- debug: var=fdetails_APP.stdout_lines
- set_fact: fdet_APP={{ fdetails_APP.stdout_lines }}
- name: Printing fpath
debug:
var: fdet_APP
Output:
TASK [Collecting APP file information]
************************************************************************************ changed: [localhost] =>
(item=/app/Ansible/playbook/tmpfiles/filename1.exe) changed:
[localhost] => (item=/app/Ansible/playbook/tmpfiles/33211.sql)
changed: [localhost] =>
(item=/app/Ansible/playbook/tmpfiles/file1.mrt) changed: [localhost]
=> (item=/app/Ansible/playbook/tmpfiles/filename1.src)
PLAY RECAP
************************************************************************************************************************************************** localhost : ok=3 changed=1 unreachable=0
failed=0 skipped=0 rescued=0 ignored=0
Can you please suggest.
Note. In near future I would also like to add the checksum of the files stored in the same variable.
As per fileglob documentation- "Matching is against local system files on the Ansible controller. To iterate a list of files on a remote node, use the find module." For remote host fileglob cannot be used.