Turn expiring TLS certificates into Jira tickets each Monday
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.
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.
Example output
What does this prompt do?
- Every Monday morning, looks up the certificates running on your public, internet-facing hosts and works out which ones have already expired or run out within the next 30 days.
- Finds certificates nobody has written down, including forgotten subdomains, old load balancers, and test boxes that never made it onto an internal renewal list.
- Checks your Jira project before filing anything, so a host that already has an open renewal ticket never collects a second one week after week.
- Opens a ticket per genuinely new certificate with the hostname, address, port, certificate name, and exact expiry date, with urgency set by how many days are left.
- Posts one Slack message listing every certificate expiring this month, with direct links to the tickets that were raised.
What do I need to use this?
- A Shodan account with search access, used to see the certificates on your internet-facing hosts. Searching uses query credits, so a paid membership is recommended for weekly runs.
- The company name or domain your public hosts sit under, so the search only ever returns your own estate and not the wider internet.
- A Jira project where certificate renewal tickets should be created, and permission to create issues in it.
- A Slack workspace and the channel where the weekly summary should land.
How can I customize it?
- Change the timing. Monday at 6am is a good weekly rhythm, but daily works if your estate moves quickly, and the first of the month suits slower ones.
- Move the thresholds. The 30 day window and the 7 day urgent cutoff are both just numbers. Teams commonly run 30, 14, and 7 day steps, or widen the window to 100 days to give renewals plenty of runway.
- Point it at the right places. Choose which Jira project receives tickets, which Slack channel gets the summary, and how days remaining map onto your own priority names.
FAQs
How does it know which hosts belong to us?
Will it open the same ticket again every week?
Can it find certificates we did not know about?
What happens to a certificate that has already expired?
Will it post to Slack even when nothing is expiring?
Related templates
When someone leaves, we check which shared passwords they used in their final months and post a ranked rotation list to your security channel.
Every Monday, check every S3 bucket for public exposure, missing encryption and weak backup settings, then get the risks ranked in Slack.
Twice every weekday, the conversations from your social inbox land on the right HubSpot contact timelines, with a Slack recap for sales.
Every morning, find the addresses that hard bounced or filed a spam complaint, update the matching HubSpot contacts, and post a short Slack recap.
Every weekday at 4pm, spot the threads that went quiet, stage a ready-to-send nudge in your mailbox, and get a ranked Slack recap.
When you merge a fix in GitHub, this agent checks the matching dead-letter queue, replays the failed messages, and reports back on the pull request and in Slack.
Never explain another expired certificate outage.
Set this up once and every certificate on your public estate comes to you with a month to spare, already in a ticket and already assigned.