ScriptRunner Groovy Script to get all users and project roles they are assigned

Whitni Smith May 17, 2024

What I want to accomplish: get a list of all the Jira instance active users and the projects and project roles they belong to by calling all the projects and then iterating through the project roles and getting all the actors. 

I'm working with ~500 active users

I've only used ScriptRunner on DC and at a novice level or manipulating existing scripts and there's a lot missing in Cloud so I've been unsuccessful in converting a DC script to Cloud, so I started with an existing Cloud script and I'm stuck and could use some help. 

I've anonymized the script I'm working with and pasted it below. 
I can successfully get all the project information but unsuccessful in project roles and actors. AND if I try to return the results formatted, I only get the query parameters spit back out, nothing about the projects. 

NOTE: I am OK if output is by project and I have to scrub the data and sort it to merge all the results for a user (example: user Jacob McCarthy is in project A as developer, user Jacob McCarthy is in project B as administrator, user Jacob McCarthy is in project F as collaborator) 

What I errors I know exist (I don't know how to fix them) - 
Iterating the role ID into the API call (id isn't a class -- is the error I get) 


import groovy.json.JsonSlurper


// String buffer to hold results

def sb = []


 // Get list of all the projects

def getProjects = get("https://your-domain.atlassian.net/rest/api/3/project/search")

    .header('Content-Type', 'application/json')

    .asJson()


// Get JSON body contents of the HTTP response

def content = getProjects.body


// Turn raw body into JSON 

def json = new JsonSlurper().parseText(content.toString())


json.each { project ->

    sb.add(project['key'])


// For each project, get the list of roles

    def getRoles = get("https://your-domain.atlassian.net/rest/api/3/project/${project.key}/role")

        .header('Content-Type', 'application/json')

        .asJson()

    
    getRoles.body.each { role ->

        def roleId = id

        def getRoleMembers = get("https://your-domain.atlassian.net/rest/api/3/role/${roleId}/actors")

            .header('Content-Type', 'application/json')

            .asJson()


        getRoleMembers.body.actors.each { roleMember ->

            sb.add("${role.actor}:   ${roleMember.displayName}")

        }

    }

}

return sb

I have also 

 

1 answer

1 accepted

2 votes
Answer accepted
Sean Chua _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 20, 2024

Hey @Whitni Smith ,

Since you are ok with:

NOTE: I am OK if output is by project and I have to scrub the data and sort it to merge all the results for a user (example: user Jacob McCarthy is in project A as developer, user Jacob McCarthy is in project B as administrator, user Jacob McCarthy is in project F as collaborator) 

You can try giving this a try. I used my Script Console to run it as (ScriptRunner for Jira Add-on User) and it gave me a text dump in the logs for the projects and the system admins/apps/users/etc.. It does take awhile to run..maybe test it in a test site first or something..

// Fetch the projects
def fetchAllProjects() {
    def projectsResponse = get('/rest/api/3/project/search')
        .header("Accept", "application/json")
        .asObject(Map)
    return projectsResponse.status == 200 ? projectsResponse.body.values : []
}

// Fetch the project roles
def fetchProjectRoles(String projectIdOrKey) {
    def rolesResponse = get("/rest/api/3/project/${projectIdOrKey}/role")
        .header("Accept", "application/json")
        .asObject(Map)
    return rolesResponse.status == 200 ? rolesResponse.body : [:]
}

// Fetch users for project
def fetchUsersForRole(String roleUrl) {
    def usersResponse = get(roleUrl)
        .header("Accept", "application/json")
        .asObject(Map)
    return usersResponse.status == 200 ? usersResponse.body.actors : []
}

// Display in the logs
def projects = fetchAllProjects()
projects.each { project ->
    logger.info("Processing project: ${project.name} (${project.key})")
    
    def projectRoles = fetchProjectRoles(project.id)
    projectRoles.each { roleName, roleUrl ->
        logger.info("Fetching users for role: ${roleName}")
        
        def users = fetchUsersForRole(roleUrl)
        users.each { user ->
            // Example filter to focus on human users (adjust as needed)
            if (!user.displayName.contains("App") && !user.displayName.contains("System")) {
                logger.info("User: ${user.displayName} has role: ${roleName} in project: ${project.name} (${project.key})")
            }
        }
    }
}

I'm sure you can kinda make it nicer and do it better as I'm not a coding pro..haha.. and since you will also be getting a text diarrhoea.. I think you can add some kind of filtering to remove those system/app stuff by adding like:

def filterOutUsers(def users) {
    // remove those with these indicators etc
    def systemUserIndicators = ["App", "JIRA", "Jira", "for Jira", "Automation", "Bot", "System"]
    return users.findAll { user ->
        !systemUserIndicators.any { indicator -> user.displayName.contains(indicator) }
    }
}


Those are my thoughts..give it a shot, and if that fails too - try opening a Support Ticket with us. Maybe there is a better way or better samples :)

 

Regards,
Sean

Whitni Smith May 20, 2024

@Sean Chua _Adaptavist_ thanks! your initial script worked fine and I just scrubbed the data using OpenRefine. I have a follow up -- I need to filter on the active value (only return users that have an active jira license -- haven't been suspended) with that being said, I adjusted the script as such - and it's not returning any values. 

import groovy.json.JsonSlurper

// Fetch the projects
def fetchAllProjects() {
def projectsResponse = get('/rest/api/3/project/search')
.header("Accept", "application/json")
.asObject(Map)
return projectsResponse.status == 200 ? projectsResponse.body.values : []
}

// Fetch the project roles
def fetchProjectRoles(String projectIdOrKey) {
def rolesResponse = get("/rest/api/3/project/${projectIdOrKey}/role")
.header("Accept", "application/json")
.asObject(Map)
return rolesResponse.status == 200 ? rolesResponse.body : [:]
}

// Fetch users for project
def fetchUsersForRole(String roleUrl) {
def usersResponse = get(roleUrl)
.header("Accept", "application/json")
.asObject(Map)
return usersResponse.status == 200 ? usersResponse.body.actors : []
}

// Filter out inactive users
def filterOutInactiveUsers(def users) {
return users.findAll { user ->
user.active
}
}

// Display in the logs
def projects = fetchAllProjects()
projects.each { project ->
logger.info("Processing project: ${project.name} (${project.key})")

def projectRoles = fetchProjectRoles(project.id)
projectRoles.each { roleName, roleUrl ->
logger.info("Fetching users for role: ${roleName}")

def users = fetchUsersForRole(roleUrl)
def activeUsers = filterOutInactiveUsers(users)

activeUsers.each { user ->
// Example filter to focus on human users (adjust as needed)
if (!user.displayName.contains("App") && !user.displayName.contains("System")) {
logger.info("User: ${user.displayName} has role: ${roleName} in project: ${project.name} (${project.key})")
}
}
}
}

 

Sean Chua _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 21, 2024

hey @Whitni Smith , yeah, for active users specific. I guess it would depend on what you mean by active?

When I used the user.active like you have, it doesn't show as well, so the other way round I went was to "exclude" users which has the indicators in the displayname. 

def filterOutUsers(def users) {
    // remove those with these indicators etc
    def systemUserIndicators = ["App", "JIRA", "Jira", "for Jira", "Automation", "Bot", "System"]
    return users.findAll { user ->
        !systemUserIndicators.any { indicator -> user.displayName.contains(indicator) }
    }
}

Maybe it could be possible to use GET /rest/api/3/users , to check against if they are active but that is against if the user is active as a user like "active":true , meaning they are not suspended etc. , my brain fried abit, i just remember that for this one, ScriptRunner for Jira Cloud is a "Connect App", hence "Connect apps cannot access this REST resource." - which is likely why nothing comes up in the search. So cutting this piece out.

But if you want to see whether the user has recently logged in kind of info, then I think that would be entirely different, and I suppose you may need to use Organizations REST API like GET /v1/orgs/{orgId}/directory/users/{accountId}/last-active-dates or something which needs Cloud Admin tokens or something of the sorts..

At least for now, I can't think of the right way to get it.. But will try playing around a little more and see if I can get something going :) 

Regards,
Sean

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
CLOUD
PRODUCT PLAN
PREMIUM
TAGS
AUG Leaders

Atlassian Community Events