Какое‑то время назад я писал в песочницу про тикет «добавьте меня в групповой чат», который админка Microsoft 365 решить не может, и про утилиту поверх Microsoft Graph, которая из этого выросла. С тех пор вышла версия 2.0, и это уже другая программа: внутри неё не Graph‑клиент, а движок, который любое изменение сначала показывает, а потом делает — неважно, идёт оно через Graph, Exchange PowerShell или LDAP.

Ниже — не обзор кнопок, а то, обо что пришлось споткнуться. Большая часть этих граблей лежит в самом Microsoft 365, и вы на них наступите с любым инструментом.

Почему «предпросмотр», а не просто кнопки

В админке Microsoft 365 и в Graph нет режима «что будет, если». Вы делаете PATCH, получаете 204 No Content и узнаёте, что поменялось, только сравнив руками «до» и «после». У Exchange‑командлетов есть -WhatIf, но не у всех, и вывод у него свободным текстом.

Поэтому в 2.0 каждое действие разбито на два шага:

1. План. Действие читает текущее состояние и возвращает список изменений: объект, поле, было → станет. Ничего не пишет.

2. Применение. Выполняет ровно этот список. Для разрушительных действий нужно набрать имя цели руками. Всё уходит в журнал запусков и аудит‑лог.

Если между планом и применением что‑то поменялось, это видно в журнале, а не в сюрпризе. Если цель уже в нужном состоянии, строка помечается «уже так» и пропускается — повторный запуск безопасен.

На скриншоте — сценарий «взломанная учётка»: блокировка входа, отзыв сессий, сброс пароля и MFA, отключение подозрительных правил входящих, снятие пересылки. Шесть шагов, один предпросмотр, одно подтверждение:

Один и тот же движок работает из GUI и из командной строки:

SwissKnifeGraph action mailbox.fullAccess mailbox=shared@contoso.com user=alice@contoso.com          # план

SwissKnifeGraph action mailbox.fullAccess mailbox=shared@contoso.com user=alice@contoso.com --apply  # применить

Грабли № 1: Graph умеет не всё, а Exchange PowerShell — не везде

Половина ежедневных задач Exchange (полный доступ к ящику, Send As, перевод в общий ящик, правила транспорта) в Graph отсутствует. Варианты:

— Exchange Admin API (/adminapi/v2.0). Это REST‑обёртка над избранными командлетами Exchange — работает с обычным токеном, без PowerShell. Но она в превью и покрывает буквально несколько эндпоинтов.

— Exchange Online PowerShell. Умеет всё, но требует PowerShell 7 и модуль.

Каждое действие в программе описано один раз, а реализаций у него может быть несколько. Например, «права на календарь» сначала пробует Admin API, а если его нет в тенанте или эндпоинт ответил не так, как ожидалось, — PowerShell. Какая реализация сработает сейчас и почему не сработали остальные, видно на отдельной странице:

А теперь грабли. Если вы на Mac или Linux:

— модуль ExchangeOnlineManagement последних версий требует PowerShell 7.6;

— Connect-IPPSSession (Security & Compliance) на macOS и Linux не работает вообще.

Решение — воркер: на любой Windows‑машине запускается та же программа в режиме worker serve --pair, ноутбук сопрягается с ней по одноразовому коду, дальше они общаются по взаимному TLS 1.3 с закреплёнными отпечатками сертификатов. Воркер исполняет только командлеты из белого списка и не хранит токены: они приходят с каждым вызовом, живут в памяти и стираются через 15 минут простоя. Для Exchange воркеру вообще не передаётся токен Graph — только токен Exchange.

Грабли № 2: откатить конфигурацию сложнее, чем сохранить

Снимок конфигурации сделать легко: прочитали Conditional Access, Intune, настройки Exchange и Teams, сохранили JSON. Сравнить два снимка — тоже. А вот вернуть тенант к снимку оказалось набором ловушек:

— Именованные расположения меняют id. Политика Conditional Access ссылается на location по id. Если расположение удалили и создали заново, id новый, и политика из снимка указывает в пустоту. Приходится переназначать ссылки по имени — причём в режиме «откатить всё» расположение создаётся в этом же прогоне, и новый id становится известен только при применении.

— Intune возвращает секреты как null или маску. Ключ Wi‑Fi приходит null, ключ продукта при обновлении редакции — маской. Наивный PATCH из снимка перезапишет настоящий ключ маской. Поэтому null не пишется никогда, маскированные поля пропускаются, а профили с зашифрованными OMA‑URI программа честно отказывается восстанавливать.

