Need help with VMware-related playbook

Good day all. It’s been a while.

I’ve created a playbook to find and delete VMware virtual machine snapshots which are 3 days old or older. It works to a point. What I’m not getting is the date format for identifying the snapshots to be deleted. Here’s my code:

---

- hosts: localhost
  gather_facts: false

  vars:
    vault_url: "https://vault.domain.com:443"
    vault_token: "{{ lookup('file', '/opt/vault-agent/agent-token') | trim }}"
    vault_secret_path: "deploy2/data/deploy-vc"
    vcenter_host: "vcenter1bed.domaincorp.domain.com"
    datacenter: "Bedford Datacenter"
    validate_certs: false

  tasks:

    - name: Delete old reports
      file:
        path: '/home/deploy/vm_snapshots.csv'
        state: absent

    - name: Read vCenter credentials from Vault
      community.hashi_vault.vault_read:
        url: "{{ vault_url }}"
        path: "{{ vault_secret_path }}"
        auth_method: token
        token: "{{ vault_token }}"
      register: secret
      tags:
        - creds

    - name: Set vCenter credentials from Vault
      set_fact:
        vc_user: "{{ secret.data.data.data.user }}"
        vc_pass: "{{ secret.data.data.data.pass }}"
      tags:
        - creds

    - name: Get list of VMs
      community.vmware.vmware_vm_info:
        hostname: "{{ vcenter_host }}"
        username: "{{ vc_user }}"
        password: "{{ vc_pass }}"
        validate_certs: "{{ validate_certs }}"
      register: vm_list
      tags:
        - listvms

    - name: Get snapshots per VM
      community.vmware.vmware_guest_snapshot_info:
        hostname: "{{ vcenter_host }}"
        username: "{{ vc_user }}"
        password: "{{ vc_pass }}"
        datacenter: "{{ datacenter }}"
        validate_certs: "{{ validate_certs }}"
        name: "{{ item.guest_name }}"
        folder: "/{{ datacenter }}/vm"
      loop: "{{ vm_list.virtual_machines }}"
      loop_control:
        label: "{{ item.guest_name }}"
      register: snapshot_data
      failed_when: false
      tags:
        - snaplist

    - name: Initialize list of VMs with snapshots
      set_fact:
        vms_with_snapshots: []

    - name: Add each VM with snapshots to the list
      set_fact:
        vms_with_snapshots: "{{ vms_with_snapshots + [ { 'vm': item.item.guest_name, 'snapshots': item.guest_snapshots.snapshots } ] }}"
      when:
        - item.guest_snapshots is mapping
        - item.guest_snapshots.snapshots is defined
        - item.guest_snapshots.snapshots | length > 0
      loop: "{{ snapshot_data.results }}"
      loop_control:
        label: "{{ item.item.guest_name }}"
      tags:
        - snaplist

    - name: Initialize flat snapshot list
      set_fact:
        all_snapshots: []

    - name: Flatten snapshots into a single list
      set_fact:
        all_snapshots: "{{ all_snapshots + [ item.1 | combine({ 'vm': item.0.vm }) ] }}"
      with_subelements:
        - "{{ vms_with_snapshots }}"
        - snapshots

    - name: Sort all snapshots by creation_time
      set_fact:
        sorted_snapshots: "{{ all_snapshots | sort(attribute='creation_time') }}"

    - name: Lookup timestamp string for threshold date (3 days ago)
      set_fact:
        threshold_date_raw: "{{ lookup('pipe', 'date -u -d \"3 days ago\" +%Y-%m-%dT%H:%M:%SZ') }}"

    - name: Parse threshold date into datetime object
      set_fact:
        threshold_date: "{{ threshold_date_raw | to_datetime('%Y-%m-%dT%H:%M:%SZ') }}"

    - name: Delete snapshots older than 3 days
      community.vmware.vmware_guest_snapshot:
        hostname: "{{ vcenter_host }}"
        username: "{{ vc_user }}"
        password: "{{ vc_pass }}"
        validate_certs: "{{ validate_certs }}"
        folder: "/{{ datacenter }}/vm"
        datacenter: "{{ datacenter }}"
        name: "{{ item.vm }}"
        snapshot_name: "{{ item.name }}"
        state: present  # Use 'present' for testing
      when: (item.creation_time | to_datetime('%Y-%m-%dT%H:%M:%S.%f%z')) < (threshold_date | to_datetime('%Y-%m-%dT%H:%M:%SZ'))
      loop: "{{ all_snapshots }}"
      loop_control:
        label: "{{ item.vm }} - {{ item.name }}"

