Skip to content

Instantly share code, notes, and snippets.

@merlos
Last active September 18, 2023 11:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save merlos/e2157b644f9b956093d439c352bddcd1 to your computer and use it in GitHub Desktop.
Save merlos/e2157b644f9b956093d439c352bddcd1 to your computer and use it in GitHub Desktop.
Python script to get all the AD groups from Graph API
import requests
import csv
import msal
import os
"""
Script to save into a file csv file the name of the groups of Active Directory
It requires 3 environment variables
CLIENT_ID
SECRET_ID
TENANT_ID
CLIENT_ID and SECRET_ID are obtained from creating an App Registration
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-app-registration
MSAL (Microsoft Authentication Library) needs to be installed
pip install msal
"""
# Load the variables from environment variables
client_id = os.environ.get("CLIENT_ID")
client_secret = os.environ.get("CLIENT_SECRET")
tenant_id = os.environ.get("TENANT_ID")
# Check if any of the variables is not set or is empty
if not client_id or not client_secret or not tenant_id:
print("Error: The following environment variables are required and must be set:")
if not client_id:
print(" - CLIENT_ID")
if not client_secret:
print(" - CLIENT_SECRET")
if not tenant_id:
print(" - TENANT_ID")
print("\nTo set these variables, you can use the following commands in your terminal:")
print("export CLIENT_ID=YourClientId")
print("export CLIENT_SECRET=YourClientSecret")
print("export TENANT_ID=YourTenantId")
else:
# Variables are set, you can proceed with your code
print("Environment variables loaded successfully:")
scope = ["https://graph.microsoft.com/.default"]
# Create a preferably long-lived app instance that maintains a token cache.
app = msal.ConfidentialClientApplication(
client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
client_credential=client_secret
# token_cache=... # Default cache is in memory only.
# You can learn how to use SerializableTokenCache from
# https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache
)
result = None
# We now check the cache to see
# whether we already have some accounts that the end user already used to sign in before.
accounts = app.get_accounts()
if accounts:
# If so, you could then somehow display these accounts and let end user choose
print("Pick the account you want to use to proceed:")
for a in accounts:
print(a["username"])
# Assuming the end user chose this one
chosen = accounts[0]
# Now let's try to find a token in cache for this account
result = app.acquire_token_silent(scope, account=chosen)
else:
# First, the code looks up a token from the cache.
# Because we're looking for a token for the current app, not for a user,
# use None for the account parameter.
result = app.acquire_token_silent(scope, account=None)
if not result:
print("No suitable token exists in cache. Let's get a new one from Azure AD.")
result = app.acquire_token_for_client(scopes=scope)
if "access_token" in result:
# Call a protected API with the access token.
print("Token type" + result["token_type"])
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You might need this when reporting a bug.
# Create a headers dictionary with the access token
headers = {
"Authorization": "Bearer " + result["access_token"]
}
# Create a list to store all the groups
all_groups = []
current_page = 1
# Define your initial URL
graph_url = "https://graph.microsoft.com/v1.0/groups?$select=displayName"
while graph_url:
# Make a GET request with the headers including the access token
response = requests.get(graph_url, headers=headers)
# Check if the request was successful
if response.status_code == 200:
print (f"Received response 200 for page {current_page}")
groups_data = response.json()
groups = groups_data.get("value", [])
print(groups_data)
all_groups.extend(groups)
print (f" all_groups length={len(all_groups)}")
# Check if there's a next link for more pages
next_link = groups_data.get("@odata.nextLink")
print (f" next_link={next_link}")
if next_link:
graph_url = next_link
current_page= current_page+1
else:
# No more pages, exit the loop
graph_url = None
else:
print(f"Failed to retrieve groups. Status code: {response.status_code}")
print(response.text)
break
# Save all_groups to a CSV file
csv_file_path = "groups.csv"
with open(csv_file_path, mode="w", newline="", encoding="utf-8") as csv_file:
csv_writer = csv.writer(csv_file)
csv_writer.writerow(["Group Name"])
for group in all_groups:
csv_writer.writerow([group["displayName"]])
print(f"Groups have been saved to {csv_file_path}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment