idengraph 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- idengraph-0.1.0/LICENSE +21 -0
- idengraph-0.1.0/MANIFEST.in +18 -0
- idengraph-0.1.0/PKG-INFO +225 -0
- idengraph-0.1.0/README.md +202 -0
- idengraph-0.1.0/idengraph.egg-info/PKG-INFO +225 -0
- idengraph-0.1.0/idengraph.egg-info/SOURCES.txt +52 -0
- idengraph-0.1.0/idengraph.egg-info/dependency_links.txt +1 -0
- idengraph-0.1.0/idengraph.egg-info/entry_points.txt +2 -0
- idengraph-0.1.0/idengraph.egg-info/requires.txt +4 -0
- idengraph-0.1.0/idengraph.egg-info/top_level.txt +2 -0
- idengraph-0.1.0/mcp_server.py +1021 -0
- idengraph-0.1.0/pyproject.toml +42 -0
- idengraph-0.1.0/services/__init__.py +0 -0
- idengraph-0.1.0/services/agent_identities.py +1905 -0
- idengraph-0.1.0/services/app_provenance.py +285 -0
- idengraph-0.1.0/services/azure_auth.py +146 -0
- idengraph-0.1.0/services/azure_graph.py +382 -0
- idengraph-0.1.0/services/azure_management_groups.py +15 -0
- idengraph-0.1.0/services/azure_pim.py +165 -0
- idengraph-0.1.0/services/azure_rbac.py +126 -0
- idengraph-0.1.0/services/azure_resources.py +26 -0
- idengraph-0.1.0/services/azure_role_definitions.py +197 -0
- idengraph-0.1.0/services/azure_roles.py +59 -0
- idengraph-0.1.0/services/blast_radius.py +231 -0
- idengraph-0.1.0/services/capability_executor.py +460 -0
- idengraph-0.1.0/services/capability_normalizer.py +309 -0
- idengraph-0.1.0/services/capability_router.py +466 -0
- idengraph-0.1.0/services/capability_service.py +521 -0
- idengraph-0.1.0/services/data/mock_iam.json +386 -0
- idengraph-0.1.0/services/data/mock_identity.json +52 -0
- idengraph-0.1.0/services/data/mock_resources.json +53 -0
- idengraph-0.1.0/services/docs.py +61 -0
- idengraph-0.1.0/services/effective_access.py +149 -0
- idengraph-0.1.0/services/entra_apps.py +172 -0
- idengraph-0.1.0/services/entra_authentication.py +196 -0
- idengraph-0.1.0/services/entra_conditional_access.py +15 -0
- idengraph-0.1.0/services/entra_groups.py +81 -0
- idengraph-0.1.0/services/entra_licenses.py +174 -0
- idengraph-0.1.0/services/entra_pim.py +665 -0
- idengraph-0.1.0/services/entra_roles.py +99 -0
- idengraph-0.1.0/services/entra_users.py +38 -0
- idengraph-0.1.0/services/entra_workload_identities.py +57 -0
- idengraph-0.1.0/services/graph_capabilities.py +1110 -0
- idengraph-0.1.0/services/iam_assessment.py +1251 -0
- idengraph-0.1.0/services/iam_common.py +150 -0
- idengraph-0.1.0/services/identity_360.py +395 -0
- idengraph-0.1.0/services/identity_access.py +347 -0
- idengraph-0.1.0/services/identity_risk.py +119 -0
- idengraph-0.1.0/services/license_posture.py +175 -0
- idengraph-0.1.0/services/ownership.py +394 -0
- idengraph-0.1.0/services/privilege_timeline.py +328 -0
- idengraph-0.1.0/services/role_risk.py +93 -0
- idengraph-0.1.0/services/toxic_combinations.py +146 -0
- idengraph-0.1.0/setup.cfg +4 -0
idengraph-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Josimar Hedler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
recursive-include services/data *.json
|
|
4
|
+
|
|
5
|
+
# O pacote publicado expõe apenas o servidor MCP. O portal Streamlit e o
|
|
6
|
+
# orquestrador de chat existem no repositório para desenvolvimento e demo,
|
|
7
|
+
# e não fazem parte da superfície distribuída.
|
|
8
|
+
exclude app.py
|
|
9
|
+
exclude agent.py
|
|
10
|
+
exclude launcher.py
|
|
11
|
+
exclude test_smoke.py
|
|
12
|
+
exclude requirements.txt
|
|
13
|
+
exclude Dockerfile
|
|
14
|
+
exclude .env.example
|
|
15
|
+
prune .streamlit
|
|
16
|
+
prune .agents
|
|
17
|
+
prune assets
|
|
18
|
+
prune .vscode
|
idengraph-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: idengraph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Identity & Access intelligence powered by Microsoft Graph and MCP. Read-only auditing for Microsoft Entra ID and Azure RBAC.
|
|
5
|
+
Author-email: josimarh <josimar.hedler@outlook.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/josimarh/idengraph
|
|
8
|
+
Project-URL: Repository, https://github.com/josimarh/idengraph
|
|
9
|
+
Project-URL: Issues, https://github.com/josimarh/idengraph/issues
|
|
10
|
+
Keywords: mcp,azure,entra,identity,security,rbac,audit,iam
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: mcp[cli]<3,>=2
|
|
19
|
+
Requires-Dist: httpx<1,>=0.28
|
|
20
|
+
Requires-Dist: azure-identity<2,>=1.17
|
|
21
|
+
Requires-Dist: python-dotenv<2,>=1.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# IdenGraph
|
|
25
|
+
|
|
26
|
+
**Identity & Access intelligence powered by Microsoft Graph and MCP.**
|
|
27
|
+
|
|
28
|
+
A read-only MCP server that turns natural-language questions into audited answers about **Microsoft Entra ID** and **Azure RBAC** — answered by your own Copilot, inside VS Code.
|
|
29
|
+
|
|
30
|
+
[](https://marketplace.visualstudio.com/items?itemName=josimarh.idengraph)
|
|
31
|
+
[](https://pypi.org/project/idengraph/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+
> **It never writes.** Every create, update, delete, grant, or privilege-activation operation is blocked before routing.
|
|
35
|
+
|
|
36
|
+
## What you can ask
|
|
37
|
+
|
|
38
|
+
- Who has **Owner** on Azure? And who holds **Global Administrator** in Entra?
|
|
39
|
+
- Which applications were **created in my tenant**, and which are Microsoft first-party?
|
|
40
|
+
- Which users have **no MFA**, or rely on weak methods?
|
|
41
|
+
- Which **application secrets** expire in the next 30 days?
|
|
42
|
+
- Which objects have **no owner** assigned?
|
|
43
|
+
- What is the **blast radius** of a given identity?
|
|
44
|
+
- Are we **paying for security features we don't use**?
|
|
45
|
+
|
|
46
|
+
**66 tools** across users, groups, applications, service principals, managed identities, PIM, RBAC, authentication, conditional access, ownership, privilege timeline, licensing posture, and toxic combinations (SoD).
|
|
47
|
+
|
|
48
|
+
## Design principles
|
|
49
|
+
|
|
50
|
+
These three rules define the behavior, and they matter more than the feature list.
|
|
51
|
+
|
|
52
|
+
### Source separation
|
|
53
|
+
|
|
54
|
+
Microsoft Graph answers for identity and directory. Azure APIs answer for RBAC and resources. **Graph is never treated as a source of truth for Azure RBAC.**
|
|
55
|
+
|
|
56
|
+
### No false zero
|
|
57
|
+
|
|
58
|
+
If a permission is missing, the answer is `PERMISSION_DENIED` with `NOT_EVALUATED` coverage — **never `0`**.
|
|
59
|
+
|
|
60
|
+
"I could not evaluate this" and "this does not exist" are different answers. Conflating them in an audit is worse than not answering at all, because a false zero looks like a clean result.
|
|
61
|
+
|
|
62
|
+
This applies to licensing too: zero conditional access policies means something entirely different when the feature isn't licensed versus when it's licensed and unused.
|
|
63
|
+
|
|
64
|
+
### Read-only by construction
|
|
65
|
+
|
|
66
|
+
Only `GET`, `LIST`, `QUERY`, `ASSESS`, and `CORRELATE`. There is no LLM-generated KQL and no free-form endpoint: every query goes through a capability registry with an allowlist and validation of filters and scopes.
|
|
67
|
+
|
|
68
|
+
## Architecture
|
|
69
|
+
|
|
70
|
+
```mermaid
|
|
71
|
+
flowchart TB
|
|
72
|
+
User(["You"]) -->|natural language| Copilot["Copilot Chat<br/><i>your own model</i>"]
|
|
73
|
+
Copilot <-->|MCP / stdio| Server
|
|
74
|
+
|
|
75
|
+
subgraph Server["IdenGraph MCP Server · 66 read-only tools"]
|
|
76
|
+
direction TB
|
|
77
|
+
Router["Capability router<br/><i>intent → capability</i>"]
|
|
78
|
+
Guard{{"Write guard<br/><i>blocks mutations</i>"}}
|
|
79
|
+
Registry[("Capability registry<br/>43 capabilities · 29 domains")]
|
|
80
|
+
Executor["Read-only executor<br/><i>allowlist + validation</i>"]
|
|
81
|
+
|
|
82
|
+
Router --> Guard --> Registry --> Executor
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Executor -->|identity & directory| Graph["Microsoft Graph"]
|
|
86
|
+
Executor -->|RBAC & resources| Azure["Azure Management<br/>+ Resource Graph"]
|
|
87
|
+
|
|
88
|
+
Graph --> Normalizer["Normalizer & correlation<br/><i>preserves evidence and coverage</i>"]
|
|
89
|
+
Azure --> Normalizer
|
|
90
|
+
Normalizer -->|answer + provenance| Copilot
|
|
91
|
+
|
|
92
|
+
style Guard fill:#c62828,color:#fff
|
|
93
|
+
style Server fill:#0B1F3A,color:#fff
|
|
94
|
+
style Normalizer fill:#1565c0,color:#fff
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Two details worth highlighting:
|
|
98
|
+
|
|
99
|
+
**The write guard sits before routing**, not after. A mutation request is rejected before it can be interpreted as a query.
|
|
100
|
+
|
|
101
|
+
**The normalizer preserves coverage**, not just data. Every answer carries where it came from and whether the source could actually be evaluated — which is what makes the no-false-zero rule enforceable rather than aspirational.
|
|
102
|
+
|
|
103
|
+
### How the pieces are distributed
|
|
104
|
+
|
|
105
|
+
| Layer | Artifact | Role |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| Discovery | [VS Code extension](https://marketplace.visualstudio.com/items?itemName=josimarh.idengraph) | One-click install, prerequisite checks, settings UI |
|
|
108
|
+
| Engine | [`idengraph` on PyPI](https://pypi.org/project/idengraph/) | The MCP server itself |
|
|
109
|
+
| Model | Your Copilot subscription | No LLM cost to this project or to you |
|
|
110
|
+
|
|
111
|
+
The extension does not replace the Python package — it registers it. The engine runs the same way whether launched by the extension or configured by hand.
|
|
112
|
+
|
|
113
|
+
## Install
|
|
114
|
+
|
|
115
|
+
### Recommended: VS Code extension
|
|
116
|
+
|
|
117
|
+
Install **[IdenGraph](https://marketplace.visualstudio.com/items?itemName=josimarh.idengraph)** from the Marketplace, then sign in to Azure:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
az login
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Open Copilot Chat in **agent mode** and ask. The extension verifies prerequisites and guides you if anything is missing.
|
|
124
|
+
|
|
125
|
+
### Alternative: manual MCP configuration
|
|
126
|
+
|
|
127
|
+
Create `.vscode/mcp.json`:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"servers": {
|
|
132
|
+
"idengraph": {
|
|
133
|
+
"type": "stdio",
|
|
134
|
+
"command": "uvx",
|
|
135
|
+
"args": ["idengraph"],
|
|
136
|
+
"env": { "MOCK_MODE": "false" }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Prerequisites
|
|
143
|
+
|
|
144
|
+
- **Python 3.10+**
|
|
145
|
+
- **[uv](https://docs.astral.sh/uv/getting-started/installation/)**
|
|
146
|
+
- **[Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli)** with an active session (`az login`)
|
|
147
|
+
|
|
148
|
+
Authentication uses `DefaultAzureCredential`, which reuses your Azure CLI session. There is no API key to manage, and no credential is stored by this project.
|
|
149
|
+
|
|
150
|
+
## Configuration
|
|
151
|
+
|
|
152
|
+
| Setting | Env var | Default | Purpose |
|
|
153
|
+
|---|---|---|---|
|
|
154
|
+
| `idengraph.useMockData` | `MOCK_MODE` | `false` (extension) | Query fictional data instead of your tenant |
|
|
155
|
+
| `idengraph.sanitizeOutput` | `SANITIZE_FOR_LLM` | `true` | Mask resource names, subscription IDs, and IPs before they reach the model |
|
|
156
|
+
| `idengraph.subscriptions` | `AZURE_SUBSCRIPTIONS` | all accessible | Restrict queries to specific subscriptions |
|
|
157
|
+
|
|
158
|
+
The Python package defaults to `MOCK_MODE=true` so that nothing touches a real tenant without explicit intent. The extension sets it to `false`, since installing it is already that intent.
|
|
159
|
+
|
|
160
|
+
## Permissions
|
|
161
|
+
|
|
162
|
+
On Azure: **`Reader`** on the subscriptions you want to audit.
|
|
163
|
+
|
|
164
|
+
On Microsoft Graph, delegated permissions vary by question:
|
|
165
|
+
|
|
166
|
+
| Area | Permission |
|
|
167
|
+
|---|---|
|
|
168
|
+
| Users, groups, applications, service principals | `Directory.Read.All` |
|
|
169
|
+
| Directory roles and directory PIM | `RoleManagement.Read.All` |
|
|
170
|
+
| Authentication methods and MFA | `UserAuthenticationMethod.Read.All` |
|
|
171
|
+
| Conditional access | `Policy.Read.All` |
|
|
172
|
+
| License posture | `Organization.Read.All` |
|
|
173
|
+
|
|
174
|
+
Missing a permission only marks the matching area as not evaluated. Everything else keeps working — and the affected area reports *why* it could not be evaluated.
|
|
175
|
+
|
|
176
|
+
## Privacy
|
|
177
|
+
|
|
178
|
+
Queried data belongs to **your** tenant and travels between your machine, Microsoft APIs, and the Copilot model **you already use**. This project sends nothing to third-party servers and collects no telemetry.
|
|
179
|
+
|
|
180
|
+
By default, `SANITIZE_FOR_LLM=true` masks resource names, resource groups, subscription IDs, and IP addresses before content reaches the model.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
python -m venv .venv
|
|
186
|
+
.venv\Scripts\activate # Windows
|
|
187
|
+
# source .venv/bin/activate # Linux/macOS
|
|
188
|
+
pip install -r requirements.txt
|
|
189
|
+
cp .env.example .env
|
|
190
|
+
python test_smoke.py
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The smoke test runs in mock mode and never touches a tenant.
|
|
194
|
+
|
|
195
|
+
### Repository layout
|
|
196
|
+
|
|
197
|
+
```text
|
|
198
|
+
mcp_server.py MCP server and tool registration
|
|
199
|
+
services/azure_auth.py credentials, tokens, caching
|
|
200
|
+
services/graph_capabilities.py capability registry
|
|
201
|
+
services/capability_router.py natural language → capability
|
|
202
|
+
services/capability_executor.py validated read-only execution
|
|
203
|
+
services/azure_role_definitions.py authoritative Azure role name resolution
|
|
204
|
+
services/azure_pim.py resource PIM with confirmed coverage
|
|
205
|
+
services/entra_licenses.py tenant licensing and feature availability
|
|
206
|
+
services/data/ mock data used when MOCK_MODE=true
|
|
207
|
+
extension/ VS Code extension (TypeScript)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The repository also contains a Streamlit portal (`app.py` + `agent.py`) used for development and demos. It is not part of the published package, whose surface is the MCP server only:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
streamlit run app.py
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Known limitations
|
|
217
|
+
|
|
218
|
+
- Agent Identity detection is **heuristic** where the directory exposes no dedicated type. Results are labeled as such, never presented as fact.
|
|
219
|
+
- `Public IP` indicates a public address, which **does not prove** workload exposure.
|
|
220
|
+
- Without `RoleManagement.Read.All`, directory PIM reports as not evaluated rather than empty.
|
|
221
|
+
- License posture currently cross-references PIM and Conditional Access. Capabilities still marked `not_integrated` in the registry are not probed, and deliberately return no verdict.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# IdenGraph
|
|
2
|
+
|
|
3
|
+
**Identity & Access intelligence powered by Microsoft Graph and MCP.**
|
|
4
|
+
|
|
5
|
+
A read-only MCP server that turns natural-language questions into audited answers about **Microsoft Entra ID** and **Azure RBAC** — answered by your own Copilot, inside VS Code.
|
|
6
|
+
|
|
7
|
+
[](https://marketplace.visualstudio.com/items?itemName=josimarh.idengraph)
|
|
8
|
+
[](https://pypi.org/project/idengraph/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
> **It never writes.** Every create, update, delete, grant, or privilege-activation operation is blocked before routing.
|
|
12
|
+
|
|
13
|
+
## What you can ask
|
|
14
|
+
|
|
15
|
+
- Who has **Owner** on Azure? And who holds **Global Administrator** in Entra?
|
|
16
|
+
- Which applications were **created in my tenant**, and which are Microsoft first-party?
|
|
17
|
+
- Which users have **no MFA**, or rely on weak methods?
|
|
18
|
+
- Which **application secrets** expire in the next 30 days?
|
|
19
|
+
- Which objects have **no owner** assigned?
|
|
20
|
+
- What is the **blast radius** of a given identity?
|
|
21
|
+
- Are we **paying for security features we don't use**?
|
|
22
|
+
|
|
23
|
+
**66 tools** across users, groups, applications, service principals, managed identities, PIM, RBAC, authentication, conditional access, ownership, privilege timeline, licensing posture, and toxic combinations (SoD).
|
|
24
|
+
|
|
25
|
+
## Design principles
|
|
26
|
+
|
|
27
|
+
These three rules define the behavior, and they matter more than the feature list.
|
|
28
|
+
|
|
29
|
+
### Source separation
|
|
30
|
+
|
|
31
|
+
Microsoft Graph answers for identity and directory. Azure APIs answer for RBAC and resources. **Graph is never treated as a source of truth for Azure RBAC.**
|
|
32
|
+
|
|
33
|
+
### No false zero
|
|
34
|
+
|
|
35
|
+
If a permission is missing, the answer is `PERMISSION_DENIED` with `NOT_EVALUATED` coverage — **never `0`**.
|
|
36
|
+
|
|
37
|
+
"I could not evaluate this" and "this does not exist" are different answers. Conflating them in an audit is worse than not answering at all, because a false zero looks like a clean result.
|
|
38
|
+
|
|
39
|
+
This applies to licensing too: zero conditional access policies means something entirely different when the feature isn't licensed versus when it's licensed and unused.
|
|
40
|
+
|
|
41
|
+
### Read-only by construction
|
|
42
|
+
|
|
43
|
+
Only `GET`, `LIST`, `QUERY`, `ASSESS`, and `CORRELATE`. There is no LLM-generated KQL and no free-form endpoint: every query goes through a capability registry with an allowlist and validation of filters and scopes.
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
```mermaid
|
|
48
|
+
flowchart TB
|
|
49
|
+
User(["You"]) -->|natural language| Copilot["Copilot Chat<br/><i>your own model</i>"]
|
|
50
|
+
Copilot <-->|MCP / stdio| Server
|
|
51
|
+
|
|
52
|
+
subgraph Server["IdenGraph MCP Server · 66 read-only tools"]
|
|
53
|
+
direction TB
|
|
54
|
+
Router["Capability router<br/><i>intent → capability</i>"]
|
|
55
|
+
Guard{{"Write guard<br/><i>blocks mutations</i>"}}
|
|
56
|
+
Registry[("Capability registry<br/>43 capabilities · 29 domains")]
|
|
57
|
+
Executor["Read-only executor<br/><i>allowlist + validation</i>"]
|
|
58
|
+
|
|
59
|
+
Router --> Guard --> Registry --> Executor
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Executor -->|identity & directory| Graph["Microsoft Graph"]
|
|
63
|
+
Executor -->|RBAC & resources| Azure["Azure Management<br/>+ Resource Graph"]
|
|
64
|
+
|
|
65
|
+
Graph --> Normalizer["Normalizer & correlation<br/><i>preserves evidence and coverage</i>"]
|
|
66
|
+
Azure --> Normalizer
|
|
67
|
+
Normalizer -->|answer + provenance| Copilot
|
|
68
|
+
|
|
69
|
+
style Guard fill:#c62828,color:#fff
|
|
70
|
+
style Server fill:#0B1F3A,color:#fff
|
|
71
|
+
style Normalizer fill:#1565c0,color:#fff
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Two details worth highlighting:
|
|
75
|
+
|
|
76
|
+
**The write guard sits before routing**, not after. A mutation request is rejected before it can be interpreted as a query.
|
|
77
|
+
|
|
78
|
+
**The normalizer preserves coverage**, not just data. Every answer carries where it came from and whether the source could actually be evaluated — which is what makes the no-false-zero rule enforceable rather than aspirational.
|
|
79
|
+
|
|
80
|
+
### How the pieces are distributed
|
|
81
|
+
|
|
82
|
+
| Layer | Artifact | Role |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| Discovery | [VS Code extension](https://marketplace.visualstudio.com/items?itemName=josimarh.idengraph) | One-click install, prerequisite checks, settings UI |
|
|
85
|
+
| Engine | [`idengraph` on PyPI](https://pypi.org/project/idengraph/) | The MCP server itself |
|
|
86
|
+
| Model | Your Copilot subscription | No LLM cost to this project or to you |
|
|
87
|
+
|
|
88
|
+
The extension does not replace the Python package — it registers it. The engine runs the same way whether launched by the extension or configured by hand.
|
|
89
|
+
|
|
90
|
+
## Install
|
|
91
|
+
|
|
92
|
+
### Recommended: VS Code extension
|
|
93
|
+
|
|
94
|
+
Install **[IdenGraph](https://marketplace.visualstudio.com/items?itemName=josimarh.idengraph)** from the Marketplace, then sign in to Azure:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
az login
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Open Copilot Chat in **agent mode** and ask. The extension verifies prerequisites and guides you if anything is missing.
|
|
101
|
+
|
|
102
|
+
### Alternative: manual MCP configuration
|
|
103
|
+
|
|
104
|
+
Create `.vscode/mcp.json`:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"servers": {
|
|
109
|
+
"idengraph": {
|
|
110
|
+
"type": "stdio",
|
|
111
|
+
"command": "uvx",
|
|
112
|
+
"args": ["idengraph"],
|
|
113
|
+
"env": { "MOCK_MODE": "false" }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Prerequisites
|
|
120
|
+
|
|
121
|
+
- **Python 3.10+**
|
|
122
|
+
- **[uv](https://docs.astral.sh/uv/getting-started/installation/)**
|
|
123
|
+
- **[Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli)** with an active session (`az login`)
|
|
124
|
+
|
|
125
|
+
Authentication uses `DefaultAzureCredential`, which reuses your Azure CLI session. There is no API key to manage, and no credential is stored by this project.
|
|
126
|
+
|
|
127
|
+
## Configuration
|
|
128
|
+
|
|
129
|
+
| Setting | Env var | Default | Purpose |
|
|
130
|
+
|---|---|---|---|
|
|
131
|
+
| `idengraph.useMockData` | `MOCK_MODE` | `false` (extension) | Query fictional data instead of your tenant |
|
|
132
|
+
| `idengraph.sanitizeOutput` | `SANITIZE_FOR_LLM` | `true` | Mask resource names, subscription IDs, and IPs before they reach the model |
|
|
133
|
+
| `idengraph.subscriptions` | `AZURE_SUBSCRIPTIONS` | all accessible | Restrict queries to specific subscriptions |
|
|
134
|
+
|
|
135
|
+
The Python package defaults to `MOCK_MODE=true` so that nothing touches a real tenant without explicit intent. The extension sets it to `false`, since installing it is already that intent.
|
|
136
|
+
|
|
137
|
+
## Permissions
|
|
138
|
+
|
|
139
|
+
On Azure: **`Reader`** on the subscriptions you want to audit.
|
|
140
|
+
|
|
141
|
+
On Microsoft Graph, delegated permissions vary by question:
|
|
142
|
+
|
|
143
|
+
| Area | Permission |
|
|
144
|
+
|---|---|
|
|
145
|
+
| Users, groups, applications, service principals | `Directory.Read.All` |
|
|
146
|
+
| Directory roles and directory PIM | `RoleManagement.Read.All` |
|
|
147
|
+
| Authentication methods and MFA | `UserAuthenticationMethod.Read.All` |
|
|
148
|
+
| Conditional access | `Policy.Read.All` |
|
|
149
|
+
| License posture | `Organization.Read.All` |
|
|
150
|
+
|
|
151
|
+
Missing a permission only marks the matching area as not evaluated. Everything else keeps working — and the affected area reports *why* it could not be evaluated.
|
|
152
|
+
|
|
153
|
+
## Privacy
|
|
154
|
+
|
|
155
|
+
Queried data belongs to **your** tenant and travels between your machine, Microsoft APIs, and the Copilot model **you already use**. This project sends nothing to third-party servers and collects no telemetry.
|
|
156
|
+
|
|
157
|
+
By default, `SANITIZE_FOR_LLM=true` masks resource names, resource groups, subscription IDs, and IP addresses before content reaches the model.
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
python -m venv .venv
|
|
163
|
+
.venv\Scripts\activate # Windows
|
|
164
|
+
# source .venv/bin/activate # Linux/macOS
|
|
165
|
+
pip install -r requirements.txt
|
|
166
|
+
cp .env.example .env
|
|
167
|
+
python test_smoke.py
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The smoke test runs in mock mode and never touches a tenant.
|
|
171
|
+
|
|
172
|
+
### Repository layout
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
mcp_server.py MCP server and tool registration
|
|
176
|
+
services/azure_auth.py credentials, tokens, caching
|
|
177
|
+
services/graph_capabilities.py capability registry
|
|
178
|
+
services/capability_router.py natural language → capability
|
|
179
|
+
services/capability_executor.py validated read-only execution
|
|
180
|
+
services/azure_role_definitions.py authoritative Azure role name resolution
|
|
181
|
+
services/azure_pim.py resource PIM with confirmed coverage
|
|
182
|
+
services/entra_licenses.py tenant licensing and feature availability
|
|
183
|
+
services/data/ mock data used when MOCK_MODE=true
|
|
184
|
+
extension/ VS Code extension (TypeScript)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The repository also contains a Streamlit portal (`app.py` + `agent.py`) used for development and demos. It is not part of the published package, whose surface is the MCP server only:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
streamlit run app.py
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Known limitations
|
|
194
|
+
|
|
195
|
+
- Agent Identity detection is **heuristic** where the directory exposes no dedicated type. Results are labeled as such, never presented as fact.
|
|
196
|
+
- `Public IP` indicates a public address, which **does not prove** workload exposure.
|
|
197
|
+
- Without `RoleManagement.Read.All`, directory PIM reports as not evaluated rather than empty.
|
|
198
|
+
- License posture currently cross-references PIM and Conditional Access. Capabilities still marked `not_integrated` in the registry are not probed, and deliberately return no verdict.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT — see [LICENSE](LICENSE).
|