From 9581ea7df86e94c697690303743308f3c4c47bc2 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Fri, 6 Sep 2024 06:53:50 +1000 Subject: [PATCH 1/3] ignore resourceToken and migrationToken as being secure params (#1955) --- ps-rule.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ps-rule.yaml b/ps-rule.yaml index bec381fe8a..1558e5788d 100644 --- a/ps-rule.yaml +++ b/ps-rule.yaml @@ -1,3 +1,6 @@ # YAML: Set the AZURE_BICEP_FILE_EXPANSION configuration option to enable expansion configuration: AZURE_BICEP_FILE_EXPANSION: true + AZURE_DEPLOYMENT_NONSENSITIVE_PARAMETER_NAMES: + - resourceToken + - migrationToken From fe229cc60b3d4ed7ed46522328bebeff6a38868a Mon Sep 17 00:00:00 2001 From: Giuseppe Galilei <44207906+GiuseppeGalilei@users.noreply.github.com> Date: Sat, 7 Sep 2024 00:37:29 +0200 Subject: [PATCH 2/3] Update README.md to include auth login fix for github codespaces users (#1856) * Update README.md to include auth login fix for github codespaces users * Update README.md * Update README.md --------- Co-authored-by: Pamela Fox Co-authored-by: Pamela Fox --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 411361f280..5378774959 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,11 @@ Follow these steps to provision Azure resources and deploy the application code: azd auth login ``` + For GitHub Codespaces users, if the previous command fails, try: + ```shell + azd auth login --use-device-code + ``` + 1. Create a new azd environment: ```shell From 2333426632d5f4d30fd733fdbf09b58197589d88 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Fri, 13 Sep 2024 10:22:12 -0700 Subject: [PATCH 3/3] Better support for deploying to non-home tenant (#1964) * Better support for multitenant * Update app/backend/app.py Co-authored-by: Anthony Shaw * Better types * Update the mock * Fix Bicep workflow * Add process timeout in both places --------- Co-authored-by: Anthony Shaw --- .github/workflows/azure-dev-validation.yaml | 6 +++-- app/backend/app.py | 26 ++++++++++++++++----- app/backend/prepdocs.py | 11 +++++---- infra/main.parameters.json | 3 --- tests/conftest.py | 6 ++--- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yaml b/.github/workflows/azure-dev-validation.yaml index 849ced2ada..31747d4a35 100644 --- a/.github/workflows/azure-dev-validation.yaml +++ b/.github/workflows/azure-dev-validation.yaml @@ -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 @@ -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' diff --git a/app/backend/app.py b/app/backend/app.py index 60c125d866..aea6a52765 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -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 @@ -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( diff --git a/app/backend/prepdocs.py b/app/backend/prepdocs.py index d4a63960cc..deea428139 100644 --- a/app/backend/prepdocs.py +++ b/app/backend/prepdocs.py @@ -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 diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 023cea7604..2a4615d911 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -191,9 +191,6 @@ "enableUnauthenticatedAccess": { "value": "${AZURE_ENABLE_UNAUTHENTICATED_ACCESS=false}" }, - "tenantId": { - "value": "${AZURE_TENANT_ID}" - }, "authTenantId": { "value": "${AZURE_AUTH_TENANT_ID}" }, diff --git a/tests/conftest.py b/tests/conftest.py index 19a403fdff..279198b18c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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 @@ -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() @@ -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()