Expiration Reminder E-Mails with Adaptavist Script Runner
If you are using API Token Authentication for Jira and also Adaptavist ScriptRunner, you can add a script job that runs once a day and sends expiration warnings to various users.
Here is how you can do this.
Add Adaptavist Script Runner Job
provide a name for the job
pick a user with appropriate permissions to run the script
schedule the script to run once every day

add the following script to the inline script section
you can ignore the type check and other warnings like the below, the script will still work

- iimport static groovyx.net.http.ContentType.JSON
- import groovyx.net.http.RESTClient
- import com.atlassian.jira.user.util.UserManager
- import com.atlassian.jira.user.UserPropertyManager
- import com.atlassian.jira.security.groups.GroupManager
- import com.opensymphony.module.propertyset.PropertySet;
- import com.atlassian.jira.component.ComponentAccessor
- import com.atlassian.jira.mail.Email
- import com.atlassian.jira.mail.settings.MailSettings
- import com.atlassian.mail.MailException
- import com.atlassian.mail.server.SMTPMailServer
- import com.atlassian.plugin.util.ContextClassLoaderSwitchingUtil
- import org.apache.log4j.Level
- import org.apache.log4j.Logger
- import java.text.SimpleDateFormat
- import groovy.time.TimeCategory
- // send email function
- String sendEmail(String emailAddr, String subject, String body) {
- def logger = Logger.getLogger(getClass())
- logger.setLevel(Level.DEBUG)
- // Stop emails being sent if the outgoing mail server gets disabled (useful if you start a script sending emails and need to stop it)
- def mailSettings = ComponentAccessor.getComponent(MailSettings)
- if (mailSettings?.send()?.disabled) {
- return 'Your outgoing mail server has been disabled'
- }
- def mailServer = ComponentAccessor.mailServerManager.defaultSMTPMailServer
- if (!mailServer) {
- logger.debug('Your mail server Object is Null, make sure to set the SMTP Mail Server Settings Correctly on your Server')
- return 'Failed to Send Mail. No SMTP Mail Server Defined'
- }
- def email = new Email(emailAddr)
- email.setMimeType('text/html')
- email.setSubject(subject)
- email.setBody(body)
- try {
- // This is needed to avoid the exception about IMAPProvider
- ContextClassLoaderSwitchingUtil.runInContext(SMTPMailServer.classLoader) {
- mailServer.send(email)
- }
- } catch (MailException e) {
- logger.debug("Send mail failed with error: ${e.message}")
- 'Failed to Send Mail, Check Logs for error'
- }
- }
- String getManagerEmail(tokenOwnerUser) {
- def logger = Logger.getLogger(getClass())
- logger.setLevel(Level.DEBUG)
- UserPropertyManager userPropertyManager = ComponentAccessor.getUserPropertyManager()
- PropertySet userProperties = userPropertyManager.getPropertySet(tokenOwnerUser)
- //logger.debug("All user properties: ${userProperties}")
- def managerEmail = userProperties.getString("jira.meta.myManager")
- return managerEmail
- }
- Boolean sendToManager(tokenOwner) {
- GroupManager groupManager = ComponentAccessor.getComponent(GroupManager)
- if (groupManager.getUserNamesInGroup("your-group-name-here").contains(tokenOwner.getUsername())) {
- return true
- }
- return false
- }
- Boolean ExpirationReminder(warnEpochFrom, warnEpochUntil, nextPageURL) {
- // Jira BaseURL, change accordingly
- def baseURL = "https://your-jira-base-url.com"
- // User with create token on behalf permission
- def userName = "admin"
- // API Token or password
- def password = "passwordOrToken"
- // Add additional recipient e-mail addresses to the array [] in double quotes and separated by comma
- def additionalRecipients = []
- String encodedAuthString = "Basic " + ("$userName:$password".bytes.encodeBase64().toString())
- def pathFrom = "/rest/de.resolution.apitokenauth/latest/user/tokensByFilter?fromExpiresDuring="
- def pathUntil = "&untilExpiresDuring="
- String queryURL = baseURL + pathFrom + warnEpochFrom + pathUntil + warnEpochUntil
- if (nextPageURL != "") {
- queryURL = nextPageURL
- }
- def restClient = new RESTClient(queryURL)
- restClient.setHeaders([Authorization: encodedAuthString, Accept: 'application/json'])
- def resp = restClient.get(contentType: JSON)
- def resultObject = resp.data
- def tokenArray = resultObject['content']
- // send warning email for each token about to expire that day
- tokenArray.each {
- def tokenDescription = it['description']
- def date = new Date(it['validUntil'])
- SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd" )
- def dateString = dateFormat.format(date)
- def userManager = ComponentAccessor.getUserManager()
- def recipient = userManager.getUserByKey(it['tokenForUserKey'])
- def tokenOwnerEmail = recipient.getEmailAddress()
- // mail to token owner
- sendEmail(tokenOwnerEmail, "API Token Expiration Warning", "Your token with description '${tokenDescription}' will expire ${dateString}")
- // mail to manager
- def managerEmail = getManagerEmail(recipient)
- if (managerEmail != null && sendToManager(recipient) == true) {
- sendEmail(managerEmail, "API Token Expiration Warning for user ${tokenOwnerEmail}", "API token of user ${tokenOwnerEmail} with description '${tokenDescription}' will expire ${dateString}")
- }
- // mail to additional recipients
- additionalRecipients.each {
- sendEmail(it, "API Token Expiration Warning for user ${tokenOwnerEmail}", "API token of user ${tokenOwnerEmail} with description '${tokenDescription}' will expire ${dateString}")
- }
- }
- // if there is a next page with more tokens expiring soon, process it as well
- if (resultObject['paginationLinks']['nextPage'] != "") {
- ExpirationReminder("","", resultObject['paginationLinks']['nextPage'])
- }
- }
- // check and warn for tokens expiring in 1 to 5 days
- for (int i = 1; i<6; i++) {
- use (groovy.time.TimeCategory) {
- warnDateFrom = i.day.from.now
- warnEpochFrom = warnDateFrom.getTime()
- warnEpochUntil = warnEpochFrom + (24 * 60 * 60 * 1000) - 1
- ExpirationReminder(warnEpochFrom, warnEpochUntil, "")
- }
- }
Adjust Script
You need to adjust the values in the lines below. Some of them are mandatory, some aren't.
Mandatory Variables
line 74, def baseURL - enter your Jira base URL in double-quotes, without the trailing forward-slash
line 76, def userName - provide a username that has the Create & Delete Token On Behalf Permission
line 78, def password - provide a password or better Read-Only API Token for that user
Optional Variables
line 58, replace myManager with the Jira user properties field name that contains the e-mail address of the manager of the token owner
be careful and don't remove the jira.meta. prefix
line 64, replace your-group-name-here with a group name that the token owner needs to be a member of if their manager should receive a notification as well
line 80, def additionalRecipients = [] - add one or more e-mail addresses in double quotes and separated by a comma who should always receive the same expiration warning the token owner receives or leave the array empty
Example: def additionalRecipients = ["user1@company.com","user2@company.com"]
lines 107, 112 and 118 contain the function calls for the sendEmail function and you could adjust the second and third parameters (subject and text body of the email)
variables available in the function scope are enclosed in in ${}, i.e. ${tokenOwnerEmail} so that you can build custom subjects and text bodies
Result
The script will run at the scheduled time and search for tokens that are about to expire in the next 5 days. You could adjust line 132 and increase or decrease the counter if you want to cover more or fewer days.
There will be an e-mail for each expiring token.