Checking File Last Change Date with Ansible


Share how you can use Ansible to check the last modified date of a file.
I wrote each of ad-hoc and playbook methods.

Test environment

  • 1 master (Ubuntu 18.04), 2 child (CentOS7, Debian8)
  • Ansible2.9.2
/etc/ansible/hosts
[child]
182.252.133.71
182.252.133.72

[child:vars]
ansible_user=root
ansible_password=Password123!

Scenario 1

Prints the last modified date of the server /etc/passwd file on child01, child02.

ad-hoc

Command

$ ansible child -m shell -a 'stat -c "%y" /etc/passwd |cut -f1 -d" "'

Result

root@master:/workspace/ansible# ansible child -m shell -a 'stat -c "%y" /etc/passwd |cut -f1 -d" "'
[WARNING]: Platform linux on host 182.252.133.72 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python
interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.

182.252.133.72 | CHANGED | rc=0 >>
2017-08-04

182.252.133.71 | CHANGED | rc=0 >>
2020-02-03

playbook

mtime_file.yml and my_filter.py are required.

├── filter_plugins
│   ├── my_filter.py
│   └── my_filter.pyc
└── mtime_file.yml
mtime_file.yml
---
- hosts : child
  tasks :
  - name: Check the passwd file change date
    stat: path=/etc/passwd
    register : status_file

  - debug :
      var : status_file.stat.mtime | my_filter
    when : status_file.stat.exists

./filter_plugins/my_filter.py
import datetime

def my_filter(arg):
    value = datetime.datetime.utcfromtimestamp(arg).strftime('%Y-%m-%d')
    return value

class FilterModule(object):
    def filters(self):
        return { 'my_filter': my_filter }

Command

$ ansible-playbook ./mtime_file.yml

Result

root@master:/workspace/ansible# ansible-playbook ./mtime_file.yml

PLAY [child] *****************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************
ok: [182.252.133.71]
ok: [182.252.133.72]

TASK [Check the passwd file change date] ****************************************************************************************************************************************
ok: [182.252.133.72]
ok: [182.252.133.71]

TASK [debug] *****************************************************************************************************************************************************
ok: [182.252.133.72] => {
    "status_file.stat.mtime | my_filter": "2017-08-04"
}
ok: [182.252.133.71] => {
    "status_file.stat.mtime | my_filter": "2020-02-03"
}

PLAY RECAP *******************************************************************************************************************************************************
182.252.133.71             : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
182.252.133.72             : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Scenario 2

Prints the last modified date of the server /etc/passwd.bak file on child01, child02.
/etc/passwd.bak is a file that exists only in child01.

ad-hoc

Command

$ ansible child -m shell -a 'stat -c "%y" /etc/passwd.bak |cut -f1 -d" "'

Result

root@master:/workspace/ansible# ansible child -m shell -a 'stat -c "%y" /etc/passwd.bak |cut -f1 -d" "'
[WARNING]: Platform linux on host 182.252.133.72 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python
interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.

182.252.133.72 | CHANGED | rc=0 >>
stat: cannot stat ‘/etc/passwd.bak’: No such file or directory

182.252.133.71 | CHANGED | rc=0 >>
2019-02-12

playbook

mtime_file.yml and my_filter.py are required.

├── filter_plugins
│   ├── my_filter.py
│   └── my_filter.pyc
└── mtime_file.yml
mtime_file.yml
---
- hosts : child
  tasks :
  - name: Check the passwd.bak file change date
    stat: path=/etc/passwd
    register : status_file

  - debug :
      var : status_file.stat.mtime | my_filter
    when : status_file.stat.exists

./filter_plugins/my_filter.py
import datetime

def my_filter(arg):
    value = datetime.datetime.utcfromtimestamp(arg).strftime('%Y-%m-%d')
    return value

class FilterModule(object):
    def filters(self):
        return { 'my_filter': my_filter }

Command

$ ansible-playbook ./mtime_file.yml

Result

root@master:/workspace/ansible# ansible-playbook ./mtime_file.yml

PLAY [child] *****************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************
ok: [182.252.133.72]
ok: [182.252.133.71]

TASK [Check the passwd.bak file change date] ****************************************************************************************************************************************
ok: [182.252.133.72]
ok: [182.252.133.71]

TASK [debug] *****************************************************************************************************************************************************
ok: [182.252.133.71] => {
    "status_file.stat.mtime | my_filter": "2019-02-12"
}
skipping: [182.252.133.72]

PLAY RECAP *******************************************************************************************************************************************************
182.252.133.71             : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
182.252.133.72             : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Lesson

I used ansible mostly for collecting server information.
The parsing of the resulting screen was necessary for the collection, whether ad-hoc or playbook

In the case of ad-hoc, normal operation was checked with rc=0 and rc=1 in the result.
As in scenario 2, it may not be normal operation even if rc=0 is included.

In the case of playbook, I can check the normal operation with ok: and skipping: on result.

As far as possible, ansible scripts should be written in playbook.