It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

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

 

8 comments

Deleted user Jan 02, 2019

Hi @Gonchik Tsymzhitov,

Cannot wait to try these scripts out, as a JIRA Admin inactive users can become an issue when migrating data so I’m really happy that I found your article.

Do you know when part 2 will be available? 

Thanks 🙏 

Hi @[deleted] , 

 

you are right about migrating Jira instances :) I am also, on that found many interesting things. 

I am thinking which part of script I need to provide :) 

1. SQL consistency checker and cleanup on SQL level

2. Lucene index analyses

3. Cleanup and analyses 2 Jira isntances by REST  API - ( I use python for to do, it is need to do daily cleanup :) )

4. Groovy cleaners :) 

 

Cheers,

Gonchik Tsymzhitov

These are great "cut and paste" scripts that most of us can use.  Thank you.

Daniel Eads Atlassian Team Nov 03, 2019

Great work Gonchik! This helps reduce the time needed for cleanup significantly.

@Daniel Eads  @Robin C_  Thanks!

Feel free to add more info, 

https://github.com/gonchik/cleanup-scripts

 

Also I can mirror into bitbucket as well

Hi @Gonchik Tsymzhitov 

Any chance you would update these scripts to match JIRA 8.6.X?

Hi @Casper Hjorth Christensen , 

sure, I have plan to upgrade, also, I see some users already send to me a Pull Request.

Comment

Log in or Sign up to comment
TAGS

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you