How to filter GCP objects based on date of their creation in Ansible

George Shuklin
OpsOps
Published in
2 min readJun 18, 2022

If you ever wonder why I’m been paid, this is it.

Goal: to find objects (in my case VMs) in gcp account which have specific labels but live for too long.

The general algo is simple: query resources, filter based on creation date (specicially, for gcp_compute_instance_info it’s resources[x].creationTimestamp), check the age, kill old ones.

The actual maddening reality:

This is google timestamp: 2022-06-18T06:59:15.045-07:00

This is Ansible current date: 2022-06-18T14:34:04Z

If you ever wonder how to feed to_datetime google-flavored ISO8601, well,…according to specs, it’s impossible. Because there is no field descriptor for milliseconds. Nope.

Moreover, man strptime does not have anything with sub-second resolution and there is no substitution character for ‘anything’ or ‘any digit’.

(Sigh) So, the true reason I’m paid for is:

Python’s datetime has %f formatter, which is number of microseconds. But Google has milliseconds. But we don’t care. Boom!

- name: Report ages
debug:
msg: "age: {{ansible_date_time.iso8601|to_datetime(ansible_iso8601_fmt) - item.creationTimestamp|to_datetime(google_iso8601_fmt) }}"
loop: "{{ servers.resources }}"
loop_control:
label: "{{ item.name }}"
vars:
google_iso8601_fmt: "%Y-%m-%dT%H:%M:%S.%f%z"
ansible_iso8601_fmt: "%Y-%m-%dT%H:%M:%S%z

… uh, I can’t read it in Medium mess. Anyway, magic is:

ansible_date_time.iso8601|to_datetime(ansible_iso8601_fmt)

and we got Ansible version of datetime (do not forget to gather facts)

item.creationTimestamp|to_datetime(google_iso8601_fmt)

and we got the Google flavor for ISO8601 datetime. Which has totally incorrect microseconds, but, again, we don’t care about milimicroseconds.

Difference between is timedelta, which has trap of its own, but if we are ̶w̶e̶l̶l̶-̶p̶a̶i̶d̶.. experienced, and we know, that it’s not .seconds, but .total_seconds().

So, difference between them is the age (or age in the seconds):

(a-b).total_seconds()

So, full code is:

- name: Get 
gcp_compute_instance_info:
filters:
- labels.env=YOUR-FILTER
....
register: servers
- name: delete instance
gcp_compute_instance:
state: absent
name: "{{ item.name }}"
...
when: (ansible_date_time.iso8601|to_datetime(ansible_iso8601_fmt) - item.creationTimestamp|to_datetime(google_iso8601_fmt)).total_seconds() > inactivity
loop: "{{ servers.resources }}"
loop_control:
label: "{{ item.name }}"
vars:
google_iso8601_fmt: "%Y-%m-%dT%H:%M:%S.%f%z"
ansible_iso8601_fmt: "%Y-%m-%dT%H:%M:%S%z"

P.S.

Why milliseconds? Why? … May be for job security?

--

--

OpsOps
OpsOps

Published in OpsOps

Tech blog on Linux, Ansible, Ceph, Openstack and operator-related programming

George Shuklin
George Shuklin

Written by George Shuklin

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.