This is the error that's generated when the playbook is run:

TASK [Delete snapshots older than 3 days] *********************************************************************************************
Tuesday 06 May 2025  11:44:03 -0400 (0:00:00.058)       0:03:09.431 ***********
Tuesday 06 May 2025  11:44:03 -0400 (0:00:00.058)       0:03:09.430 ***********
fatal: [localhost]: FAILED! =>
  msg: |-
    The conditional check '(item.creation_time | to_datetime('%Y-%m-%dT%H:%M:%S.%f%z')) < (threshold_date | to_datetime('%Y-%m-%dT%H:%M:%SZ'))' failed. The error was: time data '2025-05-03 15:44:03' does not match format '%Y-%m-%dT%H:%M:%SZ'

    The error appears to be in '/etc/ansible/playbooks/vm/dy_vmsnaprm.yml': line 105, column 7, but may
    be elsewhere in the file depending on the exact syntax problem.

    The offending line appears to be:

        - name: Delete snapshots older than 3 days
          ^ here

This is what output simply listing the extant snapshots looks like:

ok: [localhost] => (item=[{'vm': 'api-dev-01'}, {'id': 2, 'name': 'VM Snapshot 5/2/2025, 9:28:04 AM', 'description': '', 'creation_time': '2025-05-02T13:28:07.321210+00:00', 'state': 'poweredOn', 'quiesced': False}])

Note the format of "creation_time".

Your help, as always, is appreciated.

- playbook
- vmware

Your threshold_date variable is already converted to datetime object so it’s not required to convert it second time. In other words, this line:

when: (item.creation_time | to_datetime('%Y-%m-%dT%H:%M:%S.%f%z')) < (threshold_date | to_datetime('%Y-%m-%dT%H:%M:%SZ'))

should read:

when: (item.creation_time | to_datetime('%Y-%m-%dT%H:%M:%S.%f%z')) < threshold_date

or:

when: (item.creation_time | to_datetime('%Y-%m-%dT%H:%M:%S.%f%z')) < (threshold_date_raw | to_datetime('%Y-%m-%dT%H:%M:%SZ'))

… note the usage of threshold_date_raw instead of threshold_date in the last one.

Bojan, Thanks for your reply. I made the change in the playbook, but it still errors:

TASK [Delete snapshots older than 3 days] *********************************************************************************************
Tuesday 06 May 2025 14:59:53 -0400 (0:00:00.062) 0:03:17.403 ***********
Tuesday 06 May 2025 14:59:53 -0400 (0:00:00.062) 0:03:17.402 ***********
fatal: [localhost]: FAILED! =>
msg: |-
The conditional check ‘(item.creation_time | to_datetime(’%Y-%m-%dT%H:%M:%S.%f%z’)) < (threshold_date_raw | to_datetime(‘%Y-%m-%dT%H:%M:%SZ’))’ failed. The error was: Unexpected templating type error occurred on ({% if (item.creation_time | to_datetime(‘%Y-%m-%dT%H:%M:%S.%f%z’)) < (threshold_date_raw | to_datetime(‘%Y-%m-%dT%H:%M:%SZ’)) %} True {% else %} False {% endif %}): can’t compare offset-naive and offset-aware datetimes. can’t compare offset-naive and offset-aware datetimes

The error appears to be in '/etc/ansible/playbooks/vm/dy_vmsnaprm.yml': line 105, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

    - name: Delete snapshots older than 3 days
      ^ here

Ah, this needs a little bit of additional Python fiddling. I’m not sure if this will work but you can try:

when: (item.creation_time | to_datetime('%Y-%m-%dT%H:%M:%S.%f%z')).timestamp() < (threshold_date_raw | to_datetime('%Y-%m-%dT%H:%M:%SZ')).timestamp()

You’ll have to study how Python datetime objects behave:

https://stackoverflow.com/questions/60003764/typeerror-cant-compare-offset-naive-and-offset-aware-datetimes

Bojan, that worked. Thanks so much!

1 Like