We're doing some user cleanup to free up some licenses on our enterprise account. I have a csv of atlassian IDs that I'll run the Delete user API call on. https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-users/#api-rest-api-3-user-delete
Here's my question: Before we bulk deactivate these accounts, what is the best way to ensure that any needed filters and dashboards are still available? I do not want to affect any of our existing business processes inadvertently after I deactivate these accounts. I pulled the first 50 filters through Search for filters and Get filter https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-filters/#api-rest-api-3-filter-id-get and noticed that several of them were owned by people in my list to deactivate.
Is this even an issue I should stress over before deactivating these accounts? Will the filters and dashboards stay available after the account is deactivated?
I'm referencing this dead thread here "What to think about before deleting a user?" Linda never seemed to get a sufficient response.
Disable or remove inactive Jira users in bulk in Jira Cloud | Jira | Atlassian Documentation
Hi @Hunter Lardy
deleting users does not affect the Dashboards or Filters that they own. They will simply remain as they are and all users will still be able to use them.
A while ago, those filters and dashboards would've become uneditable, but especially in Cloud, Admins are always able to edit them, even if they're not granted edit permission specifically.
One thing you might want to keep in mind: It's possible that if any of the deleted users are mentioned within JQL of a filter, these filters will break. But I'm not entirely sure about that; it's possible that - in Cloud - a placeholder will be used for their name and the filter still works.
So, in short: You don't have to worry about Filters or Dashboards becoming lost when you delete their owners' user accounts.
Greetings
Philipp
Good morning @Philipp Sendek . Thanks for taking the time to answer my question.
I'm getting conflicting answers between you and Atlassian Support. I posed the same question as above to them, and received this answer.
Thank you for reaching out to Atlassian Support. I'm Alexandre and I will assist you today with this issue.
Let me first clarify: you want to know how to keep filters and dashboards available while bulk deactivating users; is that correct?
Deleting a user from an instance removes the filters/dashboard associated with that user.
If the filter is associated with a board, it makes the board unusable.
A user with Administer Jira Global permission can take over ownership of any such filters.
Dashboards of deleted users will remain in Trash for 60 days before being permanently deleted. Steps to restore, here: https://support.atlassian.com/jira-cloud-administration/docs/manage-shared-dashboards/#Restore-a-dashboard-from-trash
So, ideally, you will have to change the owner of these filters and dashboards before, although there is no public doc, I found the endpoints that you can use to achieve this: [insert REST calls I already know about].
--------------------------
Do you have a source for your claim?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Hunter Lardy ,
thank you for circling back and sharing this with me. As the Cloud products keep evolving also in their internal workings, it's possible that my statement isn't accurate anymore and you should trust the statement of Atlassian Support or simply test it out with a dummy user to be 100% sure. I haven't tested it personally recently.
Such things happen and if I need to be 100% certain, I just test it with example data (and recommend everyone to do the same).
Apologies for my answer if it was indeed inaccurate and thank you again for pointing out the contradicting statement by Support!
Greetings
Philipp
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Philipp Sendek .
After doing some of my own testing, I'm finding that filters and dashboards do seem to function as normal after a user is deactivated.
At first, we were unable to locate some filters from a deactivated user. We found that was because all we knew at the time was the owner (who is not searchable from the All Filters page after deactivation). We just need to know the filter name or ID and we can access it after we deactivate someone.
Thanks again for responding. It does seem like your explanation has more truth that Support's in this instance.
We do still plan to change the owner of our deactivated user's filters to one of the Jira admins, just to keep them in one place. If anyone's curious, I could share the Python script I made to accomplish this automagically. (I'm no professional developer, no warranties xD).
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Hunter Lardy ,
thank you once again for taking the time to share your test results here. It might be logical that content of those users will be deleted too, but it has never been the case for anything I can think of in Jira and it would also be unnecessarily destructive when the configuration items are shared.
However, what could be the case is that Dashboards and Filters that aren't shared with anyone (hence that are private) will be deleted with the owner's account; that would also solve the issue of cluttering the instance as all of those elements can sum up over time.
Feel free to share the script in case someone else might need it at some point stumbling over this question. :-)
Greetings
Philipp
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Sure.
Below is a script to read a CSV file of Atlassian accounts we'd like to deactivate, figure out if those accounts own any filters, then "re owner" the filters to one of our admins.
CSV file looks like the first 3 columns when exported from the managed users list in Atlassian Admin, with the headers of the columns removed.
Hunter Lardy does NOT guarantee this will work for you!
**********Please read and understand this script before turning off the DEBUG flag near the top. *********
# -*- coding: utf-8 -*-
"""
Created on Tue Mar 18 16:58:45 2025
@author: hlardy
"""
import requests
from requests.auth import HTTPBasicAuth
import csv
import json
# Authentication credentials
auth = HTTPBasicAuth("email", "apitoken")
# **********************
# Keep this on until you're satisfied with the debugging outputs.
# **********************
DEBUG = True
# Grab a list of all filters in the instance of Jira
# Input args: authentication token, starting page.
# Search filters call only returns up to 100 items. Need to keep calling
# until we get isLast==True.
# Output: dictionary
# Notes:
# Will need to change the searchURL to the other Jira instances and
# rerun main to cover the different Jira sites if we have multiple sites in
# our organization
def search_filters(auth, startPage):
searchURL = "https://domain.atlassian.net/rest/api/3/filter/search"
headers = {
"Accept": "application/json",
}
query = {
"maxResults": 100,
"startAt": startPage,
"expand": 'owner'
}
response = requests.request(
"GET",
searchURL,
headers=headers,
params=query,
auth=auth
)
filters = json.loads(response.text)
return filters
# GET a filter by filter ID - not needed in main below. Keeping for posterity.
# Input args: authentication token, filterID
# Output: dictionary
# Notes:
# Will need to change the searchURL to the other Jira instances and
# rerun main to cover the different Jira sites if we have multiple sites in
# our organization
def get_filter(auth, filterId):
getURL = "https://domain.atlassian.net/rest/api/3/filter/" + filterId
headers = {
"Accept": "application/json",
}
response = requests.request(
"GET",
getURL,
headers=headers,
auth=auth
)
tempFilter = json.loads(response.text)
return tempFilter
# PUT - change the owner of a filter in Jira
# Input args: auth token, filter ID, Atlassian ID
# Output: boolean
# Notes:
# Will need to change the searchURL to the other Jira instances and
# rerun main to cover the different Jira sites.
def put_filter(auth, filterId, newOwnerId):
putURL = "https://domain.atlassian.net/rest/api/3/filter/" + filterId + "/owner"
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
payload = json.dumps( {
"accountId": newOwnerId
} )
response = requests.request(
"PUT",
putURL,
data=payload,
headers=headers,
auth=auth
)
if response.status_code == 404:
print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
return False
elif response.status_code == 403:
print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
return False
else:
return True
# Main
# Read a CSV file with target Atlassian accounts to deactivate.
# My CSV file has column 1: name, column 2: email, column 3: AtlassianID. No headers
# Check if these accounts have any filters they own.
# Change the filter owner to an admin
# Create two lists from columns in the CSV, Atlassian IDs and Names
all_atlassian_ids = []
all_atlassian_names = []
with open('2025-03-17JiraAccountsToDelete.csv') as csvDataFile:
csvReader = csv.reader(csvDataFile)
for row in csvReader:
all_atlassian_ids.append(row[2])
all_atlassian_names.append(row[0])
# Grab all filters from Jira API
startPage = 0
isLast = False
filter_owners = []
filter_ids = []
# Search API call only returns up to 100 results. Loop until isLast == True
while isLast != True:
# Set isLast to True. Avoid infinite loop if it's missing from Search call.
isLast = True
# Call search_filters, return a nested dictionary with <= 100 entries
# in the values key
filter_dict = search_filters(auth, startPage)
isLast = filter_dict["isLast"]
# Iterate throught the filters. Build an array of Filter IDs and Filter Owners
i = 0
while i < 100:
try:
filter_ids.append(filter_dict["values"][i]["id"])
# *************************************************
# the line below is where everything could go wrong.
# Only as good as the data input. no good error checking.
# *************************************************
filter_owners.append(filter_dict["values"][i]["owner"]["accountId"])
except:
i = 100
i += 1
startPage += 100
# check your work
if DEBUG == True:
i = 0
while i < len(filter_ids):
print(filter_ids[i] + " : " + filter_owners[i])
i += 1
# Find if the filter owner is in our AtlassianID list. Add those filter IDs to a new list
filter_toReowner = []
atlassianIds_toDel = []
i = 0
while i < len(filter_owners):
if filter_owners[i] in all_atlassian_ids:
filter_toReowner.append(filter_ids[i])
atlassianIds_toDel.append(filter_owners[i])
print("Filter ID: " + filter_ids[i] + " Owner: " + filter_owners[i])
i += 1
if DEBUG == True:
for checkYourWork in filter_toReowner:
print(checkYourWork)
for checkYourWork in atlassianIds_toDel:
print(checkYourWork)
# Build another list with names instead of IDs
atlassianNamesWithFilters = []
for Id in atlassianIds_toDel:
i = 0
while i < len(all_atlassian_ids):
if Id == all_atlassian_ids[i]:
atlassianNamesWithFilters.append(all_atlassian_names[i])
break
i += 1
# check your work
if DEBUG == True:
i = 0
while i < len(filter_toReowner):
print(filter_toReowner[i] + " : " + atlassianNamesWithFilters[i])
i += 1
# Change the owner of filters that are owned by people we want to deactivate
if DEBUG == False:
for filterId in filter_toReowner:
print("Attempt to PUT " + filterId)
if put_filter(auth, filterId, "*****ATLASSIAN_ID_OF_ADMIN*****") != True:
raise Exception("Failed to PUT")
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.