Microsoft Graph API: An Overview
What is Microsoft Graph API?
My Recent Challenge: Automating MFA Management
Automating MFA Unlinking with Python and Microsoft Graph API
# mfa_manager.py
import requests
import msal
import json
import logging
from datetime import datetime
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class EntraMFAManager:
def __init__(self, tenant_id, client_id, client_secret):
self.tenant_id = tenant_id
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
self.graph_url = "https://graph.microsoft.com/v1.0"
def authenticate(self):
"""Get access token from Microsoft Entra ID"""
try:
# Create MSAL app for authentication
authority = f"https://login.microsoftonline.com/{self.tenant_id}"
app = msal.ConfidentialClientApplication(
client_id=self.client_id,
client_credential=self.client_secret,
authority=authority
)
# Acquire token for Graph API
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
if "access_token" in result:
self.access_token = result["access_token"]
logger.info("Authentication successful")
return True
else:
logger.error(f"Authentication failed: {result.get('error_description', 'Unknown error')}")
return False
except Exception as e:
logger.error(f"Authentication exception: {str(e)}")
return False
def get_user_mfa_methods(self, user_principal_name):
"""Get a user's MFA methods"""
if not self.access_token:
if not self.authenticate():
return None
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
# Get user's authentication methods
url = f"{self.graph_url}/users/{user_principal_name}/authentication/methods"
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
methods = response.json().get("value", [])
logger.info(f"Found {len(methods)} authentication methods for user {user_principal_name}")
return methods
except requests.exceptions.RequestException as e:
logger.error(f"Error getting MFA methods: {str(e)}")
if hasattr(e.response, 'text'):
logger.error(f"Response: {e.response.text}")
return None
def remove_mfa_method(self, user_principal_name, method_id):
"""Remove a specific MFA method from a user"""
if not self.access_token:
if not self.authenticate():
return False
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
url = f"{self.graph_url}/users/{user_principal_name}/authentication/methods/{method_id}"
try:
response = requests.delete(url, headers=headers)
response.raise_for_status()
logger.info(f"Successfully removed authentication method {method_id} for user {user_principal_name}")
return True
except requests.exceptions.RequestException as e:
logger.error(f"Error removing MFA method: {str(e)}")
if hasattr(e.response, 'text'):
logger.error(f"Response: {e.response.text}")
return False
# Example usage
if __name__ == "__main__":
# Replace with your Entra ID (Azure AD) app registration details
TENANT_ID = "your-tenant-id"
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"
# Initialize the MFA manager
mfa_manager = EntraMFAManager(TENANT_ID, CLIENT_ID, CLIENT_SECRET)
# Example: Process a list of users
users_to_process = ["[email protected]", "[email protected]"]
for user in users_to_process:
print(f"\nProcessing user: {user}")
# Get user's MFA methods
mfa_methods = mfa_manager.get_user_mfa_methods(user)
if not mfa_methods:
print(f"No MFA methods found or error occurred for {user}")
continue
# Print current methods
print(f"Found {len(mfa_methods)} authentication methods:")
for method in mfa_methods:
method_id = method.get("id")
method_type = method.get("@odata.type", "").replace("#microsoft.graph.", "")
print(f"- {method_type} (ID: {method_id})")
# Example: Remove phone methods only
phone_methods = [m for m in mfa_methods if "phone" in m.get("@odata.type", "").lower()]
for method in phone_methods:
method_id = method.get("id")
if method_id:
if mfa_manager.remove_mfa_method(user, method_id):
print(f"β
Successfully removed phone method {method_id}")
else:
print(f"β Failed to remove phone method {method_id}")How This Script Works
Permissions Required
What I've Learned
Conclusion
Last updated