Hi all,
We are trying to keep our user licenses under control, and have found the following code within the ScriptRunner library:
import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.bc.user.UserService
import com.atlassian.jira.bc.user.search.UserSearchParams
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.crowd.embedded.api.CrowdService
import groovy.xml.MarkupBuilder
import java.time.LocalDateTime
import java.time.Instant
import java.time.ZoneId
def crowdService = ComponentAccessor.getComponent(CrowdService)
// Number of days the user was not logged in Date
def numOfDays = 30
def dateLimit = LocalDateTime.now().minusDays(numOfDays)
// Search all active users
UserSearchParams.Builder paramBuilder = UserSearchParams.builder()
.allowEmptyQuery(true)
.includeActive(true)
.includeInactive(false)
def jiraServiceContext = new JiraServiceContextImpl(ComponentAccessor.jiraAuthenticationContext.loggedInUser)
def allActiveUsers = ComponentAccessor.getComponent(UserSearchService).findUsers(jiraServiceContext, '', paramBuilder.build())
// Users which last activity is before than limit date
def usersToDelete = allActiveUsers.findAll { user ->
def userWithAtributes = crowdService.getUserWithAttributes(user.username)
def lastLoginMillis = userWithAtributes.getValue('login.lastLoginMillis')
if (lastLoginMillis?.number) {
def lastLogin = Instant.ofEpochMilli(Long.parseLong(lastLoginMillis)).atZone(ZoneId.systemDefault()).toLocalDateTime()
if (lastLogin.isBefore(dateLimit)) {
user
}
}
}
if (!usersToDelete) {
return 'No Idle users found'
}
def stringWriter = new StringWriter()
def content = new MarkupBuilder(stringWriter)
content.html {
p('Follow users deactivated ') {
ul {
usersToDelete.each {
user -> deactivateUser(user)
}*.username?.each { deactivated ->
li(deactivated)
}
}
}
}
stringWriter.toString()
def deactivateUser(ApplicationUser user) {
def userService = ComponentAccessor.getComponent(UserService)
def updateUser = userService.newUserBuilder(user).active(false).build()
def updateUserValidationResult = userService.validateUpdateUser(updateUser)
if (!updateUserValidationResult.valid) {
log.error "Update of ${user.name} failed. ${updateUserValidationResult.errorCollection}"
return
}
userService.updateUser(updateUserValidationResult)
log.info "${updateUser.name} deactivated"
}
This would work, but we would also like to ensure that none of our admins are ever "De-Activated" if they are working within other Jira instances that month.
Can anyone suggest a way to include multiple groups that are not "searched" for de-activation?
Thanks,
Mike
Hi @Michael
For your requirement, you could try something like the below in the ScriptRunner console:-
import com.adaptavist.hapi.jira.groups.Groups
import com.adaptavist.hapi.jira.users.Users
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.login.LoginManager
def adminGroup = Groups.getByName('jira-administrators').members
def jiraUserGroup = Groups.getByName('jira-software-users').members
def jsmUserGroup = Groups.getByName('jira-servicedesk-users').members
def users = adminGroup + jiraUserGroup + jsmUserGroup
users.unique()
def loginManager = ComponentAccessor.getComponentOfType(LoginManager)
users.each {
def username = it.username
def active = it.active
def lastLoginTime = loginManager.getLoginInfo(it.username).lastLoginTime
if (!active || !lastLoginTime) {
Users.getByName(username).deactivate()
}
}
Please note that the sample code above is not 100% exact to your environment. Hence, you will need to make the required modifications.
The code above does a check for any inactive or users that have never logged in. If found they will be deactivated.
I hope this helps to solve your question. :-)
Thank you and Kind regards,
Ram
Hello again @Ram Kumar Aravindakshan _Adaptavist_
Thanks for the help.
To clarify, we really don't want to "de-activate" anyone, we just want to remove their "jira-software-user" group so that they aren't using up a Jira license. (Sorry for the mis-communication before)
Also will this allow us to add specific custom admin groups which will never be checked for removal? If so - can you please point me to the section where I can customize it to include our custom admin groups?
We'd like to make sure none of our admin groups are ever removed from the "jira-software-user" group.
**Update**
I've tried my best to put together some code based on the above for what I'm looking for below:
import com.adaptavist.hapi.jira.groups.Groups
import com.adaptavist.hapi.jira.users.Users
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.login.LoginManager
//For removal of user from group
def groupManager = ComponentAccessor.groupManager
def userUtil = ComponentAccessor.userUtil
def userManager = ComponentAccessor.userManager
def adminGroup = Groups.getByName('jira-administrators').members
def jiraUserGroup = Groups.getByName('jira-software-users').members
def customAdminGroup = Groups.getByName('CMS Support').members
def users = jiraUserGroup
users.unique()
def loginManager = ComponentAccessor.getComponentOfType(LoginManager)
users.each {
def username = it.username
def active = it.active
def lastLoginTime = loginManager.getLoginInfo(it.username).lastLoginTime
if (!lastLoginTime && (!adminGroup || !customAdminGroup)) {
userUtil.removeUserFromGroup(jira-software-user, username)
}
}
But it's incomplete, and I don't know how to change it to fix it :(
Could you possibly help again?
Thanks,
Mike
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Michael
Removing the inactive users from the Group is a different requirement from what you mentioened early.
I'll get back to you once I have modified the code.
Thank you and Kinder regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Michael
If you want to remove an inactive user from a particular Group, you can try the updated code below on the ScriptRunner Console:-
import com.adaptavist.hapi.jira.groups.Groups
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.login.LoginManager
def adminGroup = Groups.getByName('jira-administrators')
def jiraUserGroup = Groups.getByName('jira-software-users')
def jsmUserGroup = Groups.getByName('jira-servicedesk-users')
def adminGroupMembers = adminGroup.members
def jiraUserGroupMembers = jiraUserGroup.members
def jsmUserGroupMembers = jsmUserGroup.members
def users = adminGroupMembers + jiraUserGroupMembers + jsmUserGroupMembers
users.unique()
def loginManager = ComponentAccessor.getComponentOfType(LoginManager)
users.each {
def username = it.username
def active = it.active
def lastLoginTime = loginManager.getLoginInfo(it.username).lastLoginTime
if (!active || !lastLoginTime) {
jiraUserGroup.remove(username)
}
}
Please note that the sample code above is not 100% exact to your environment. Hence, you will need to make the required modifications.
Below are a couple of test screenshots for your reference:-
1. Before the script is executed, all the users are in the jira-software-users group as shown in the screenshot below:-
2. After the script has been executed, all the inactive users are removed from the jira-software-users group as shown in the screenshot below:-
I hope this helps to solve your question. :-)
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Could this be amended to use in Scriptrunner for Cloud, this would be useful in my system
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @James Carn
This not applicable for ScriptRunner for Jira cloud.
In the cloud environment you cannot invoke the objects directly. You need to make GET and POST requests to remove the users from the Groups.
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi James,
In Jira Cloud you would need to call the Add user to a Group and Remove User from a Group Rest APIs to add and remove users from a group.
However, in Jira Cloud it is not possible to get the last login time for a user as Atlassian does not provide this on the get User api.
However, they provide the Users Last Active Dates API to see when users last accessed each product which could be used to see when users last interacted with a specific Jira feature.
I hope this helps.
Regards,
Kristian
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi again @Ram Kumar Aravindakshan _Adaptavist_
Thanks for the updated code, sorry for the previous miss-communcation about removing users from a group.
Within the most recent code, is it possible to select specific groups which the code does not "scan" for in-active users? We have several custom made admin groups which contain users who will only log-in once every few months, but that we don't want to remove access from. would I be able to add an "&&" statement within the if statement for these groups?
Ex:
if (!active || !lastLoginTime && !Groups.getByName('custom_group_name')) {
jiraUserGroup.remove(username)
}
}
Thanks,
Mike
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Michael
So if you want to exclude certain users that belong to other specific groups from being removed from of jira-software-users group, you can try something like this:-
import com.adaptavist.hapi.jira.groups.Groups
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.login.LoginManager
def adminGroup = Groups.getByName('jira-administrators')
def jiraUserGroup = Groups.getByName('jira-software-users')
def jsmUserGroup = Groups.getByName('jira-servicedesk-users')
def devGroup = Groups.getByName('Development')
def adminGroupMembers = adminGroup.members
def jiraUserGroupMembers = jiraUserGroup.members
def jsmUserGroupMembers = jsmUserGroup.members
def devGroupMembers = devGroup.members
def users = adminGroupMembers + jiraUserGroupMembers + jsmUserGroupMembers
users.unique()
def loginManager = ComponentAccessor.getComponentOfType(LoginManager)
users.removeAll {
it in devGroupMembers
}
users.each {
def username = it.username
def active = it.active
def lastLoginTime = loginManager.getLoginInfo(it.username).lastLoginTime
if (!active || !lastLoginTime) {
jiraUserGroup.remove(username)
}
}
Please note the sample working code above is not 100% exact to your environment. Hence, you will need to make the required modifications.
In the code above, all users that belong to the jira-administrators, jira-software-users and jira-servicedesk-users are combined in the users list.
However, if any of the users in the users list belong to the Development group, they are excluded from the list.
Hence, the users belonging to the Development group will not be removed from the jira-software-users group. The other users will be taken out.
The only modification made in the code is the inclusion of these lines:-
def devGroupMembers = devGroup.members
....
....
....
users.removeAll {
it in devGroupMembers
}
I hope this helps to solve your question. :-)
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello again @Ram Kumar Aravindakshan _Adaptavist_
Thank you for the latest code block! It seems to work if there is only a single group added to that users.removeAll section, however; if I add several groups to that section, it looks like only the last group entered before the "}" seems to be excluded.
Ex:
import com.adaptavist.hapi.jira.groups.Groups
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.security.login.LoginManager
def adminGroup = Groups.getByName('PC Management')
//def jiraUserGroup = Groups.getByName('jira-software-users')
def testGroup = Groups.getByName('PeopleCloud - Base Users')
def exampleGroup = Groups.getByName('brand-experience-group')
def adminGroupMembers = adminGroup.members
//def jiraUserGroupMembers = jiraUserGroup.members
def testGroupMembers = testGroup.members
def exampleGroupMembers = exampleGroup.members
def users = exampleGroupMembers
users.unique()
def loginManager = ComponentAccessor.getComponentOfType(LoginManager)
users.removeAll {
it in testGroupMembers
it in adminGroupMembers
}
users.each {
def username = it.username
def active = it.active
def lastLoginTime = loginManager.getLoginInfo(it.username).lastLoginTime
if (!active || !lastLoginTime) {
exampleGroup.remove(username)
}
}
I changed the groups in the above to make sure the code worked before unleashing it on all of our users, and after it runs, the only group that was "excluded" from the search was the adminGroupMembers group. All users within the testGroupMembers had their 'brand-experience-group' group removed.
I've also tried to combine the two groups into a single line with a comma as well as an or statement "||" but neither of those seemed to work for me.
Can you please help me figure out how to exclude all groups under the removeAll section?
Thanks,
Mike
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Michael
After reading through your last comment, the modification you have made to the code will not work. The line below will fail:-
users.removeAll {
it in testGroupMembers
it in adminGroupMembers
}
The parameter is the removeAll { } method is a boolean method and works on boolean values returned in the condition.
Adding in one after the other doesn't mean it will exclude users in multiple groups.
For the condition to work, you must try something like this:-
users.removeAll {
it in adminGroupMembers || it in testGroupMembers
}
The condition above means that if the user is in the Admin Group or the Test Group, that user will be removed from the users list and excluded from group removal.
Please make the suggested change above and rerun the test.
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks Ram,
That worked perfectly! Have a great day ahead.
~Mike
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Michael ,
We've noticed that many users have been requesting an "Inactive User Anonymizer" feature for anonymizing users who have been inactive for a certain time. I’m pleased to inform you that our app, User Data Cleanup for Jira, includes this functionality. It allows you to easily anonymize inactively or never-logged-in users based on your specified time frame.
Key features of this app include:
Highly customizable and modular: Configure multiple cleanup rules that can be applied to a single or bulk of users. Maximize control over user-specific data cleansing.
Two-Stage Cleanup: Leaving Day & Permanent: Apply a two-stage cleanup process: deactivate, remove, or transfer specific user data on the leaving day, retain necessary information for traceability, and execute a permanent cleanup.
Preview & Retrospect: Cleanup Control: Before performing cleanup actions for single or multiple users, use the Preview to check all planned actions. Use the Retrospect to check previously cleaned data for specific users.
We believe UDC can save you time and effort, and we’d love for you to give it a try. You can find more details and download our app here.
Our docs: https://mgm-atlassian-apps.mgm-tp.com/user-data-cleanup/latest/overview
Best regards,
Duy
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello, quick question @Ram Kumar Aravindakshan _Adaptavist_ // @Kristian Walker _Adaptavist_
is this possible with Scriptrunner for Jira Cloud yet?
thanks a lot, kind regards
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Jesus,
It is still the same as the answer above: you cannot get users last login dates but add or remove users from groups with ScriptRunner for Jira Cloud.
Regards,
Kristian
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.