Here’s a very basic example of a script to review group membership on Jira Server/DC

By first fetching the groups, and then the users in each group, we take the most efficient path toward only fetching the users who are in a group.

On the other hand, we could also tweak this script to show us users who are NOT in a group, or who are in  X or fewer groups.   That might be interesting, too.


 import com.atlassian.jira.component.ComponentAccessor

def groupManager = ComponentAccessor.getGroupManager()
def groups = groupManager.getAllGroups()
def sb = []
//Define a string buffer to hold the results

sb.add("<br>Group Name, Active User Count, Inactive User Count, Total User Count")
//Add a header to the buffer
groups.each{ group ->

 def activeUsers = 0
 def inactiveUsers = 0
 Each time we iterate over a new group, the count of active/inactive users gets set back to zero
 def groupMembers = groupManager.getUsersInGroup(group)
 //For each group, fetch the members of the group
    groupMembers.each{ member ->
    //Process each member of each group
    def memberDetails = ComponentAccessor.getUserManager().getUserByName(
    //We have to fetch the full user object, using the *name* attribute of the group member
            activeUsers += 1 
            inactiveUsers += 1
    }//Increment the count of 

There’s a simple way to return a list of field configurations and field configuration schemes in Jira DC/Jira Server.  However, in order to find that information you have to know that Jira once referred to these as field layouts

Using the FieldLayoutManager class, this script returns a list of field layouts:

 import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutManager

def layoutManager = ComponentAccessor.getFieldLayoutManager()
def fieldLayouts = layoutManager.getEditableFieldLayouts()
def sb = []

fieldLayouts.each{ fieldLayout ->

    sb.add("<br> ${}")


return sb 


This script returns the field layout schemes, with a simple change of the method:

 import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutManager

def layoutManager = ComponentAccessor.getFieldLayoutManager()
def layoutSchemes = layoutManager.getFieldLayoutSchemes()
def sb = []

layoutSchemes.each{ layoutScheme ->

    sb.add("<br> ${}")


return sb 

This simple script fetches all projects, then fetches each issue in the project.  For each issue, it counts the number of attachments and adds it to a running tally for that project.

 import com.atlassian.jira.component.ComponentAccessor

def projectManager = ComponentAccessor.getProjectManager()
def projects = projectManager.getProjectObjects()
def issueManager = ComponentAccessor.getIssueManager()

projects.each{ project ->

  def attachmentsTotal = 0
  def issues = ComponentAccessor.getIssueManager().getIssueIdsForProject(

  issues.each{ issueID ->

    def issue = ComponentAccessor.getIssueManager().getIssueObject(issueID)
    def attachmentManager = ComponentAccessor.getAttachmentManager().getAttachments(issue).size()
    attachmentsTotal += attachmentManager

  log.warn(project.key + " - " + attachmentsTotal)


Tempo Planner allows for planning team capacity and schedules within Jira.  However, you may have some need to pull that resource planning information out of the Tempo interface and add it to a ticket.

The Tempo API has some severe limitations, but where there’s a will there’s a way.

Team Info

The first thing we’ll examine is how to get information on all of the teams in Tempo Planner.  According the documentation, this isn’t possible.  Per the API documentation, you can return limited very information about plans and allocations. 

Naturally I found this to be unacceptable, and I figured out a way to have the API return all of the teams.   One of the undocumented API endpoints is a search function: /rest/tempo-teams/3/search.    One of the tricks to using this method is that it’s not a GET, it’s a POST, so we have to supply a search parameter as a payload.  When we POST to this endpoint, we supply some JSON: {“teamSearchString”:”<string>”}.  But here’s the rub: the API will accept an empty search string, and return all of the teams as a result.

Allocation Info

Much like team info, there is no public Tempo API endpoint that will

I try to learn something new every day when it comes to Groovy, or at the very least to challenging my understanding of some of the tenets of the syntax that I think I already know.

This morning I was playing around with the assert statement in ScriptRunner, trying to understand the nuances of it.   I was getting frustrated because I wanted the assertion to fail, but then for the script to keep going.  I couldn’t figure out how to capture the error without having the script stop.

I tried a few things.   I know you can include a log statement to the end of an assertion, like so:

 assert (1 == 2) : "Assertion failed"  


 java.lang.AssertionError: Assertion failed. Expression: (1 == 2)
	at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(

But even with the inclusion of the log statement, my script would grind to a halt when the assertion failed.    I also tried putting the assertion in a try/catch statement, but that didn’t work at all.

I then realized that I had no idea what the functional difference between an assertion and a try/catch statement actually was.  Moreover, I didn’t know when it was appropriate to use one or the