Подготовка сцены: я пытаюсь добавить участников в группу MS Entra, вызывая API MS Graph из приложения Python, размещенного в экземпляре приложения функций Azure. Приложение запускается от имени имени участника-службы, которое проходит проверку подлинности с помощью сертификата клиента и имеет разрешение User.Read.All API MS Graph. Кроме того, SPN является владельцем MS Entra Group. Сначала приложение получает идентификаторы объектов пользователя (I), а затем добавляет их в группу MS Entra в качестве участников (II).
Проблема: если я запускаю логику Python на своем локальном компьютере от имени SPN, участники добавляются в мою группу MS Entra. Если я запущу логику в приложении Azure Fuction, приложение сможет получить идентификаторы объектов пользователей, но не сможет добавить участников. API MS Graph возвращает: Error: 403, {"code":"Authorization_RequestDenied","message":"Insufficient privileges to complete the operation."
.
Переменные среды для DefaultAzureCredential
$env:AZURE_CLIENT_CERTIFICATE_PATH = ".\cert.pem"
$env:AZURE_CLIENT_ID = "00000000-0000-0000-0000-000000000000"
$env:AZURE_TENANT_ID = "11111111-1111-1111-1111-111111111111"
Python-скрипт
import requests, re
from azure.identity import DefaultAzureCredential
class GraphClient:
def __init__(self, az_credential):
self._graph_token = az_credential.get_token("https://graph.microsoft.com/.default").token
def get_members_object_ids(self, members):
url = "https://graph.microsoft.com/v1.0/users"
headers = {
"Authorization": f"Bearer {self._graph_token}",
"Content-Type": "application/json"
}
members = re.findall(r'\((.*?)\)', members)
members_ids = []
for member in members:
query_params = {
"$filter": f"mailNickname eq '{member}'"
}
try:
response = requests.get(url=url, headers=headers, params=query_params)
response.raise_for_status()
members_ids.append(response.json()['value'][0]['id'])
except Exception as e:
raise e
return members_ids
def add_members_to_group(self, group_id, member_ids: list):
url = f"https://graph.microsoft.com/v1.0/groups/{group_id}/members/$ref"
headers = {
"Authorization": f"Bearer {self._graph_token}",
"Content-Type": "application/json"
}
for member_id in member_ids:
data = {
"@odata.id": f"https://graph.microsoft.com/v1.0/users/{member_id}"
}
try:
response = requests.post(url=url, json=data, headers=headers)
response.raise_for_status()
print(f"Member with id {member_id} added to group with id {group_id}")
except Exception as e:
print(f"{response.status_code}: {response.text}")
raise e
members = "Tiger Woods (AAAA), Taylor Swift (BBBB)"
adgr_id = "22222222-2222-2222-2222-222222222222"
az_credential = DefaultAzureCredential()
graph_client = GraphClient(az_credential)
members_ids = graph_client.get_members_object_ids(members)
graph_client.add_members_to_group(adgr_id, members_ids)
Вывод выполняется локально Если я запускаю сценарий локально (от имени SPN), все работает как положено, и я получаю следующий вывод:
Вывод, выполняемый в функциях Azure Если я запускаю логику в приложении «Функции Azure» в облаке Azure, я могу получить идентификаторы объектов пользователей, но получаю следующий вывод ошибки при попытке добавить членов группы MS Entra:
2024-08-28T13:50:04Z [Information] CertificateCredential.get_token succeeded
2024-08-28T13:50:04Z [Information] EnvironmentCredential.get_token succeeded
2024-08-28T13:50:04Z [Information] DefaultAzureCredential acquired a token from EnvironmentCredential
2024-08-28T13:50:05Z [Error] 403: {"error":{"code":"Authorization_RequestDenied","message":"Insufficient privileges to complete the operation.","innerError":{"date":"2024-08-28T13:50:04","request-id":"603ee6e0-07b1-44b4-ad82-43b4709aa761","client-request-id":"603ee6e0-07b1-44b4-ad82-43b4709aa761"}}}
2024-08-28T13:50:05Z [Error] Executed 'Functions.create_ad_group' (Failed, Id=25c267f3-49df-4d78-b826-b4de42311f83, Duration=8280ms)
Токены
Я расшифровал токены, и оба имеют следующую пару ключ-значение roles
:
"roles": [
"User.Read.All"
],
Есть ли способ решить эту проблему без предоставления более широких разрешений API?
@GauravMantri Разрешения API MS Graph: User.Read.All — приложение, User.Read — делегировано. SPN является владельцем группы MS Entra, поэтому разрешений должно быть достаточно.
Я бы рекомендовал декодировать токен доступа и проверить там роли/разрешения. Я считаю, что где-то разрешения неверны, и поэтому вы получаете ошибку 403.
@GauravMantri, я декодировал JWT как локальную, так и Azure-функцию, и разница только в iat, nbf, exp, aio, uti и xms_idrel. Эти атрибуты не представляют разрешения.
Не могли бы вы подтвердить, содержит ли декодированный токен утверждение roles
или scp
? Если да, укажите его значения и то, как вы передали сведения о сертификате в приложении-функции Azure?
@Sridevi, декодированные токены имеют roles
, а значения содержат только роль User.Read.All, как и ожидалось. Однако ему не потребуются дополнительные разрешения API, поскольку SPN является владельцем MS Entra Group.
Как вы добавили сертификат в приложение-функцию Azure?
Сертификат извлекается из хранилища ключей и записывается в файловую систему, а путь определяется в переменных среды. Эта логика работает нормально, иначе я не смог бы запросить идентификаторы объектов пользователей, вызвав API MS Graph.
Как минимум, чтобы добавить участников в группу, вам понадобится GroupMember.ReadWrite.All. Все, как описано в документации здесь — Learn.microsoft.com/en-us/graph/api/… Можете ли вы поделиться идентификатором корреляции и временная метка успешного звонка? Несмотря на то, что SPN является владельцем группы, это не обязательно означает, что у него есть все необходимые разрешения для управления группой.
Привет @bakkie103 Есть ли у вас дополнительная функция с именем create_ad_group
для создания группы Azure AD, в которой возникает ошибка из этого образа i.sstatic.net/H0J7dLOy.png?
Привет @CarolM, если принципал является владельцем группы MS Entra, вам не нужны дополнительные разрешения API для добавления участников в группу, поскольку они уже предоставлены как часть набора разрешений владельца.
В процессе устранения неполадок я тестировал использование локально сгенерированного токена в приложении-функции Azure, который успешно добавлял участников в группу. Впоследствии я использовал токен, созданный приложением-функцией Azure, в своей локальной среде, и это также привело к успешному добавлению участников. Это подтвердило, что оба токена функционируют правильно.
Чтобы решить эту проблему, я ввел в свою логику Python задержку между созданием группы MS Entra и добавлением участников. Это решило проблему, заставив меня заподозрить, что группа MS Entra не была полностью подготовлена, когда я первоначально пытался вызвать API MS Graph для добавления участников.
Какие разрешения API Graph назначены субъекту службы? Это приложения или делегированные разрешения.