Управление доступом в Azure может быстро превратиться в хаос: десятки групп, сотни пользователей, тысячи разрешений. Простая, динамическая визуализация в виде дерева мгновенно показывает структуру, пробелы и потенциальные риски.
В этом посте мы рассмотрим, как построить такую визуализацию прямо внутри .NET Interactive Jupyter Notebooks, используя чистые Vega JSON спецификации — без необходимости писать JavaScript-код.
✨ Почему Vega?
Хотя библиотеки вроде d3.js обеспечивают максимальную гибкость, Vega предоставляет декларативный способ создания мощных, интерактивных графиков с использованием только JSON.
Это делает её идеальной для интеграции в среду с активным скриптингом, например PowerShell-ноутбуки.
📄 Подготовка данных для визуализации
Перед тем как перейти к визуализациям, важно отметить:
Vega требует плоское представление иерархических данных.
Хотя членства в группах Azure или назначение ролей естественно образуют дерево, Vega ожидает представление в формате “id → родитель” в табличной форме.
В противном случае преобразования вроде stratify, tree и treelinks не смогут корректно построить структуру.
Пример ожидаемого формата для Vega:
[
{ "id": "root", "parent": null },
{ "id": "group-A", "parent": "root" },
{ "id": "user-John", "parent": "group-A" },
{ "id": "user-Delenn", "parent": "group-A" }
]
Чтобы преобразовать данные из Azure в этот формат, можно использовать простую рекурсивную функцию PowerShell:
$inputPath = "data/prodScopesAndPermissions.json"
$outputPath = "data/prodScopesAndPermissions_flat.json"
# reding JSON
$tree = Get-Content $inputPath -Raw | ConvertFrom-Json
# init result and ID counter
$flattened = @()
$idCounter = 0
function Flatten-Tree {
param(
[Parameter(Mandatory)] $Node,
[Parameter()] $ParentId,
[ref] $FlatList,
[ref] $IdCounter
)
# increment ID
$IdCounter.Value++
# save current ID
$currentId = $IdCounter.Value
$FlatList.Value += [pscustomobject]@{
id = $currentId
name = $Node.name
parent = $ParentId
}
foreach ($child in $Node.children) {
Flatten-Tree -Node $child -ParentId $currentId -FlatList $FlatList -IdCounter $IdCounter
}
}
# Run
Flatten-Tree -Node $tree -FlatList ([ref]$flattened) -IdCounter ([ref]$idCounter)
# Export results
$flattened | ConvertTo-Json -Depth 5 | Out-File $outputPath -Force
Write-Output "Flattened data saved to $outputPath"
🧹 Представление членства в группах
Первая визуализация показывает, какие пользователи входят в какие группы.
Начиная с Azure Active Directory, мы собираем данные о пользователях и группах с помощью PowerShell, преобразуем их в иерархическую структуру и отображаем с помощью Vega.
Вот как отобразить Группы и пользователей:
Vega-спецификация и её вызов в .NET Interactive Jupyter с PowerShell
@"
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "Groups and Users view.",
"width": 600,
"height": 400,
"padding": 5,
"signals": [
{"name": "labels", "value": true, "bind": {"input": "checkbox"}}
],
"data": [
{
"name": "tree",
"url": "permissionsData/userGroupData.json",
"type": "json",
"transform": [
{"type": "stratify", "key": "id", "parentKey": "parent"},
{
"type": "tree",
"method": "tidy",
"size": [{"signal": "height"}, {"signal": "width - 100"}],
"separation": "false",
"as": ["y", "x", "depth", "children"]
}
]
},
{
"name": "links",
"source": "tree",
"transform": [
{"type": "treelinks"},
{
"type": "linkpath",
"orient": "horizontal",
"shape": "diagonal"
}
]
}
],
"scales": [
{
"name": "color",
"type": "ordinal",
"domain": [0, 1, 2],
"range": ["#5e4fa2", "#3288bd", "#66c2a5"]
}
],
"marks": [
{
"type": "path",
"from": {"data": "links"},
"encode": {
"update": {"path": {"field": "path"}, "stroke": {"value": "#ccc"}}
}
},
{
"type": "symbol",
"from": {"data": "tree"},
"encode": {
"enter": {"size": {"value": 100}, "stroke": {"value": "#fff"}},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"fill": {"scale": "color", "field": "depth"}
}
}
},
{
"type": "text",
"from": {"data": "tree"},
"encode": {
"enter": {
"text": {"field": "name"},
"fontSize": {"value": 9},
"baseline": {"value": "middle"}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"dx": {"signal": "datum.children ? -7 : 7"},
"align": {"signal": "datum.children ? 'right' : 'left'"},
"opacity": {"signal": "labels ? 1 : 0"}
}
}
}
]
}
"@ | Out-Display -MimeType "application/vnd.vega.v5+json"
🛡️ Представление разрешений
Вторая визуализация показывает, какие пользователи, группы и сервисные принципы имеют какие разрешения на ресурсы Azure.
Vega-спецификация и вызов в ноутбуке для визуализации разрешений
@"
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "User and Group Permissions.",
"width": 600,
"height": 1200,
"padding": 5,
"signals": [
{"name": "labels", "value": true, "bind": {"input": "checkbox"}}
],
"data": [
{
"name": "tree",
"url": "permissionsData/prodScopesAndPermissions.json",
"type": "json",
"transform": [
{"type": "stratify", "key": "id", "parentKey": "parent"},
{
"type": "tree",
"method": "tidy",
"size": [{"signal": "height"}, {"signal": "width - 100"}],
"separation": "false",
"as": ["y", "x", "depth", "children"]
}
]
},
{
"name": "links",
"source": "tree",
"transform": [
{"type": "treelinks"},
{
"type": "linkpath",
"orient": "horizontal",
"shape": "diagonal"
}
]
}
],
"scales": [
{
"name": "color",
"type": "ordinal",
"domain": [0, 1, 2],
"range": ["#5e4fa2", "#3288bd", "#66c2a5"]
}
],
"marks": [
{
"type": "path",
"from": {"data": "links"},
"encode": {
"update": {"path": {"field": "path"}, "stroke": {"value": "#ccc"}}
}
},
{
"type": "symbol",
"from": {"data": "tree"},
"encode": {
"enter": {"size": {"value": 100}, "stroke": {"value": "#fff"}},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"fill": {"scale": "color", "field": "depth"}
}
}
},
{
"type": "text",
"from": {"data": "tree"},
"encode": {
"enter": {
"text": {"field": "name"},
"fontSize": {"value": 9},
"baseline": {"value": "middle"}
},
"update": {
"x": {"field": "x"},
"y": {"field": "y"},
"dx": {"signal": "datum.children ? -7 : 7"},
"align": {"signal": "datum.children ? 'right' : 'left'"},
"opacity": {"signal": "labels ? 1 : 0"}
}
}
}
]
}
"@ | Out-Display -MimeType "application/vnd.vega.v5+json"
🚀 Результаты
Имея всего два JSON-файла и немного магии Observable или .NET Interactive, вы можете мгновенно визуализировать сложные модели доступа Azure — и наконец-то увидеть, как связаны ваши пользователи, группы, сервисные принципы и назначения ролей.
📋 Что дальше?
Вы можете расширить этот подход, чтобы:
- Обнаружить избыточные разрешения
- Найти осиротевшие группы и сервисные принципы
- Отслеживать изменения разрешений со временем
- Создавать обзоры безопасности на основе ролей
В целом, если вы можете извлечь данные — вы можете их визуализировать.
Как и обычно с ноутбуками, вы можете открыть их в GitHub Codespace и протестировать:
Удачи! 🚀