Github enterprise can generate a CSV report about dormant / inactive users, but you have then to manually remove these users and when you have many, it takes a lot of time.
Is there any API or script allowing to remove all these users from the enterprise, and all organizations it contains?
I had no luck to find such API, script, or command in Github CLI, so i asked Github support, and they shared this GraphQL API, which is quite a recent addition: https://docs.github.com/en/enterprise-cloud@latest/graphql/reference/mutations#removeenterprisemember
My AI friend and I developed this Python script, that hopefully will be useful to you too. The little trick to know about GraphQL is that ids, for users, enterprise, etc are specific, you cannot use the same ids/slugs than the ones you would use with the REST API. The first step is to get the id of your Github enterprise, and then the id of the users you want to remove.
import os
import requests
import csv
# Set the name of the csv file containing the list of dormant users to remove
# This file is generated by the Github Enterprise report "Dormant Users"
dormant_users_csv_report_filename = "export-github-enterprise-1679693671.csv"
# Set the name of the Github enterprise slug
enterprise_slug = "github-enterprise"
# Set up the GraphQL API endpoint
api_url = "https://api.github.com/graphql"
# Set up the GraphQL queries
query_enterprise_id = """
query ($enterprise: String!) {
enterprise(slug: $enterprise) {
id
}
}
"""
query_user_id = """
query ($login: String!) {
user(login: $login) {
id
}
}
"""
query_remove_user = """
mutation ($enterpriseId: ID!, $userId: ID!) {
removeEnterpriseMember(input: {enterpriseId: $enterpriseId, userId: $userId}) {
clientMutationId
}
}
"""
# Set up the authorization header with a bearer token
access_token = os.environ["GITHUB_TOKEN"]
headers = {
"Authorization": f"Bearer {access_token}",
"X-Github-Next-Global-ID": "1"
}
# Get the enterprise ID
response = requests.post(
api_url,
json = {
"query": query_enterprise_id,
"variables": { "enterprise": enterprise_slug }
},
headers = headers
)
if response.status_code != 200:
print(f"Failed to get the enterprise ID")
print(f"Response status code: {response.status_code}")
print(f"Response message: {response.text}")
exit(1)
enterprise_id = response.json()["data"]["enterprise"]["id"]
# Open the csv file and loop through the usernames to delete each one
with open(dormant_users_csv_report_filename, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
username = row["login"]
if username:
# Get the user ID
response = requests.post(
api_url,
json = {
"query": query_user_id,
"variables": { "login": username }
},
headers = headers
)
if response.status_code != 200:
print(f"Failed to get the user ID for user '{username}'")
print(f"Response status code: {response.status_code}")
print(f"Response message: {response.text}")
continue
user_id = response.json()["data"]["user"]["id"]
print(f"Deleting user '{username}")
# Make the API request to remove the user from the enterprise
response = requests.post(
api_url,
json = {
"query": query_remove_user,
"variables": { "enterpriseId": enterprise_id, "userId": user_id }
},
headers = headers
)
if response.status_code != 200:
print(f"Failed to remove the user '{username}' from the enterprise")
print(f"Response status code: {response.status_code}")
print(f"Response message: {response.text}")
continue
if response.json().get("errors"):
print("Response contains an error: " + str(response.json()["errors"]))