How to script user removal from Github enterprise

587 Views Asked by At

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?

1

There are 1 best solutions below

1
Maxime Lem On BEST ANSWER

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"]))