diff --git a/charts/azure-app-exporter/Chart.yaml b/charts/azure-app-exporter/Chart.yaml index ff76325..76bb2bb 100644 --- a/charts/azure-app-exporter/Chart.yaml +++ b/charts/azure-app-exporter/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: azure-app-exporter description: Exposing Prometheus Metrics for Azure Service Principals type: application -version: 0.2.0 -appVersion: "0.1.35" +version: 0.3.0 +appVersion: "0.1.36" keywords: - azure - prometheus diff --git a/src/services/azure_app_service.py b/src/services/azure_app_service.py index cc3953b..9bca76e 100644 --- a/src/services/azure_app_service.py +++ b/src/services/azure_app_service.py @@ -1,12 +1,12 @@ from datetime import datetime from typing import List, Optional -from msgraph.graph_service_client import GraphServiceClient from msgraph.generated.models.application import Application from msgraph.generated.models.key_credential import KeyCredential from msgraph.generated.models.password_credential import PasswordCredential +from msgraph.generated.service_principals.service_principals_request_builder import ServicePrincipalsRequestBuilder +from msgraph.graph_service_client import GraphServiceClient from prometheus_client import Gauge -from prometheus_client import REGISTRY from models import AppRegistration from models.app_registration import Credential @@ -33,7 +33,7 @@ async def get_all(self) -> List[AppRegistration]: result = await self.client.applications.get() apps = [] while result is not None: - apps += [AzureAppService._map_app(a) for a in result.value] + apps += [await self._map_app(a) for a in result.value] if result.odata_next_link is None: break result = await self.client.applications.with_url(result.odata_next_link).get() @@ -44,17 +44,34 @@ async def get_all(self) -> List[AppRegistration]: async def get_by(self, app_id: str) -> AppRegistration: result = await self.client.applications.by_application_id(app_id).get() if result is not None: - return AzureAppService._map_app(result) + return await self._map_app(result) else: raise "Application with app id %s not found." % app_id - @staticmethod - def _map_app(app: Application) -> AppRegistration: + async def _map_app(self, app: Application) -> AppRegistration: app_id = app.app_id name = app.display_name - creds = [AzureAppService._map_cred(c) for c in app.password_credentials + app.key_credentials] + sp_creds = await self._fetch_sp_creds(app_id) + creds = [] + for cred in app.password_credentials + app.key_credentials + sp_creds: + if len([c for c in creds if c.name == cred.display_name]) == 0: + creds.append(self._map_cred(cred)) return AppRegistration(id=app_id, name=name, credentials=creds) + async def _fetch_sp_creds(self, app_id): + query_params = ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetQueryParameters( + search=f'\"appId:{app_id}\"' + ) + request_configuration = ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetRequestConfiguration( + query_parameters=query_params, + ) + request_configuration.headers.add("ConsistencyLevel", "eventual") + response = await self.client.service_principals.get(request_configuration=request_configuration) + sp_creds = [] + if len(response.value) > 0: + sp_creds = response.value[0].key_credentials + response.value[0].password_credentials + return sp_creds + @staticmethod def _map_cred(cred: KeyCredential | PasswordCredential) -> Credential: return Credential( @@ -73,4 +90,8 @@ def observe(apps: List[AppRegistration]): if expiry: APP_EXPIRY.labels(app_id=app.id, app_name=app.name).set(int(expiry.timestamp())) for cred in app.credentials: - APP_CREDS_EXPIRY.labels(app_id=app.id, app_name=app.name, credential_name=cred.name).set(int(cred.expires.timestamp())) + ( + APP_CREDS_EXPIRY + .labels(app_id=app.id, app_name=app.name, credential_name=cred.name) + .set(int(cred.expires.timestamp())) + )