config.py•3 kB
import json
import os
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from azure.identity import ClientSecretCredential
class AzureConfig:
"""Minimal loader for azure-config.json used by this server.
Looks for (in order):
- Path in env var AZURE_ASSISTANT_CONFIG
- ./azure-config.json (project root)
"""
def __init__(self) -> None:
self.root_dir = Path(__file__).resolve().parents[2]
self.path = self._resolve_config_path()
with open(self.path, "r") as f:
self._data = json.load(f)
def _resolve_config_path(self) -> Path:
env_path = os.environ.get("AZURE_ASSISTANT_CONFIG")
if env_path and Path(env_path).is_file():
return Path(env_path)
here = self.root_dir / "azure-config.json"
if here.is_file():
return here
raise FileNotFoundError(
"azure-config.json not found. Place it in the project root or set AZURE_ASSISTANT_CONFIG to the file path."
)
def get_tenants(self) -> List[Dict[str, Any]]:
return list(self._data.get("tenants", []))
def is_debug_enabled(self) -> bool:
return bool(self._data.get("debug", False))
def get_config_path(self) -> str:
return str(self.path)
def get_default_tenant(self) -> Dict[str, Any]:
tenants = self.get_tenants()
if not tenants:
raise RuntimeError("No tenants configured in azure-config.json")
return tenants[0]
def get_tenant(self, name_or_id: Optional[str] = None) -> Dict[str, Any]:
if not name_or_id:
return self.get_default_tenant()
for t in self.get_tenants():
if t.get("name") == name_or_id or t.get("id") == name_or_id:
return t
raise KeyError(f"Tenant '{name_or_id}' not found in azure-config.json")
def get_credentials(self, tenant_name: Optional[str] = None) -> Tuple[ClientSecretCredential, List[str]]:
t = self.get_tenant(tenant_name)
tenant_id = t["id"]
sp = t["service_principal"]
cred = ClientSecretCredential(
tenant_id=tenant_id,
client_id=sp["client_id"],
client_secret=sp["client_secret"],
)
subs = []
if t.get("default_subscription_id"):
subs.append(t["default_subscription_id"])
return cred, subs
def get_management_group_id(self, tenant_name: Optional[str] = None) -> Optional[str]:
t = self.get_tenant(tenant_name)
mg = t.get("management_group_id") or t.get("managementGroupId")
if not isinstance(mg, str) or not mg.strip():
return None
mg = mg.strip()
# Accept either short MG name (recommended) or full resource ID; normalize to short name
if "/managementGroups/" in mg:
try:
return mg.rsplit("/managementGroups/", 1)[-1]
except Exception:
return mg
return mg