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

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

How do you analyze GC logs, thread dumps and head dumps?