Hi!
After reading this article, I have tried to implement automation scripts for my Jira instance.
Today, I would like to share a few scripts related to clean up inactive users references to issue properties, group, role membership.
Disclaimer, as usual, everything should be tested in the test-environment first.
Also, I'll be happy if you share or provide advice for the related small scripts. All scripts were tested and executed on Jira 7 Server releases (7.2.1, 7.6.3, 7.6.10).
Well, you can use for execute groovy on Jira one of these apps (add-ons) - Scriptrunner for Jira (commercial) , MyGroovy (Opensource BSD-2) , Groovioli (Opensource BSD-2)
Well, let's next step is cleanup our Jira instance.
1. Let's remove memberships from group and project role for inactive users. (This script has been adopted for Jira 7 from Jamie Echlin answer from answer.atlassian.com)
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.util.SimpleErrorCollection
import com.atlassian.jira.bc.projectroles.ProjectRoleService
import com.atlassian.jira.project.Project
import com.atlassian.crowd.embedded.api.Group
import com.atlassian.jira.security.roles.actor.UserRoleActorFactory
import com.atlassian.jira.security.GlobalPermissionManager
import org.apache.log4j.Logger
import org.apache.log4j.Level
def log = Logger.getLogger("com.gonchik.scripts.groovy.deleteInActiveUsersFromGroup")
log.setLevel(Level.DEBUG)
UserSearchService userSearchService = ComponentAccessor.getComponent(UserSearchService.class);
UserSearchParams userSearchParams = (new UserSearchParams.Builder()).allowEmptyQuery(true).includeActive(false).includeInactive(true).maxResults(100000).build();
boolean isPreview = false
for (ApplicationUser appUser : userSearchService.findUsers("", userSearchParams)) {
ApplicationUser user = appUser
def userUtil = ComponentAccessor.userUtil
// get the first user of the first group which has the ADMIN privilege...
// cannot use current user, not really sure who that is when run as a service
def adminUser = userUtil.getJiraAdministrators()[0]
log.debug("deactivateUser ${user.getName()}")
// Remove user from all groups...
def groups = userUtil.getGroupsForUser(user.name)
if (!groups.isEmpty()) {
try {
userUtil.removeUserFromGroups(groups, user)
log.info(userToRemove.name + " from groups")
} catch (Exception e){
log.info(user.name + " should be reviewed in AD")
log.error(e)
}
}
// Remove user from all roles...
ProjectRoleService projectRoleService = (ProjectRoleService) ComponentManager.getComponentInstanceOfType(ProjectRoleService.class);
SimpleErrorCollection errorCollection = new SimpleErrorCollection();
log.debug("Removing all roles references for ${user.getName()}")
projectRoleService.getProjectsContainingRoleActorByNameAndType(adminUser, user.getName(), 'atlassian-user-role-actor', errorCollection).each { Project project ->
log.debug("Remove user ${user.getName()} from role: ${project.getName()}")
}
if (!isPreview) {
projectRoleService.removeAllRoleActorsByNameAndType(adminUser, user.getName(), 'atlassian-user-role-actor', errorCollection)
}
println errorCollection.dump()
}
2. Let's set unfavourite for inactive users favourite filters and dashboards.
def decreaseFavouriteCounter(ApplicationUser appUser){
def portalPageService = ComponentAccessor.getComponent(PortalPageService.class)
def pages = portalPageService.getFavouritePortalPages(appUser)
JiraServiceContext serviceContext = new JiraServiceContextImpl(appUser)
def isFavourite = false
pages.each { page ->
portalPageService.updatePortalPage(serviceContext, page, isFavourite)
log.debug "| ${page.id} | ${page.name} | ${page.favouriteCount} | ${page.ownerUserName} | ${page.permissions.isPrivate()} |"
}
// decrease the filter counts
def searchRequestService = ComponentAccessor.getComponent(SearchRequestService.class)
def filters = searchRequestService.getFavouriteFilters(appUser)
filters.findAll { filter ->
searchRequestService.updateFilter(serviceContext, filter, isFavourite)
log.debug "| ${filter.id} | ${filter.name} | ${filter.favouriteCount} | ${filter.ownerUserName} | ${filter.permissions.isPrivate()} |"
}
}
3. Let's remove private dashboards and dashboards favourite counter <= 1 for inactive users.
def deletePrivateDashBoards(ApplicationUser appUser){
def portalPageService = ComponentAccessor.getComponent(PortalPageService.class)
def pages = portalPageService.getOwnedPortalPages(appUser)
JiraServiceContext serviceContext = new JiraServiceContextImpl(appUser)
pages.each { page ->
if (page.permissions.isPrivate() || page.favouriteCount <= 1) {
portalPageService.deletePortalPage(serviceContext, (long) page.id)
log.debug "| ${page.id} | ${page.name} | ${page.favouriteCount} | ${page.ownerUserName} | ${page.permissions.isPrivate()} |"
}
}
}
4. Let's to do the same as p.3 for filters.
def deletePrivateFilters(ApplicationUser appUser){
def searchRequestService = ComponentAccessor.getComponent(SearchRequestService.class)
def filters = searchRequestService.getOwnedFilters(appUser)
JiraServiceContext serviceContext = new JiraServiceContextImpl(appUser)
filters.findAll { filter ->
if (filter.permissions.isPrivate() || filter.favouriteCount <= 1) {
searchRequestService.deleteFilter(serviceContext, (long) filter.id)
log.debug "| ${filter.id} | ${filter.name} | ${filter.favouriteCount} | ${filter.ownerUserName} | ${filter.permissions.isPrivate()} |"
}
}
}
5. Also, I don't like to see in watchers field inactive users. Therefore let's remove inactive users from watchers fields.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.watchers.WatcherManager
import org.apache.log4j.Logger
import org.apache.log4j.Level
def log = Logger.getLogger("com.gonchik.scripts.groovy.cleanupStopWatchingInactiveUsers")
log.setLevel(Level.DEBUG)
// this script shows how to clean up the inactive watchers
UserSearchService userSearchService = ComponentAccessor.getComponent(UserSearchService.class)
UserSearchParams userSearchParams = (new UserSearchParams.Builder()).allowEmptyQuery(true).includeActive(false).includeInactive(true).maxResults(100000).build()
WatcherManager watcherManager = ComponentAccessor.getComponent(WatcherManager.class)
for (ApplicationUser appUser : userSearchService.findUsers("", userSearchParams)) {
ApplicationUser userToRemove = appUser
watcherManager.removeAllWatchesForUser(userToRemove)
log.debug('Done for ' + userToRemove.getName())
}
6. And let's remove votes from inactive users. This thing was very useful for migrating Jira projects.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.vote.VoteManager
import org.apache.log4j.Logger
import org.apache.log4j.Level
def log = Logger.getLogger("com.gonchik.scripts.groovy.cleanupVotesForInactiveUsers")
log.setLevel(Level.DEBUG)
// This script shows how to clean up the votes from inactive users
UserSearchService userSearchService = ComponentAccessor.getComponent(UserSearchService.class)
UserSearchParams userSearchParams = (new UserSearchParams.Builder()).allowEmptyQuery(true).includeActive(false).includeInactive(true).maxResults(100000).build()
VoteManager voteManager = ComponentAccessor.getComponent(VoteManager.class)
for (ApplicationUser appUser : userSearchService.findUsers("", userSearchParams)) {
ApplicationUser userToRemove = appUser
voteManager.removeVotesForUser(userToRemove)
log.debug('Done for ' + userToRemove.getName())
}
7. Of course, you can use tools from article, for very useful is Integrity checker from Configuration manager.
References:
https://community.atlassian.com/t5/Jira-articles/Spring-Cleaning-for-Jira/ba-p/766063
https://community.atlassian.com/t5/Jira-articles/Continuous-cleanup-for-Jira-Part-2/ba-p/997326
Cheers,
Gonchik Tsymzhitov
Gonchik Tsymzhitov
Solution architect | DevOps
:)
Cyprus, Limassol
175 accepted answers
8 comments