Skip to content

Commit

Permalink
Better support for deploying to non-home tenant (#1964)
Browse files Browse the repository at this point in the history
* Better support for multitenant

* Update app/backend/app.py

Co-authored-by: Anthony Shaw <anthony.p.shaw@gmail.com>

* Better types

* Update the mock

* Fix Bicep workflow

* Add process timeout in both places

---------

Co-authored-by: Anthony Shaw <anthony.p.shaw@gmail.com>
  • Loading branch information
pamelafox and tonybaloney authored Sep 13, 2024
1 parent fe229cc commit 2333426
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 19 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/azure-dev-validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ jobs:
- name: Build Bicep for linting
uses: azure/CLI@v2
with:
inlineScript: az config set bicep.use_binary_from_path=false && az bicep build -f infra/main.bicep --stdout
inlineScript: |
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
az config set bicep.use_binary_from_path=false && az bicep build -f infra/main.bicep --stdout
psrule:
runs-on: ubuntu-latest
Expand All @@ -42,7 +44,7 @@ jobs:
outputPath: reports/ps-rule-results.sarif
summary: true
continue-on-error: true

env:
PSRULE_CONFIGURATION_AZURE_BICEP_FILE_EXPANSION: 'true'
PSRULE_CONFIGURATION_AZURE_BICEP_FILE_EXPANSION_TIMEOUT: '30'
Expand Down
26 changes: 20 additions & 6 deletions app/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
SpeechSynthesizer,
)
from azure.core.exceptions import ResourceNotFoundError
from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider
from azure.identity.aio import (
AzureDeveloperCliCredential,
ManagedIdentityCredential,
get_bearer_token_provider,
)
from azure.monitor.opentelemetry import configure_azure_monitor
from azure.search.documents.aio import SearchClient
from azure.search.documents.indexes.aio import SearchIndexClient
Expand Down Expand Up @@ -436,11 +440,21 @@ async def setup_clients():
USE_SPEECH_OUTPUT_BROWSER = os.getenv("USE_SPEECH_OUTPUT_BROWSER", "").lower() == "true"
USE_SPEECH_OUTPUT_AZURE = os.getenv("USE_SPEECH_OUTPUT_AZURE", "").lower() == "true"

# Use the current user identity to authenticate with Azure OpenAI, AI Search and Blob Storage (no secrets needed,
# just use 'az login' locally, and managed identity when deployed on Azure). If you need to use keys, use separate AzureKeyCredential instances with the
# keys for each service
# If you encounter a blocking error during a DefaultAzureCredential resolution, you can exclude the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True)
azure_credential = DefaultAzureCredential(exclude_shared_token_cache_credential=True)
# Use the current user identity for keyless authentication to Azure services.
# This assumes you use 'azd auth login' locally, and managed identity when deployed on Azure.
# The managed identity is setup in the infra/ folder.
azure_credential: Union[AzureDeveloperCliCredential, ManagedIdentityCredential]
if os.getenv("WEBSITE_HOSTNAME"): # Environment variable set on Azure Web Apps
current_app.logger.info("Setting up Azure credential using ManagedIdentityCredential")
azure_credential = ManagedIdentityCredential()
elif AZURE_TENANT_ID:
current_app.logger.info(
"Setting up Azure credential using AzureDeveloperCliCredential with tenant_id %s", AZURE_TENANT_ID
)
azure_credential = AzureDeveloperCliCredential(tenant_id=AZURE_TENANT_ID, process_timeout=60)
else:
current_app.logger.info("Setting up Azure credential using AzureDeveloperCliCredential for home tenant")
azure_credential = AzureDeveloperCliCredential(process_timeout=60)

# Set up clients for AI Search and Storage
search_client = SearchClient(
Expand Down
11 changes: 6 additions & 5 deletions app/backend/prepdocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,12 @@ async def main(strategy: Strategy, setup_index: bool = True):
use_int_vectorization = args.useintvectorization and args.useintvectorization.lower() == "true"

# Use the current user identity to connect to Azure services unless a key is explicitly set for any of them
azd_credential = (
AzureDeveloperCliCredential()
if args.tenantid is None
else AzureDeveloperCliCredential(tenant_id=args.tenantid, process_timeout=60)
)
if args.tenantid:
logger.info("Connecting to Azure services using the azd credential for tenant %s", args.tenantid)
azd_credential = AzureDeveloperCliCredential(tenant_id=args.tenantid, process_timeout=60)
else:
logger.info("Connecting to Azure services using the azd credential for home tenant")
azd_credential = AzureDeveloperCliCredential(process_timeout=60)

if args.removeall:
document_action = DocumentAction.RemoveAll
Expand Down
3 changes: 0 additions & 3 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,6 @@
"enableUnauthenticatedAccess": {
"value": "${AZURE_ENABLE_UNAUTHENTICATED_ACCESS=false}"
},
"tenantId": {
"value": "${AZURE_TENANT_ID}"
},
"authTenantId": {
"value": "${AZURE_AUTH_TENANT_ID}"
},
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def mock_env(monkeypatch, request):
if os.getenv("AZURE_USE_AUTHENTICATION") is not None:
monkeypatch.delenv("AZURE_USE_AUTHENTICATION")

with mock.patch("app.DefaultAzureCredential") as mock_default_azure_credential:
with mock.patch("app.AzureDeveloperCliCredential") as mock_default_azure_credential:
mock_default_azure_credential.return_value = MockAzureCredential()
yield

Expand Down Expand Up @@ -383,7 +383,7 @@ async def auth_client(
for key, value in request.param.items():
monkeypatch.setenv(key, value)

with mock.patch("app.DefaultAzureCredential") as mock_default_azure_credential:
with mock.patch("app.AzureDeveloperCliCredential") as mock_default_azure_credential:
mock_default_azure_credential.return_value = MockAzureCredential()
quart_app = app.create_app()

Expand Down Expand Up @@ -422,7 +422,7 @@ async def auth_public_documents_client(
for key, value in request.param.items():
monkeypatch.setenv(key, value)

with mock.patch("app.DefaultAzureCredential") as mock_default_azure_credential:
with mock.patch("app.AzureDeveloperCliCredential") as mock_default_azure_credential:
mock_default_azure_credential.return_value = MockAzureCredential()
quart_app = app.create_app()

Expand Down

0 comments on commit 2333426

Please sign in to comment.