— Часть полей Intune меняет сам Intune. Даты окончания паузы и отката обновлений в кольцах Windows Update — если их сравнивать, вы получите вечный ложный дрейф.

— Политику соответствия нельзя создать без действия при несоответствии (scheduledActionsForRule), а в обычном списке политик его нет. Его надо читать отдельным запросом и класть в снимок, иначе пересозданная политика получит действие по умолчанию — «несоответствие сразу», и CA‑политики с требованием соответствующего устройства начнут блокировать людей.

— Правило транспорта по снимку не пересоздать: в выводе Get-TransportRule слишком мало информации об условиях. Включить, выключить, переименовать — можно; удалённое правило программа помечает «восстановите руками» и не делает вид, что справилась.

— Graph обрезает большие коллекции. Если в тенанте больше 5000 групп, а снимок ограничен, два снимка будут содержать разные подмножества, и сравнение покажет фантомные изменения. Обрезанный раздел нужно помечать и исключать из сравнения.

Грабли № 3: чужие скрипты с вашим админским токеном

В 2.0 появились пакеты действий и каталог (Action Hub): готовые сценарии вроде «фишинговая рассылка», «новый сотрудник», «доступ ассистенту», отчёты аудита Exchange.

Пакеты бывают двух видов:

— Сценарии — цепочки встроенных действий, вообще без кода. Самый безопасный вариант: каждый шаг проходит те же проверки, что и обычное действие.

— Скрипты PowerShell — для того, чего во встроенных действиях нет.

Скрипт сообщества, запущенный в сессии Exchange с вашим токеном, — это ровно то, чего не хочется. Что сделано:

1. Пакет объявляет, какие командлеты вызывает его скрипт. Перед запуском хост разбирает скрипт парсером PowerShell (AST) и отказывает, если там есть необъявленная команда, имя команды не литералом (& $x), dot‑source, -AsJob/-Parallel или обращение к $ExecutionContext.

2. Сам скрипт исполняется в ConstrainedLanguage: никаких.NET‑вызовов, Add-Type и динамического кода. Командлеты модулей при этом работают как обычно.

3. Команды, которые ходят в сеть или на диск, объявить можно, но перед одобрением пакета они подсвечиваются.

4. Пакеты из каталога подписаны отдельным ключом проекта (minisign), остальные запускаются только после того, как вы одобрили конкретный дайджест файлов.

Честно: это не JEA и не изоляция уровня ОС. Это разумный барьер для «пакет делает не то, что обещал». Ещё грабли: если в организации включено Module Logging, токен из Connect-ExchangeOnline -AccessToken попадёт в журнал событий PowerShell — это поведение самого командлета, обойти его нельзя, можно только знать.

Остальное, что экономит время

— Ctrl+K: описываете задачу словами (по‑русски или по‑английски) и попадаете на форму, которая её делает.

— Зеркало доступа: сравнить двух сотрудников по группам, командам, приватным каналам, ролям и лицензиям и докинуть недостающее.

— «Почему человек не может войти»: неудачные входы одного пользователя с кодом ошибки Entra по каждому.

— Профили с ограничениями: только чтение или только для участников выбранных групп — для дежурных и стажёров. Проверяется в бэкенде, а не прячется кнопками.

— Локальный Active Directory по LDAPS рядом с Microsoft 365: разблокировать, сбросить пароль, группы.

Чего нет

— Подписи кода. Под Windows SmartScreen предупредит при первом запуске, под macOS нужно снять карантин. Контрольные суммы подписаны minisign, встроенное обновление проверяет подпись само.

— Бэкапа чатов Teams без одобрения Microsoft: Chat.Read.All для app‑only одобряется отдельно, admin consent недостаточно.

— Exchange Admin API в превью: если Microsoft его поменяет, действия уйдут на PowerShell.

— Широкого тестирования. Проект одного человека и одного тестового тенанта.

Как попробовать

Windows (установщик или portable), macOS, Linux (deb, rpm, AppImage, AUR) — на странице релизов: https://github.com/Nemu‑x/SwissKnife‑for‑MS‑Graph/releases

Нужна своя регистрация приложения в Entra. В wiki есть таблица: какое разрешение для какой функции. Совет: начните с профиля «только чтение», покликайте, и только потом выдавайте права на запись под конкретные задачи.

Мне важнее всего обратная связь:

— какую задачу вы пришли решить и решилась ли она;

— каждая ошибка 400/403, по тексту которой непонятно, что чинить;

— ради чего вам всё равно пришлось открыть PowerShell.

Issues: https://github.com/Nemu‑x/SwissKnife‑for‑MS‑Graph/issues — читаю все. Лицензия MIT.