Turn expiring TLS certificates into Jira tickets each Monday

By General Input

Every Monday we check the certificates on your internet-facing hosts, open a ticket for each one expiring within 30 days, and post a single Slack summary.

Integrations

  • Shodan
  • Jira
  • Slack Bot

Type

Deterministic Code

Categories

  • Engineering
  • Operations

Every Monday at 6:00am, catch the TLS certificates on our internet-facing hosts before they expire and turn each one into a Jira ticket. Build this as a deterministic code workflow on a cron trigger: a fixed search, a fixed date threshold, a fixed duplicate lookup, and fixed ticket creation. Nothing here needs reasoning or judgement at run time.

Start with Shodan Search Hosts using an SSL-scoped query for our organisation, for example `ssl org:"ACME Corp"` or `ssl hostname:example.com`. Expose the organisation or domain as a configurable input rather than hardcoding it, so the same workflow works for a different estate. Shodan uses filter syntax such as `apache country:DE port:443`, and the List Search Filters operation can confirm any filter before relying on it. Request the certificate fields you need on each match, specifically the hostnames, IP address, port, certificate subject common name, and certificate expiry. Search returns 100 results per page, so advance with the `page` parameter until the results run out, and keep to roughly one request per second because Shodan rate limits at 1 request per second and returns HTTP 429 above that.

For every match, read the certificate expiry date and compute whole days remaining against today in UTC. Certificate expiry comes back as a compact certificate timestamp rather than a plain ISO date, so normalise it into a real date before comparing, and skip any match where the expiry cannot be parsed instead of treating it as expiring. Then keep only the hosts whose certificate has already expired or expires within the next 30 days, and discard the rest. Identify each remaining item by hostname plus IP plus port, so the same certificate served on two different ports stays two separate items. Sort the survivors soonest first.

Before creating anything, dedupe against Jira. For each remaining host, run a Jira Search Issues (JQL) lookup for an existing open ticket that already names that host, restricted to the target project and to unresolved work, for example `project = SEC AND statusCategory != Done AND summary ~ "legacy.example.com"`. The `~` operator is a fuzzy text match, so confirm in code that the returned summary genuinely contains the exact hostname before treating it as a match. If an open ticket exists, skip the host and record the existing issue key for the summary. If none exists, the host is genuinely new and gets a ticket.

Raise the new ones with Jira Create Issues (Bulk), which accepts up to 50 issues per request, so chunk the list into batches of 50 if it is ever longer. Each ticket needs the hostname, the IP address, the port, the certificate subject, and the exact expiry date, both in the summary and in the description, so the ticket is actionable without opening the workflow. Title them like "TLS certificate expires 2026-08-19: shop.example.com (203.0.113.19:443)". Set priority from days remaining: already expired or 7 days or fewer becomes the most urgent priority available, 8 to 14 days the next band down, and 15 to 30 days the middle band. Map these onto whatever priority scheme the project actually uses rather than assuming the Jira defaults exist. Add a consistent label such as `tls-expiry` to every ticket so the dedupe lookup and any later reporting have a reliable handle. Mark already expired certificates explicitly in the summary text, because an expired certificate is an active outage rather than a future risk.

Finish by posting one Slack message with Send a Message that lists the certificates expiring this month, sorted soonest first. Each line shows the hostname, the IP and port, the expiry date, the days remaining, and either the key of the ticket just raised or the key of the existing ticket already tracking it, linked as `https://<your-site>.atlassian.net/browse/<ISSUE-KEY>` from the keys returned by the bulk create. Send one message for the whole run, never one message per host, and close with a count of new tickets raised versus items already tracked. If nothing has expired and nothing expires within 30 days, still post the message and say explicitly that all certificates are clear for the next 30 days, so a quiet week is never mistaken for a broken workflow.

Related prompts

Explore more prompts
A brand asset library your marketing team actually searchesTurn Mailjet email clicks into ranked HubSpot follow-upsClean out the Looker dashboards and Looks nobody opensLiveKit live operations console for room moderationWake up dormant Keap leads with a researched reasonLiveChat coverage board for planning next week's shiftsPhone routing control panel for LiveKit voice agentsLinkedIn Ads budget pacing dashboard for every client accountGive your team Looker numbers without buying more seatsPause marketing emails to escalated customers, then restore them