I'm trying to extract a file's last modified time in the format YYMMDD-HHMMSS (ex:210422-135018) using Ansible's stat module.
I'm getting the file's mtime in a playbook:
- name: stat
stat:
path: /path/to/file
register: file
- debug:
msg: "{{ file.stat.mtime }}"
Which returns the mtime in EPOCH format (ex: 1632916899.9357276)
I've tried following instructions in the Ansible docs, but could not get it to work.
Is there a simple way to do this via Ansible, or is it best to do it via the shell module?
Thanks!
UPDATE: Solved with help from comment below:
- name: stat
stat:
path: /path/to/file
register: file
- debug:
msg: "{{ '%Y%m%d-%H%M%S' | strftime(file.stat.mtime) }}"
Related
Using the Ansible win_find module, I want to output just the path to the found files and I am trying this:
- name: Find files in directory while searching recursively
win_find:
paths: C:\Downloads
recurse: yes
register: dwnlds_list
- name: Display win_find file list result
debug: var=item.path
with_items: "{{ dwnlds_list.files }}"
However, the output I get is every return value for every found file.
What I would like is just:
"C:\\Downloads\\file0"
"C:\\Downloads\\file1"
"C:\\Downloads\\file2"
This is the solution that worked for me:
- name: Find files
win_find:
paths: C:\Downloads
recurse: yes
register: dwnlds_list
- name: Output file list
debug: msg="{{ dwnlds_list | json_query('files[].path') }}"
Similar to the Get-Content command in PowerShell, I am looking for a way to read a file on a Windows target and save the file contents to a variable in Ansible. The documentation says that the ansible.builtin.file module is able to get file contents but this feature does not seem to be available for the win_file module.
The following pseudo-code should better explain what I'm trying to do:
- name: save file contents to variable
win_get_content:
path: C:\somefile.txt
register: file_contents
if "{{lookup('file', 'C:\somefile.txt') }}" is not functional in window, you could try:
- name: get content of file
win_shell: 'type C:\somefile.txt'
register: file_contents
- name: display content
debug:
msg: "{{ file_contents.stdout_lines }} "
You can do so using the module slurp.
It works on both windows and linux.
- name: Read file
ansible.builtin.slurp:
src: '<path/to/file>'
register: file
- name: Print file content
ansible.builtin.debug:
msg: "{{ file['content'] | b64decode }}"
How to register a variable when using loop with stat module?
I am working on a project where I wish to run comparisons against the known value of a collection of files (checksum), which I will then take action if a change is detected (EG: notify someone, have not written this part yet).
If this were purely a CLI matter, I would have this sorted with some easy SH scripting.
That said, I have Ansible (2.7.5) available within my ENV and am keen to use it!
In reading the vendor documents, using the stat module felt the "Ansible way" to go on this one.
Currently just *NIX servers (Linux, Solaris, and possibly AIX) are in scope, but eventually this might also apply to Windows, where I expect I would use win_stat instead with suitable parameters.
At present I plan to dump the results of the scan to a file (EG: CSV), which I would then iterate / match against, for the purposes of a comparison (to detect if a file has been somehow changed).
This is another part I have not written yet (the read a file and compare portions), but expect to hit those once I get this present matter sorted.
My current challenge, is that I can get "one-off" stat checks to work fine.
However, I expect to be targeting a whole directory worth of files, and thus want to presumably:
"discover" the contents of the target directory, and retain this in memory
iterate (loop) through the list in memory
performing a stat check upon each file
retaining the checksum of each file
building some sort of dict or list?
write the collective results (or one line at a time) out to a log file of sorts (CSV.log: file_path,file_checksum)
I would welcome your feedback on what I might be missing (aside from some hair at this point)?
I have tried a few different approaches to looping within the playbook (loop, with_items, etc.), however the challenge remains the same.
The stat loop runs fine, but the trailing register statement fails to commit the output to memory (resulting in a variety of "undefined variable" errors).
Am I somehow missing something in my loop definition?
Looking at the vendor docs on "Using register with a loop", it would appear I am doing this correctly (in my view anyway).
Simple "target files" I am checking against within a directory.
/tmp/app/targets/file1.txt
Some text.
/tmp/app/targets/file2.cfg
cluster=0
cluster_id=app_pool_00
/tmp/app/targets/file3.sh
#!/bin/sh
printf "Hello world\n"
exit 0
My prototyping playbook as it exists currently.
---
- name: check file integrity
hosts: localhost
become: no
vars:
TARGET: /tmp/app/targets
LOG: /tmp/app/archive/scan_results.log
tasks:
- name: discover target files
find:
paths: "{{ TARGET }}"
recurse: yes
file_type: file
register: TARGET_FILES
- name: scan target
stat:
path: "{{ item.path }}"
get_checksum: yes
loop: "{{ TARGET_FILES.files }}"
register: TARGET_RESULTS
- name: DEBUG
debug:
var: "{{ TARGET_RESULTS }}"
- name: write findings to log
copy:
content: "{{ TARGET_RESULTS.stat.path }},{{ TARGET_RESULTS.stat.checksum }}"
dest: "{{ LOG }}"
...
My "one-off" playbook that worked.
---
- name: check file integrity
hosts: localhost
become: no
vars:
TARGET: /tmp/app/targets/file1.txt
LOG: /tmp/app/archive/scan_results.log
tasks:
- name: scan target
stat:
path: '{{ TARGET }}'
checksum_algorithm: sha1
follow: no
get_attributes: yes
get_checksum: yes
get_md5: no
get_mime: yes
register: result
- name: write findings to log
copy:
content: "{{ result.stat.path }},{{ result.stat.checksum }}"
dest: "{{ LOG }}"
...
The output was not exciting, but useful.
Would expect to build this up with multi-line output (one line per file stat checked) if I could figure out how to loop / register loop output correctly.
/tmp/app/archive/scan_results.log
/tmp/app/targets/file1.txt,8d06cea05d408d70c59b1dbc5df3bda374d869a4
You can use the set_fact module to register a variable like you want.
I don't use it in my test for you, it maybe useless in your case :
---
- name: check file integrity
hosts: localhost
vars:
TARGET: /tmp/app/targets
LOG: /tmp/app/archive/scan_results.log
tasks:
- name: 'discover target files'
find:
paths: "{{ TARGET }}"
recurse: yes
file_type: file
register: TARGET_FILES
- debug:
var: TARGET_FILES
- name: 'scan target'
stat:
path: "{{ item.path }}"
get_checksum: yes
loop: "{{ TARGET_FILES.files }}"
register: TARGET_RESULTS
- debug:
var: TARGET_RESULTS
- name: 'write findings to log'
lineinfile:
line: "{{ item.stat.path }},{{ item.stat.checksum }}"
path: "{{ LOG }}"
create: yes
loop: '{{ TARGET_RESULTS.results }}'
result:
# cat /tmp/app/archive/scan_results.log
/tmp/app/targets/file3.sh,bb4b0ffe4b5d26551785b250c38592b6f482cab4
/tmp/app/targets/file1.txt,8d06cea05d408d70c59b1dbc5df3bda374d869a4
/tmp/app/targets/file2.cfg,fb23292e06f91a0e0345f819fdee34fac8a53e59
Best Regards
I am struggling to find out how to compare two files. Tried several methods including this one which errors out with:
FAILED! => {"msg": "The module diff was not found in configured module paths. Additionally, core modules are missing. If this is a
checkout, run 'git pull --rebase' to correct this problem."}
Is this the best practice to compare two files and ensure the contents are the same or is there a better way?
Thanks in advance.
My playbook:
- name: Find out if cluster management protocol is in use
ios_command:
commands:
- show running-config | include ^line vty|transport input
register: showcmpstatus
- local_action: copy content="{{ showcmpstatus.stdout_lines[0] }}" dest=/poc/files/{{ inventory_hostname }}.result
- local_action: diff /poc/files/{{ inventory_hostname }}.result /poc/files/transport.results
failed_when: "diff.rc > 1"
register: diff
- name: debug output
debug: msg="{{ diff.stdout }}"
Why not using stat to compare the two files?
Just a simple example:
- name: Get cksum of my First file
stat:
path : "/poc/files/{{ inventory_hostname }}.result"
register: myfirstfile
- name: Current SHA1
set_fact:
mf1sha1: "{{ myfirstfile.stat.checksum }}"
- name: Get cksum of my Second File (If needed you can jump this)
stat:
path : "/poc/files/transport.results"
register: mysecondfile
- name: Current SHA1
set_fact:
mf2sha1: "{{ mysecondfile.stat.checksum }}"
- name: Compilation Changed
debug:
msg: "File Compare"
failed_when: mf2sha1 != mf1sha1
your "diff" task is missing the shell keyword, Ansible thinks you want to use the diff module instead.
also i think diff (as name of the variable to register the tasks result) leads ansible to confusion, change to diff_result or something.
code (example):
tasks:
- local_action: shell diff /etc/hosts /etc/fstab
failed_when: "diff_output.rc > 1"
register: diff_output
- debug:
var: diff_output
hope it helps
From Ansible User Guide: https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html
- name: Fail task when both files are identical
ansible.builtin.raw: diff foo/file1 bar/file2
register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2
A slightly shortened version of 'imjoseangel' answer which avoids setting facts:
vars:
file_1: cats.txt
file_2: dogs.txt
tasks:
- name: register the first file
stat:
path: "{{ file_1 }}"
checksum: sha1
get_checksum: yes
register: file_1_checksum
- name: register the second file
stat:
path: "{{ file_2 }}"
checksum: sha1
get_checksum: yes
register: file_2_checksum
- name: Check if the files are the same
debug: msg="The {{ file_1 }} and {{ file_2 }} are identical"
failed_when: file_1_checksum.stat.checksum != file_2_checksum.stat.checksum
ignore_errors: true
I am trying to list the files in a directory and copy to some other directory.
This is my playbook:
---
- hosts: testserver
become: true
tasks:
- name: list files
command: " ls /root/"
register: r
- debug: var=r
- debug: msg="item.item={{item.item}}, item.stdout={{item.stdout}}, item.changed={{item.changed}}"
with_items: "{{r.results}}"
This is the error I am getting:
FAILED! => {"msg": "'dict object' has no attribute 'results'"}
I am trying to list the files in a directory and copy to some other directory.
Don't parse ls output! Neither in Ansible, nor anywhere else.
Don't use command module for what Ansible offers a native one.
Ansible has a find module which returns a list of files. In your case:
- name: list files
find:
paths: /root
register: my_find
- debug:
var: item.path
with_items: "{{ my_find.files }}"
You have a debug task there that shows you the contents of the variable r. Do you see a key named results?
You only see the results key (and need to use item.item) when are have registered values in a loop, as described here. You're not doing that, so the structure of r will be much simpler.
If you're trying to iterate over the lines of the ls output, you probably want:
- debug:
msg: "filename={{item}}"
with_items: "{{r.stdout_lines}}"