Example scripts
To homepage
Confluence

Deactivate inactive cloud user accounts
Tags
Created 6 month(s) ago, Updated 27 day(s) ago
App in script

ScriptRunner For Confluence
by Adaptavist
Compatibility

Confluence
Language |
groovy
import java.text.SimpleDateFormat
import com.atlassian.confluence.rest.clientv1.model.User
import com.atlassian.confluence.rest.clientv1.model.Group
// NOTE: Make sure to setup ORG_ID and ORG_API_KEY before running this script.
int USER_LIMIT_PER_GROUP = 10 // Define the user limit you want to maintain for the group.
def CUTOFF_DATE = "2024-01-01" // Follow the format YYYY-MM-DD.
def GROUP_NAME = "confluence-users" // Define the group where you want to find the inactive users.
def DISABLED_USERS_GROUP = "disabled-users-group" // Create the group before you run the script.
def FORCE_DISABLED = false // True: Disable inactive Confluence users, no matter if users has other product access (e.g. Jira).
def disabledUsersGroupId = fetchDisabledUsersGroupId(DISABLED_USERS_GROUP)
def groups = fetchGroups(GROUP_NAME)
def inactiveUsers = fetchInactiveUserIds(groups, CUTOFF_DATE, FORCE_DISABLED, USER_LIMIT_PER_GROUP)
logger.info("Found ${inactiveUsers.size()} users to disable")
disableUsers(groups, inactiveUsers, CUTOFF_DATE, disabledUsersGroupId)
void disableUsers(List<Group> groups, def inactiveUsers, def cutoffDateStr, def disabledUsersGroupId) {
inactiveUsers.each { userId ->
def res = post("https://api.atlassian.com/users/${userId}/manage/lifecycle/disable")
.header('Content-Type', 'application/json')
.header("Authorization", "Bearer ${ORG_API_KEY}")
.body([
"message": "Disable user for cutoff date: ${cutoffDateStr}",
])
.asObject(Map)
if (res.status != 204) {
logger.warn("Failed to disable user with id: ${userId}")
return
}
removeUserFromTheGroup(groups, userId)
moveUserToDisabledGroup(userId, disabledUsersGroupId)
}
}
void moveUserToDisabledGroup(def accountId, def disabledUsersGroupId) {
def res = post("https://api.atlassian.com/admin//v1/orgs/${ORG_ID}/directory/groups/${disabledUsersGroupId}/memberships")
.header('Content-Type', 'application/json')
.header("Authorization", "Bearer ${ORG_API_KEY}")
.body([
"account_id": accountId,
])
.asObject(Map)
if (res.status != 200) {
logger.warn("Failed to move user: ${accountId} to disabled-users-group")
}
}
void removeUserFromTheGroup(List<Group> groups, def accountId) {
groups.each { group ->
def res = delete("https://api.atlassian.com/admin/v1/orgs/${ORG_ID}/directory/groups/${group.id}/memberships/${accountId}")
.header("Authorization", "Bearer ${ORG_API_KEY}")
.asObject(Map)
if (res.status != 200) {
logger.warn("Failed to remove user: ${accountId} from the group: ${group.name}")
}
}
}
Set<String> fetchInactiveUserIds(List<Group> groups, String cutoffDateStr, def forceEnabled, int userLimit) {
def usersToDisable = []
groups.each { group ->
def users = fetchMembersInGroup(group.id)
if (users.size() < userLimit) {
return
}
users.each { user ->
def res = get("https://api.atlassian.com/admin/v1/orgs/${ORG_ID}/directory/users/${user.accountId}/last-active-dates")
.header("Authorization", "Bearer ${ORG_API_KEY}")
.asObject(Map).body
def dateFormat = new SimpleDateFormat("yyyy-MM-dd")
def cutoffDate = dateFormat.parse(cutoffDateStr)
def products = (List) res["data"]["product_access"]
def targetProduct = products.find { product ->
product["key"] == "confluence"
}
if (!targetProduct) {
logger.warn("The user ${user.displayName} has no access to confluence product.")
return
}
if (!forceEnabled && products.size() > 1) {
logger.warn("The user ${user.displayName} has access to other products. If you want to disable it by force. Please set the variable FORCE_DISABLED to true")
return
}
def lastActiveDate = dateFormat.parse((String) targetProduct["last_active"])
def productKey = targetProduct["key"]
if (productKey == "confluence" && lastActiveDate.before(cutoffDate)) {
usersToDisable.add((String) user.accountId)
}
}
}
// eliminate duplicated userIds
new HashSet(usersToDisable)
}
List<User> fetchMembersInGroup(def groupId) {
def members = get("/wiki/rest/api/group/${groupId}/membersByGroupId").asObject(Map).body
if (!members) {
throw new RuntimeException("Failed to fetch members or no members found.")
}
(List<User>) members.results
}
List<Group> fetchGroups(def groupNames) {
def groups = get("/wiki/rest/api/group/picker?query=${groupNames}").asObject(Map).body
if (!groups) {
throw new RuntimeException("Failed to fetch groups or no groups found.")
}
(List<Group>) groups.results
}
def fetchDisabledUsersGroupId(def disabledUserGroupName) {
def disabledUsersGroup = fetchGroups(disabledUserGroupName)
if (disabledUsersGroup.size() !== 1) {
throw new RuntimeException("Failed to find ${disabledUserGroupName} or you have more than one group with the same name")
}
disabledUsersGroup[0].id
}
Having an issue with this script?
Report it here