After playing around through some community threads and testing, I created a script that allows you to
- Find users in a specified directory
- Determine if they have data assigned, reported, or commented (and if not, are safe to delete)
- Removes user
This is great for removing unneeded JSM customers in datacenter that appear in your user list under Internal Directory.
Recommended you limit results to 20k or less and run iteratively
(Script broken into two parts due to formatting)
import com.atlassian.crowd.manager.directory.DirectoryManager
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.component.ComponentAccessor
import groovy.xml.MarkupBuilder
import java.io.StringWriter
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.bc.user.UserService
final directoryToCheck = 'Jira Internal Directory'
// Get necessary Jira components
def directoryManager = ComponentAccessor.getComponent(DirectoryManager)
def userSearchService = ComponentAccessor.getComponent(UserSearchService)
def userService = ComponentAccessor.getComponent(UserService)
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
// Get the internal directory ID based on directory name
def internalDirectoryId = directoryManager.findAllDirectories()?.find { it.name.toString().toLowerCase() == directoryToCheck.toLowerCase() }?.id
// Build search parameters for user search
UserSearchParams.Builder paramBuilder = UserSearchParams.builder()
.allowEmptyQuery(true)
.includeActive(true) // Only active users
.includeInactive(false) // Exclude inactive users
.limitResults(200) // Adjust this if you have many users
// Get all active users from the internal directory
def allInternalDirectoryUsers = userSearchService.findUsers('', paramBuilder.build()).findAll { it.directoryId == internalDirectoryId }
// Method to check if a user does **not** have any assigned, reported, or commented issues
def isUserInactive(user) {
if (user instanceof ApplicationUser) {
def username = user.username
// Construct JQL queries for:
// - Issues assigned to the user
// - Issues reported by the user
// - Comments authored by the user
def assignedJQL = "assignee = \"${username}\""
def reportedJQL = "reporter = \"${username}\""
def commentedJQL = "issue in commentsBy(\"${username}\")"
try {
def assignedIssues = Issues.search(assignedJQL)
def reportedIssues = Issues.search(reportedJQL)
def commentedIssues = Issues.search(commentedJQL)
// If none of the queries return issues, the user is effectively inactive (no assigned, reported, or commented issues)
return (assignedIssues.size() == 0 && reportedIssues.size() == 0 && commentedIssues.size() == 0)
} catch (Exception e) {
println "Error executing JQL: ${e.message}"
return false
}
}
return false
}
// Collect inactive users for the table and prepare a list of deleted users
def inactiveUsers = []
def deletedUsers = []
allInternalDirectoryUsers.each { user ->
// Safely access user properties and check if they are active but have no issues or comments
if (user instanceof ApplicationUser && isUserInactive(user)) {
// Add the inactive user to the list (active but no issues, no comments)
inactiveUsers.add([username: user.username, email: user.emailAddress]) // Only add users with username and email
// Validate the deletion of the user
try {
def result = userService.validateDeleteUser(currentUser, user.username)
// Check if the validation is successful
if (result.isValid()) {
// If validation passes, delete the user
userService.removeUser(currentUser, result)
deletedUsers.add([username: user.username, email: user.emailAddress]) // Track deleted user
println "User deleted: ${user.username} (Email: ${user.emailAddress})"
} else {
println "User deletion validation failed for ${user.username}: ${result.errorCollection}"
}
} catch (Exception e) {
println "Error deleting user ${user.username}: ${e.message}"
}
}
}
// Create the StringWriter to generate HTML output
def stringWriter = new StringWriter()
def content = new MarkupBuilder(stringWriter)
// Create the header for the HTML output
content.html {
p("Users in Jira Internal Directory: ${allInternalDirectoryUsers?.size()}")
p("List of Deleted Users:")
// Print a table of deleted users
if (deletedUsers.size() > 0) {
table {
tr {
th("Username")
th("Email")
}
deletedUsers.each { user ->
tr {
td(user.username)
td(user.email)
}
}