Automated Email Reminders for Jira Assets Expiry with Power Scripts for Jira
Goal: Send automated email reminders to user accounts stored on Jira Assets (Insight) objects when a date attribute (e.g., License Expiry) is approaching, using SIL (Power Scripts). This vendor-agnostic guide shows two generic approaches:
Using Assets REST API from SIL (works without SIL Insight Connector).
Using SIL Insight Connector functions (if available on your DC instance).
What you’ll need
Jira Data Center with Assets (formerly Insight).
Power Scripts for Jira (SIL) installed; access to SIL Manager and SIL Scheduler.
An Assets Object Type (e.g., Host) with attributes:
Hostname (Text)
License Expiry Date (Date/Time)
Status (e.g., Running/Inactive)
Group Owner (User-type attribute pointing to a Jira user) and/or Primary Contact (User) and/or Notification Email (Text or Email)
Outgoing Mail configured in Jira and permitted to send to intended domains.
Approach A — REST API + SIL (generic, no connector)
This approach queries Assets using AQL via the REST API. It suits environments without the SIL Insight Connector.
AQL to target expiring assets
Example AQL to find Hosts with a License Expiry in the next 30 days and Status=Running:
objectType=Host and "License Expiry Date" > now(30d) and status=RunningSIL script (REST)
// Query Assets via REST and email upcoming expiries (30 days)
// Requires: Power Scripts DC. Run on demand in SIL Manager, then schedule with SIL Scheduler.
struct ObjectType{ int id; string name; int type; date created; date updated; }
struct ObjectAttributeValues{ string value; string searchValue; string displayValue; string referencedType; }
struct Attributes{ int id; int objectTypeAttributeId; ObjectAttributeValues[] objectAttributeValues; int objectId; }
struct ObjectEntries{ int id; string label; string objectKey; ObjectType[] objectType; date created; date updated; string name; Attributes[] attributes; }
struct ObjectTypeAttributes{ int id; string name; }
struct AQLresults{ ObjectEntries[] objectEntries; boolean iqlSearchResult; ObjectTypeAttributes[] objectTypeAttributes; }
function searchByAQL(string AQLquery, string JiraBaseURL){
AQLresults res;
string REST = JiraBaseURL + "/rest/assets/1.0/aql/objects";
string auth_user = "your-user@example.com"; // user with API token
string Jira_API_Token = "<api-token>"; // create at id.atlassian.com
HttpRequest request;
request.headers += httpCreateHeader("Content-Type", "application/json");
request.headers += httpBasicAuthHeader(auth_user, Jira_API_Token);
// URL-encode AQL or pre-encode it
REST += "?qlQuery=" + AQLquery; // if not encoded, use replace for space/quotes
res = httpGet(REST, request);
number statusCode = httpGetStatusCode();
if (!(statusCode >= 200 && statusCode < 300)) {
runnerLog("Assets REST error: " + statusCode + ":" + httpGetReasonPhrase());
}
return res;
}
string jiraInstanceURL = getJIRABaseUrl();
string AQL = "objectType=Host%20and%20%22License%20Expiry%20Date%22%20%3E%20now(30d)%20and%20status=Running";
AQLresults res = searchByAQL(AQL, jiraInstanceURL);
for (ObjectEntries ent in res.objectEntries){
string hostName; string key; string expiry; string groupOwner; string primaryContact; string notificationEmail; string assetStatus;
// Map attributes by name
for (Attributes att in ent.attributes){
for (ObjectTypeAttributes t in res.objectTypeAttributes){
if (t.id == att.objectTypeAttributeId){
if (t.name == "Hostname"){ hostName = att.objectAttributeValues[0].value; }
else if (t.name == "License Expiry Date" || t.name == "Expiry"){
// Convert 2025-12-31T09:45:00.000Z -> 2025-12-31 09:45:00
expiry = replace(substring(att.objectAttributeValues[0].value,0,length(att.objectAttributeValues[0].value) - 5), "T", " ");
}
else if (t.name == "Key"){ key = att.objectAttributeValues[0].value; }
else if (t.name == "Group Owner"){ groupOwner = att.objectAttributeValues[0].searchValue; }
else if (t.name == "Primary Contact"){ primaryContact = att.objectAttributeValues[0].searchValue; }
else if (t.name == "Notification Email"){ notificationEmail = att.objectAttributeValues[0].value; }
else if (t.name == "Status"){ assetStatus = att.objectAttributeValues[0].value; }
}
}
}
// Resolve Jira users (for user-type attributes)
JUser ownerUser = isNotEmpty(groupOwner) ? getUser(groupOwner) : null;
JUser primaryUser = isNotEmpty(primaryContact) ? getUser(primaryContact) : null;
// Build recipient list (TO), fallbacks to available fields
string[] toList;
if (ownerUser != null && isNotEmpty(ownerUser.email)) toList += ownerUser.email;
if (primaryUser != null && isNotEmpty(primaryUser.email)) toList += primaryUser.email;
if (isNotEmpty(notificationEmail)) toList += notificationEmail;
if (size(toList) > 0){
JEmailMessage email;
email.to = toList; // also supports array
email.subject = "Host license expiring in 30 days";
email.message = "Host: " + hostName + "\n"+
"License expiry: " + hostName + " will expire by " + expiry + "\n"+
"Status: " + assetStatus + "\n"+
"Asset Key: " + key;
// sendEmail(email); // uncomment after testing
}
runnerLog("Asset " + ent.objectKey + " => Host=" + hostName + ", Expiry=" + expiry + ", To=" + join(toList, ", "));
}Tip: Test in SIL Manager first with sendEmail commented. Validate the runnerLog output and recipients. When correct, uncomment sendEmail and move to a scheduled job.
Schedule it daily with SIL Scheduler
Navigate to Power Scripts → Scheduler and create a new Job.
Choose the script file above. Set a daily cron (e.g., early morning).
Run once manually to ensure mail queue is populated and no errors are logged.
Approach B — SIL Insight Connector functions (when available)
If your DC instance has the SIL Insight Connector, you can query with insight_findObjects and read attributes with insight_getAttribByName, then send emails. Example:
use "insight";
string iql = "objectType=Host AND Status = \"Running\""; // adjust to your object type/attributes
date deadline = currentDate() + "30d"; // today + 30 days
for (IObject obj in insight_findObjects(iql)){
string host = insight_getAttribByName(obj, "Hostname");
date expiry = insight_getAttribByName(obj, "License Expiry Date");
if (startOfDay(expiry) >= startOfDay(deadline)){
string groupOwnerUser = insight_getAttribByName(obj, "Group Owner"); // user key/name
string primaryContactUser = insight_getAttribByName(obj, "Primary Contact");
string notifEmail = insight_getAttribByName(obj, "Notification Email");
string[] toList;
JUser owner = isNotEmpty(groupOwnerUser) ? getUser(groupOwnerUser) : null;
JUser primary = isNotEmpty(primaryContactUser) ? getUser(primaryContactUser) : null;
if (owner != null && isNotEmpty(owner.email)) toList += owner.email;
if (primary != null && isNotEmpty(primary.email)) toList += primary.email;
if (isNotEmpty(notifEmail)) toList += notifEmail;
if (size(toList) > 0){
JEmailMessage email;
email.to = toList;
email.subject = "Host license expiring in 30 days";
email.message = "Host: " + host + "\n"+
"License expiry: " + host + " will expire by " + expiry + "\n"+
"Status: " + insight_getAttribByName(obj, "Status");
// sendEmail(email); // uncomment after testing
}
runnerLog(obj["key"] + " -> Host=" + host + ", Expiry=" + expiry + ", To=" + join(toList, ", "));
}
}Configuration checklist
Attributes: Confirm exact attribute names in Assets match those referenced in the script (case-sensitive).
User attributes: Ensure “Group Owner” / “Primary Contact” point to Jira users resolvable by getUser; otherwise, use email text attributes.
Mail server: Outgoing Mail must be configured and allowed to deliver to recipient domains; check logs if messages queue but don’t send.
Permissions: The API user (REST approach) must have Assets permissions to read queried objects.
Scheduling: Run daily; consider time-of-day that best suits recipients and rate limits.
Sample AQL patterns
Next 30 days and running:
objectType=Host and "License Expiry Date" > now(30d) and status=RunningExpired already:
objectType=Host and "License Expiry Date" < now()Window (7, 14, 30 days):
"License Expiry Date" > now(7d) and "License Expiry Date" <= now(14d)(adjust ranges)
Troubleshooting
No results: Print the final AQL string in runnerLog; verify attribute names and URL-encoding for REST.
Emails not sending: Check Jira mail queue and application logs for SMTP/network blocks; validate “to” list is not empty.
User resolution fails: Ensure user-type attributes store a Jira username/key resolvable by getUser; fallback to an email text attribute.