Answer one question from a playbook, down to the part number: is this hardware still supported by its manufacturer, and when does that stop?
Every date this collection returns comes from the manufacturer's own end-of-life bulletin, and the URL of that bulletin is returned alongside it. Nothing is estimated, modelled or inferred. Data comes from EOSL.ai, which tracks 1,100+ hardware families and 6,000+ part numbers across 10 manufacturers, refreshed weekly.
- No API key, no account, no rate limit.
- No Python dependencies beyond ansible-core itself.
- One HTTP request per run, however many part numbers you check.
- Data is CC BY 4.0; the collection is GPL-3.0-or-later, the standard licence for Ansible modules.
ansible-galaxy collection install eosl.lifecycle- hosts: localhost
tasks:
- name: Check some hardware
eosl.lifecycle.part_info:
part_numbers:
- WS-C3850-24T-L
- N9K-C93180YC-EX
register: eosl
- name: Show what is past vendor support
ansible.builtin.debug:
msg: "{{ eosl.summary.unsupported_part_numbers }}"ok: [localhost] => {
"msg": ["WS-C3850-24T-L"]
}
One request covers the whole fleet, so this scales to thousands of devices.
- name: Gather model numbers from network devices
hosts: switches
gather_facts: false
tasks:
- cisco.ios.ios_facts:
gather_subset: hardware
- name: Check every gathered model against vendor lifecycle data
hosts: localhost
gather_facts: false
tasks:
- eosl.lifecycle.part_info:
part_numbers: >-
{{ groups['switches'] | map('extract', hostvars, 'ansible_net_model')
| select('string') | unique | list }}
register: audit
- ansible.builtin.debug:
msg: >-
{{ audit.summary.counts.unsupported }} of {{ audit.summary.counts.total }} models
are past end of service life| Option | Type | Default | Description |
|---|---|---|---|
part_numbers |
list | required | Part numbers to look up |
vendor |
str | Enables vendor-specific SKU alias expansion (see below) | |
api_url |
str | https://eosl.ai/data/lookup.json |
Override to use a mirror or pinned copy |
timeout |
int | 30 |
HTTP timeout in seconds |
validate_certs |
bool | true |
TLS certificate validation |
Returns parts (part number → record, or null), unmatched (list), and summary
(counts plus unsupported_part_numbers). Read-only, so it supports --check.
The map is called
parts, notresults— Ansible reservesresultsfor loop output and will silently rename any module return that uses it.
Each record:
part_number: WS-C3850-24T-L
family: Catalyst 3850 Series
vendor: Cisco
slug: cisco-catalyst-3850
end_of_sale: "2020-10"
end_of_service_life: "2025-10"
status: unsupported # active | supported | unsupported | unknown
active: false # true = manufacturer has announced no end of life
support_runway_score: 23 # higher = more remaining support runway
support_runway_band: Little or no runway
source_url: https://www.cisco.com/...eos-eol-notice-c51-743072.html
cisa_bod_26_02_scope: true # networked edge device category, derived from public vendor data
url: https://eosl.ai/system/cisco-catalyst-3850/- ansible.builtin.debug:
msg: "{{ lookup('eosl.lifecycle.eosl', 'WS-C3850-24T-L').end_of_service_life }}"
- ansible.builtin.assert:
that: lookup('eosl.lifecycle.eosl', model, vendor='Cisco').status != 'unsupported'
fail_msg: "{{ model }} is past vendor end of service life"Options: vendor, api_url, timeout, validate_certs, and on_missing
(null — default, skip, or error).
Deterministic and conservative, in this order:
- Exact match on the part number.
- Loose match ignoring case and punctuation —
FortiGate-60E,fortigate 60eandFORTIGATE60Eare the same part. - Vendor-gated alias expansion — Fortinet publishes
FortiGate-60Ewhere inventories often carryFG-60E. This step only runs when you pass a matchingvendor, so it cannot map one manufacturer's part number onto another's record.
There is no fuzzy or substring matching. WS-C3850 does not match WS-C3850-24T-L. A part
number the database does not carry is returned as null and listed in unmatched — it is never
resolved to a near neighbour, because a wrong lifecycle date is worse than no lifecycle date.
This is the same logic as the NetBox plugin, deliberately, so both integrations resolve a given part number identically.
status: unknowndoes not mean supported. It means this database holds no end-of-service-life date for that part.unmatchedis not "this hardware is fine". It means the database does not track that part.- Dates describe what the manufacturer published, not what it will do. Vendors sometimes extend
support, and paid extensions exist that this data does not see. Check
source_urlbefore acting on anything load-bearing. - One part number resolves to one record. Where the same part appears in more than one product
family with different dates, the lookup document serves a single answer. For those cases consult
the family page at
url, which shows the conflict. cisa_bod_26_02_scopeflags a category of networked edge device derived from public vendor data. CISA's own device list is not public, so this never claims presence on it.
ansible-core 2.15 or newer. No collections or Python packages beyond it.
Outbound HTTPS to eosl.ai is needed. For air-gapped use, mirror
https://eosl.ai/data/lookup.json and point api_url at your copy — the data is CC BY 4.0.
- Data source and API: https://eosl.ai/api/
- Open dataset (CC BY 4.0): https://eosl.ai/dataset/
- How the data is sourced: https://eosl.ai/about/
- NetBox plugin: https://github.com/Kranny36/netbox-eosl
- Source & issues: https://github.com/EOSL-AI/ansible-eosl
Not affiliated with, endorsed by, or sponsored by any hardware manufacturer named in the data, nor by Red Hat or the Ansible project.