1-introduction/1-authentication.ipynb (590 lines of code) (raw):
{
"cells": [
{
"cell_type": "markdown",
"id": "9c198c79",
"metadata": {},
"source": [
"# Troubleshooting Authentication with DefaultAzureCredential\n",
"\n",
"## Overview\n",
"\n",
"DefaultAzureCredential is the recommended way to handle authentication in Azure applications. It provides a streamlined authentication flow by trying multiple credential types in sequence until one succeeds. This notebook will help you troubleshoot common authentication issues and ensure proper setup.\n",
"\n",
"## Understanding DefaultAzureCredential\n",
"\n",
"DefaultAzureCredential attempts authentication methods in the following order:\n",
"\n",
"1. Environment Credentials\n",
"2. Workload Identity (in Kubernetes)\n",
"3. Managed Identity\n",
"4. Azure CLI Credentials\n",
"5. Azure PowerShell Credentials\n",
"6. Visual Studio Code Credentials\n",
"7. Interactive Browser Authentication (as fallback)\n",
"\n",
"## Prerequisites\n",
"\n",
"Ensure you have the following installed:\n",
"- Azure CLI\n",
"- Azure Developer CLI (optional)\n",
"- Python Virtual Environment or Conda (use `uv venv` or `conda create`)\n",
"- Required role assignments (Azure AI Developer)\n",
"- Jupyter Notebook environment - kernel configured to use Python 3.8 or later\n",
"\n",
"## Authentication Methods\n",
"\n",
"### 1. Using Azure CLI (Recommended for Local Development)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74c8ba60",
"metadata": {},
"outputs": [],
"source": [
"# Install required packages\n",
"!pip install azure-identity"
]
},
{
"cell_type": "markdown",
"id": "18f7cb3d",
"metadata": {},
"source": [
"# First, we'll authenticate using Azure CLI\n",
"This is the recommended approach for local development.\n",
"\n",
"When you run the code below, you will be redirected to:\n",
"- Either the Azure portal in your browser to complete the login \n",
"- Or use Windows login if you're already signed in to your machine\n",
"\n",
"The code will:\n",
"1. Load environment variables from .env file, including the TENANT_ID\n",
"2. Use Azure CLI to log in to your specific tenant \n",
"3. Test authentication by attempting to get a token\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "23fe28d8",
"metadata": {},
"outputs": [],
"source": [
"# Import required packages\n",
"from IPython.display import display\n",
"from IPython.display import HTML\n",
"import getpass\n",
"from dotenv import load_dotenv\n",
"import os\n",
"from pathlib import Path # For cross-platform path handling\n",
"\n",
"# Get the path to the .env file which is in the parent directory\n",
"notebook_path = Path().absolute() # Get absolute path of current notebook\n",
"parent_dir = notebook_path.parent # Get parent directory\n",
"load_dotenv(parent_dir / '.env') # Load environment variables from .env file\n",
"\n",
"# Get tenant ID from environment variable\n",
"tenant_id = os.getenv(\"TENANT_ID\")\n",
"\n",
"# Azure login with specific tenant\n",
"!az login --tenant {tenant_id}\n",
"\n",
"# Get subscription ID from connection string\n",
"conn_str = os.getenv(\"PROJECT_CONNECTION_STRING\")\n",
"subscription_id = conn_str.split(';')[1] if conn_str else None\n",
"\n",
"if subscription_id:\n",
" # Set the subscription\n",
" !az account set --subscription {subscription_id}\n",
" print(f\"✓ Successfully set subscription: {subscription_id}\")\n",
"else:\n",
" print(\"⚠️ Could not get subscription ID from PROJECT_CONNECTION_STRING\")\n"
]
},
{
"cell_type": "markdown",
"id": "44c3458a",
"metadata": {},
"source": [
"# Next, we'll test the authentication by attempting to get a token using DefaultAzureCredential\n",
"\n",
"DefaultAzureCredential will try multiple authentication methods in this order:\n",
"1. Environment credentials (if environment variables are set)\n",
"2. Managed Identity credentials (if running in Azure)\n",
"3. Shared Token Cache credentials (from previous logins) \n",
"4. Visual Studio Code credentials (if using VS Code)\n",
"5. Azure CLI credentials (which we just set up)\n",
"\n",
"The code below will:\n",
"1. Create a DefaultAzureCredential instance\n",
"2. Try to get a token for Azure Cognitive Services\n",
"3. Print success message if token is acquired\n",
"\n",
">Note: You may see some warning/error messages as it tries different authentication methods - \n",
">this is normal and can be ignored as long as you see \"Successfully acquired token!\" at the end\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f9140263",
"metadata": {},
"outputs": [],
"source": [
"# Then use DefaultAzureCredential in your code\n",
"from azure.identity import DefaultAzureCredential\n",
"from azure.core.credentials import AccessToken\n",
"import logging\n",
"\n",
"# Enable detailed logging\n",
"logging.basicConfig(level=logging.DEBUG)\n",
"\n",
"try:\n",
" credential = DefaultAzureCredential()\n",
" # Test token acquisition\n",
" token = credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n",
" print(\"Successfully acquired token!\")\n",
"except Exception as e:\n",
" print(f\"Authentication failed: {str(e)}\")"
]
},
{
"cell_type": "markdown",
"id": "5ad83ef2",
"metadata": {},
"source": [
"## Now that you have successfully authenticated, you can proceed to [2-environment_setup.ipynb](2-environment_setup.ipynb), or try the additional authentication methods or troubleshoot below.\n"
]
},
{
"cell_type": "markdown",
"id": "d717f84a",
"metadata": {},
"source": [
"### 2. Using Visual Studio Code (optional)\n",
"\n",
"If you're using VS Code with the Azure extension:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5bc79867",
"metadata": {},
"outputs": [],
"source": [
"from azure.identity import DefaultAzureCredential, VisualStudioCodeCredential\n",
"\n",
"try:\n",
" # Explicitly try VS Code credentials\n",
" vscode_credential = VisualStudioCodeCredential()\n",
" token = vscode_credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n",
" print(\"Successfully authenticated with VS Code credentials!\")\n",
"except Exception as e:\n",
" print(f\"VS Code authentication failed: {str(e)}\")"
]
},
{
"cell_type": "markdown",
"id": "9286b93f",
"metadata": {},
"source": [
"### 3. Using Service Principal (Optional - have to set environment variables)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8d7e24f1",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"# Set these environment variables before running your application\n",
"required_env_vars = {\n",
" \"AZURE_CLIENT_ID\": \"your-client-id\",\n",
" \"AZURE_CLIENT_SECRET\": \"your-client-secret\",\n",
" \"AZURE_TENANT_ID\": \"your-tenant-id\"\n",
"}\n",
"\n",
"# Verify environment variables are set\n",
"def check_env_vars():\n",
" missing_vars = [var for var, _ in required_env_vars.items() \n",
" if not os.getenv(var)]\n",
" if missing_vars:\n",
" print(f\"Missing environment variables: {', '.join(missing_vars)}\")\n",
" return False\n",
" return True\n",
"\n",
"if check_env_vars():\n",
" credential = DefaultAzureCredential()\n",
" # Test authentication\n",
" try:\n",
" token = credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n",
" print(\"Successfully authenticated using environment credentials!\")\n",
" except Exception as e:\n",
" print(f\"Authentication failed: {str(e)}\")"
]
},
{
"cell_type": "markdown",
"id": "f69d5e09",
"metadata": {},
"source": [
"## Troubleshooting Steps\n",
"\n",
"### 1. Verify Role Assignments\n",
"\n",
"```bash\n",
"# Check role assignments for your user/service principal\n",
"az role assignment list --assignee \"your-email@domain.com\" --output table\n",
"```\n",
"\n",
"### 2. Debug Token Acquisition\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "86dfd0ee",
"metadata": {},
"outputs": [],
"source": [
"import logging\n",
"from azure.identity import DefaultAzureCredential\n",
"\n",
"# Set up detailed logging\n",
"logger = logging.getLogger('azure.identity')\n",
"logger.setLevel(logging.DEBUG)\n",
"\n",
"# Use a basic StreamHandler instead of LoggingHandler\n",
"handler = logging.StreamHandler()\n",
"handler.setLevel(logging.DEBUG)\n",
"formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n",
"handler.setFormatter(formatter)\n",
"logger.addHandler(handler)\n",
"\n",
"def test_authentication():\n",
" try:\n",
" credential = DefaultAzureCredential(logging_enable=True)\n",
" token = credential.get_token(\"https://cognitiveservices.azure.com/.default\")\n",
" print(f\"Authentication successful!\")\n",
" return True\n",
" except Exception as e:\n",
" print(f\"Authentication failed with error: {str(e)}\")\n",
" print(\"\\nTroubleshooting steps:\")\n",
" print(\"1. Verify you're logged in: 'az account show'\")\n",
" print(\"2. Check role assignments: 'az role assignment list'\")\n",
" print(\"3. Verify tenant ID: 'az account show --query tenantId'\")\n",
" return False\n",
"\n",
"# Run the test\n",
"test_authentication()"
]
},
{
"cell_type": "markdown",
"id": "d917029e",
"metadata": {},
"source": [
"\n",
"### 3. Verify Azure AI Developer Role"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cc256cd1",
"metadata": {},
"outputs": [],
"source": [
"def verify_ai_developer_role(subscription_id, resource_group, resource_name):\n",
" from azure.mgmt.authorization import AuthorizationManagementClient\n",
" \n",
" auth_client = AuthorizationManagementClient(\n",
" credential=DefaultAzureCredential(),\n",
" subscription_id=subscription_id\n",
" )\n",
" \n",
" resource_id = f\"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.CognitiveServices/accounts/{resource_name}\"\n",
" \n",
" assignments = auth_client.role_assignments.list_for_scope(resource_id)\n",
" \n",
" ai_developer_role_id = \"a97b65f3-24c7-4388-baec-2e87135dc908\" # Azure AI Developer role ID\n",
" \n",
" for assignment in assignments:\n",
" if assignment.role_definition_id.endswith(ai_developer_role_id):\n",
" return True\n",
" \n",
" return False"
]
},
{
"cell_type": "markdown",
"id": "e5c9d0b9",
"metadata": {},
"source": [
"### 4. Add Agent Service permissions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "53414c19",
"metadata": {},
"outputs": [],
"source": [
"# Install required packages\n",
"!pip install azure-mgmt-authorization azure-mgmt-resource"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "87c35ddc",
"metadata": {},
"outputs": [],
"source": [
"from azure.mgmt.authorization import AuthorizationManagementClient\n",
"from azure.mgmt.resource import ResourceManagementClient\n",
"from azure.identity import DefaultAzureCredential\n",
"from azure.ai.projects import AIProjectClient\n",
"from azure.ai.projects.models import ConnectionType\n",
"import os, jwt\n",
"\n",
"def assign_roles():\n",
" try:\n",
" # Parse connection string for resource details\n",
" conn_str = os.environ.get(\"PROJECT_CONNECTION_STRING\")\n",
" parts = conn_str.split(';')\n",
" subscription_id = parts[1]\n",
" resource_group = parts[2]\n",
" workspace_name = parts[3]\n",
" \n",
" # Initialize project client\n",
" project_client = AIProjectClient.from_connection_string(\n",
" credential=DefaultAzureCredential(),\n",
" conn_str=conn_str\n",
" )\n",
" print(\"✅ Created AIProjectClient\")\n",
" \n",
" # Initialize credential and get current user's principal ID\n",
" credential = DefaultAzureCredential()\n",
" token = credential.get_token(\"https://management.azure.com/.default\")\n",
" claims = jwt.decode(token.token, options={\"verify_signature\": False})\n",
" principal_id = claims.get('oid') # Object ID is the principal ID in Azure AD\n",
" \n",
" # Initialize clients\n",
" auth_client = AuthorizationManagementClient(credential, subscription_id)\n",
" resource_client = ResourceManagementClient(credential, subscription_id)\n",
" \n",
" # Get workspace resource ID\n",
" workspace_scope = (f\"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/\"\n",
" f\"providers/Microsoft.MachineLearningServices/workspaces/{workspace_name}\")\n",
" \n",
" # Get search service scope from the connection\n",
" search_conn = project_client.connections.get_default(\n",
" connection_type=ConnectionType.AZURE_AI_SEARCH,\n",
" include_credentials=True\n",
" )\n",
" search_endpoint = search_conn.endpoint_url\n",
" search_name = search_endpoint.split('.')[0].split('//')[1]\n",
" search_scope = (f\"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/\"\n",
" f\"providers/Microsoft.Search/searchServices/{search_name}\")\n",
"\n",
" # Define custom role for agent permissions\n",
" custom_role_name = \"AI Foundry Agent Operator\"\n",
" custom_role_def = {\n",
" \"properties\": { # Added properties wrapper\n",
" \"roleName\": custom_role_name,\n",
" \"description\": \"Custom role for AI Foundry agent operations\",\n",
" \"assignableScopes\": [workspace_scope],\n",
" \"permissions\": [{\n",
" \"actions\": [\n",
" \"Microsoft.MachineLearning/workspaces/agents/*\", # Updated action format\n",
" \"Microsoft.MachineLearning/workspaces/connections/*\"\n",
" ],\n",
" \"notActions\": [],\n",
" \"dataActions\": [\n",
" \"Microsoft.MachineLearning/workspaces/agents/*\",\n",
" \"Microsoft.MachineLearning/workspaces/connections/*\"\n",
" ],\n",
" \"notDataActions\": []\n",
" }],\n",
" \"type\": \"CustomRole\" # Added type\n",
" }\n",
" }\n",
"\n",
" # Create custom role definition\n",
" try:\n",
" role_definition_id = os.urandom(16).hex()\n",
" custom_role = auth_client.role_definitions.create_or_update(\n",
" scope=workspace_scope,\n",
" role_definition_id=role_definition_id,\n",
" role_definition=custom_role_def\n",
" )\n",
" print(f\"✅ Created custom role: {custom_role_name}\")\n",
" except Exception as e:\n",
" print(f\"⚠️ Custom role creation error: {str(e)}\")\n",
" print(\"Trying alternative role definition format...\")\n",
" \n",
" # Try alternative format if first attempt fails\n",
" try:\n",
" custom_role_def_alt = {\n",
" \"properties\": {\n",
" \"roleName\": custom_role_name,\n",
" \"description\": \"Custom role for AI Foundry agent operations\",\n",
" \"assignableScopes\": [workspace_scope],\n",
" \"permissions\": [{\n",
" \"actions\": [\n",
" \"Microsoft.MachineLearningServices/*\" # Broader permissions as fallback\n",
" ],\n",
" \"notActions\": [],\n",
" \"dataActions\": [\n",
" \"Microsoft.MachineLearningServices/*\"\n",
" ],\n",
" \"notDataActions\": []\n",
" }],\n",
" \"type\": \"CustomRole\"\n",
" }\n",
" }\n",
" \n",
" custom_role = auth_client.role_definitions.create_or_update(\n",
" scope=workspace_scope,\n",
" role_definition_id=role_definition_id,\n",
" role_definition=custom_role_def_alt\n",
" )\n",
" print(f\"✅ Created custom role with alternative permissions: {custom_role_name}\")\n",
" except Exception as e2:\n",
" print(f\"❌ Alternative role creation also failed: {str(e2)}\")\n",
" \n",
" # Role definitions to assign\n",
" roles_to_assign = [\n",
" {\n",
" \"name\": \"Azure AI Developer\",\n",
" \"scope\": workspace_scope\n",
" },\n",
" {\n",
" \"name\": \"Storage Blob Data Contributor\",\n",
" \"scope\": workspace_scope\n",
" },\n",
" {\n",
" \"name\": \"Search Service Contributor\",\n",
" \"scope\": search_scope\n",
" },\n",
" {\n",
" \"name\": custom_role_name,\n",
" \"scope\": workspace_scope\n",
" }\n",
" ]\n",
" \n",
" print(\"\\n🔑 Starting role assignments...\")\n",
" \n",
" for role in roles_to_assign:\n",
" try:\n",
" # Get role definition ID\n",
" role_defs = list(auth_client.role_definitions.list(\n",
" scope=role[\"scope\"],\n",
" filter=f\"roleName eq '{role['name']}'\"\n",
" ))\n",
" if not role_defs:\n",
" print(f\"❌ Could not find role definition for {role['name']}\")\n",
" continue\n",
" \n",
" role_def_id = role_defs[0].id\n",
" \n",
" # Create role assignment\n",
" assignment = auth_client.role_assignments.create(\n",
" scope=role[\"scope\"],\n",
" role_assignment_name=os.urandom(16).hex(),\n",
" parameters={\n",
" \"properties\": {\n",
" \"roleDefinitionId\": role_def_id,\n",
" \"principalId\": principal_id,\n",
" \"principalType\": \"User\"\n",
" }\n",
" }\n",
" )\n",
" print(f\"✅ Assigned {role['name']} role\")\n",
" \n",
" except Exception as e:\n",
" print(f\"❌ Error assigning {role['name']}: {str(e)}\")\n",
" \n",
" print(\"\\n⚠️ Note: Role assignments may take a few minutes to propagate\")\n",
" \n",
" except Exception as e:\n",
" print(f\"❌ Error in role assignment process: {str(e)}\")\n",
" import traceback\n",
" print(f\"Full traceback:\\n{traceback.format_exc()}\")\n",
"\n",
"# Execute the role assignments\n",
"assign_roles()"
]
},
{
"cell_type": "markdown",
"id": "57658b99",
"metadata": {},
"source": [
"## Common Issues and Solutions\n",
"\n",
"1. **Token Acquisition Failed**\n",
" - Verify Azure CLI login: `az login --tenant <tenant-id>`\n",
" - Check default subscription: `az account show`\n",
" - Ensure correct tenant: `az account set --subscription <subscription-id>`\n",
"\n",
"2. **Missing Role Assignments**\n",
" - Add Azure AI Developer role:\n",
" ```bash\n",
" az role assignment create --assignee \"user@domain.com\" \\\n",
" --role \"Azure AI Developer\" \\\n",
" --scope \"/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.CognitiveServices/accounts/<resource-name>\"\n",
" ```\n",
"\n",
"3. **Environment Variable Issues**\n",
" - Verify environment variables are set correctly\n",
" - Check for typos in variable names\n",
" - Ensure no extra spaces in values\n",
"\n",
"## Best Practices\n",
"\n",
"1. Always use environment variables for service principal credentials\n",
"2. Implement proper error handling and logging\n",
"3. Use managed identities when deploying to Azure services\n",
"4. Regularly rotate service principal secrets\n",
"5. Follow the principle of least privilege when assigning roles\n",
"\n",
"## Additional Resources\n",
"\n",
"- [Azure Identity Documentation](https://docs.microsoft.com/python/api/overview/azure/identity-readme)\n",
"- [DefaultAzureCredential Authentication Flow](https://docs.microsoft.com/azure/developer/python/azure-sdk-authenticate)\n",
"- [Azure RBAC Documentation](https://docs.microsoft.com/azure/role-based-access-control/overview)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}