Не удается добавить участников в группу MS Entra через API MS Graph через приложение Python Azure Function

Подготовка сцены: я пытаюсь добавить участников в группу 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?

Какие разрешения API Graph назначены субъекту службы? Это приложения или делегированные разрешения.

Gaurav Mantri 28.08.2024 16:20

@GauravMantri Разрешения API MS Graph: User.Read.All — приложение, User.Read — делегировано. SPN является владельцем группы MS Entra, поэтому разрешений должно быть достаточно.

bakkie103 28.08.2024 16:28

Я бы рекомендовал декодировать токен доступа и проверить там роли/разрешения. Я считаю, что где-то разрешения неверны, и поэтому вы получаете ошибку 403.

Gaurav Mantri 28.08.2024 16:33

@GauravMantri, я декодировал JWT как локальную, так и Azure-функцию, и разница только в iat, nbf, exp, aio, uti и xms_idrel. Эти атрибуты не представляют разрешения.

bakkie103 29.08.2024 10:18

Не могли бы вы подтвердить, содержит ли декодированный токен утверждение roles или scp? Если да, укажите его значения и то, как вы передали сведения о сертификате в приложении-функции Azure?

Sridevi 29.08.2024 15:03

@Sridevi, декодированные токены имеют roles, а значения содержат только роль User.Read.All, как и ожидалось. Однако ему не потребуются дополнительные разрешения API, поскольку SPN является владельцем MS Entra Group.

bakkie103 29.08.2024 15:32

Как вы добавили сертификат в приложение-функцию Azure?

Sridevi 29.08.2024 15:34

Сертификат извлекается из хранилища ключей и записывается в файловую систему, а путь определяется в переменных среды. Эта логика работает нормально, иначе я не смог бы запросить идентификаторы объектов пользователей, вызвав API MS Graph.

bakkie103 29.08.2024 15:46

Как минимум, чтобы добавить участников в группу, вам понадобится GroupMember.ReadWrite.All. Все, как описано в документации здесь — Learn.microsoft.com/en-us/graph/api/… Можете ли вы поделиться идентификатором корреляции и временная метка успешного звонка? Несмотря на то, что SPN является владельцем группы, это не обязательно означает, что у него есть все необходимые разрешения для управления группой.

CarolM 30.08.2024 18:11

Привет @bakkie103 Есть ли у вас дополнительная функция с именем create_ad_group для создания группы Azure AD, в которой возникает ошибка из этого образа i.sstatic.net/H0J7dLOy.png?

Sridevi 03.09.2024 07:53

Привет @CarolM, если принципал является владельцем группы MS Entra, вам не нужны дополнительные разрешения API для добавления участников в группу, поскольку они уже предоставлены как часть набора разрешений владельца.

bakkie103 03.09.2024 09:21
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
Как установить LAMP Stack 1/2 на Azure Linux VM
Как установить LAMP Stack 1/2 на Azure Linux VM
В дополнение к нашему предыдущему сообщению о намерении Azure прекратить поддержку Azure Database для MySQL в качестве единого сервера после 16...
0
11
110
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В процессе устранения неполадок я тестировал использование локально сгенерированного токена в приложении-функции Azure, который успешно добавлял участников в группу. Впоследствии я использовал токен, созданный приложением-функцией Azure, в своей локальной среде, и это также привело к успешному добавлению участников. Это подтвердило, что оба токена функционируют правильно.

Чтобы решить эту проблему, я ввел в свою логику Python задержку между созданием группы MS Entra и добавлением участников. Это решило проблему, заставив меня заподозрить, что группа MS Entra не была полностью подготовлена, когда я первоначально пытался вызвать API MS Graph для добавления участников.

Другие вопросы по теме

Скопируйте большой двоичный объект из одной учетной записи хранения в другую с помощью функции Azure, написанной на Java
Развертывание приложения-функции Python, источник которого находится в подкаталоге
Ошибка «Невозможно определить язык проекта по файлам» при использовании функций Azure в VSCode
У меня возникает ошибка 404 в моей функции Azure
Развертывание функции Azure с помощью действий Github завершается неудачей без указания четкой причины
Получение ошибки «Azure.Storage.Queues: значение не может быть нулевым» в функции Azure с триггером BLOB-объекта
Расширения OpenAPI для Swagger в функции Azure v4 — пользовательский интерфейс Swagger пуст
Ошибка [ERR_MODULE_NOT_FOUND]: работнику не удалось загрузить точку входа «dist/src/functions/trigger.js»
Как я могу зарегистрировать HTTP-ответ с помощью промежуточного программного обеспечения в функциях Azure, выполняющих изолированный процесс .NET 8?
Как отладить zip-развертывание функции Python Azure?