diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py index 6e36aa81fed5..5c8fbcc18ab2 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py @@ -25,6 +25,15 @@ EvaluationsOperations, TelemetryOperations, ) +from azure.identity import DefaultAzureCredential +from azure.identity import DefaultAzureCredential +from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient +from azure.mgmt.applicationinsights.v2020_02_02_preview.models import ApplicationInsightsComponent +from azure.mgmt.authorization import AuthorizationManagementClient +from azure.mgmt.authorization.models import RoleAssignmentCreateParameters +from azure.mgmt.applicationinsights.v2020_02_02_preview.operations._components_operations import ComponentsOperations + +import uuid from .operations._patch import _SyncCredentialWrapper, InferenceOperations if TYPE_CHECKING: @@ -66,6 +75,9 @@ def __init__( # pylint: disable=super-init-not-called,too-many-statements kwargs3 = kwargs.copy() self._user_agent: Optional[str] = kwargs.get("user_agent", None) + self._create_app_insights: Optional[bool] = kwargs.get("create_app_insights", None) + if self._create_app_insights: + self.create_app_insights(subscription_id, resource_group_name, project_name, credential) # For getting AppInsights connection string from the AppInsights resource. # The AppInsights resource URL is not known at this point. We need to get it from the @@ -259,6 +271,55 @@ def from_connection_string(cls, conn_str: str, credential: "AsyncTokenCredential credential, **kwargs, ) + + @classmethod + def create_app_insights(cls, subscription_id, resource_group_name, resource_name, location, principal_id, credential=None) -> Self: + """Create an Application Insights resource and assign RBAC roles. + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Application Insights resource. + :type resource_name: str + :param location: The location of the resource. + :type location: str + :return: The connection string of the Application Insights resource. + :rtype: str + """ + # Use provided credential or create a new one + credential = credential or DefaultAzureCredential() + + # Create Application Insights resource + client = ApplicationInsightsManagementClient(credential, subscription_id) + app_insights_component = ApplicationInsightsComponent( + location=location, + application_type="web", + kind="web" + ) + client.components.create_or_update(resource_group_name, resource_name, app_insights_component) + + # Retrieve the connection string + component = client.components.get(resource_group_name, resource_name) + connection_string = component.connection_string + + # Assign RBAC roles + role_definition = '3913510d-42f4-4e42-8a64-420c390055eb' # Monitoring Metrics Publisher + authorization_client = AuthorizationManagementClient(credential, subscription_id) + role_definition_id = f"/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/{role_definition}" + scope = f"/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/microsoft.insights/components/{resource_name}" + role_assignment_name = str(uuid.uuid4()) # Generate a unique role assignment name + + role_assignment_params = RoleAssignmentCreateParameters( + role_definition_id=role_definition_id, + principal_id=principal_id + ) + authorization_client.role_assignments.create( + scope=scope, + role_assignment_name=role_assignment_name, + parameters=role_assignment_params + ) + if not connection_string: + raise ValueError("Connection string cannot be None") + credential = DefaultAzureCredential() + return cls.from_connection_string(connection_string, credential) def upload_file(self, file_path: Union[Path, str, PathLike]) -> Tuple[str, str]: """Upload a file to the Azure AI Foundry project. diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py index ca9fe7d7ad6a..3214d496157d 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py @@ -33,6 +33,16 @@ from azure.core.credentials import TokenCredential from azure.core.exceptions import ResourceNotFoundError from azure.core.tracing.decorator_async import distributed_trace_async +from azure.identity import DefaultAzureCredential +from azure.core.tracing.decorator import distributed_trace +from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient +from azure.mgmt.applicationinsights.v2018_05_01_preview.models import ApplicationInsightsComponent +from azure.identity import DefaultAzureCredential +from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient +from azure.mgmt.applicationinsights.models import ApplicationInsightsComponent +from azure.mgmt.authorization import AuthorizationManagementClient +from azure.mgmt.authorization.models import RoleAssignmentCreateParameters +import uuid from ... import models as _models from ..._vendor import FileType diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py index 0db1e7c1a4b8..a6d29ffe68b1 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_patch.py @@ -29,7 +29,16 @@ ) from azure.core.exceptions import ResourceNotFoundError +from azure.identity import DefaultAzureCredential from azure.core.tracing.decorator import distributed_trace +from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient +from azure.mgmt.applicationinsights.v2018_05_01_preview.models import ApplicationInsightsComponent +from azure.identity import DefaultAzureCredential +from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient +from azure.mgmt.applicationinsights.models import ApplicationInsightsComponent +from azure.mgmt.authorization import AuthorizationManagementClient +from azure.mgmt.authorization.models import RoleAssignmentCreateParameters +import uuid from .. import models as _models from .._vendor import FileType @@ -797,7 +806,15 @@ def get_connection_string(self) -> str: ) if not get_workspace_response.properties.application_insights: - raise ResourceNotFoundError("Application Insights resource was not enabled for this Project.") + # raise ResourceNotFoundError("Application Insights resource was not enabled for this Project.") + # instead or raising resource not found, create resource. may need to refactor depending on if + # we want this to be client choice to create the app insights in case resource doesnt exist + # or just do this automatically + self._outer_instance.telemetry.create_application_insights_if_not_exists( + resource_group_name=get_workspace_response.properties.resourceGroupName, + resource_name="your_resource_name", + location="your_location" + ) # Make a GET call to the Application Insights resource URL to get the connection string app_insights_respose: GetAppInsightsResponse = self._get_app_insights( @@ -808,6 +825,7 @@ def get_connection_string(self) -> str: return self._connection_string + # TODO: what about `set AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED=true`? # TODO: This could be a class method. But we don't have a class property AIProjectClient.telemetry def enable(self, *, destination: Union[TextIO, str, None] = None, **kwargs) -> None: