Continuous cleanup for Jira. Part 1.

 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

Next article is here.

 

Cheers, 

Gonchik Tsymzhitov

Comments

Popular posts from this blog

How only 2 parameters of PostgreSQL reduced anomaly of Jira Data Center nodes

Stories about detecting Atlassian Confluence bottlenecks with APM tool [part 1]

Atlassian Community, let's collaborate and provide stats to vendors about our SQL index usage