From 065926d19b004ab5631abfbf8579baefec8220a0 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:00:41 +0100 Subject: [PATCH 01/33] Change from key based access to AAD Auth for several services --- .vscode/launch.json | 6 +- app/backend/app.py | 50 ++++++---------- app/backend/requirements.txt | 2 +- app/backend/testsuite.py | 14 +++-- app/enrichment/app.py | 32 +++++----- app/enrichment/requirements.txt | 5 +- app/frontend/src/api/api.ts | 10 ++-- app/frontend/src/api/models.ts | 4 +- .../components/FolderPicker/FolderPicker.tsx | 5 +- .../src/components/filepicker/file-picker.tsx | 5 +- docs/function_debug.md | 2 - functions/FileDeletion/__init__.py | 7 ++- functions/FileFormRecPollingPDF/__init__.py | 34 ++++++----- functions/FileFormRecPollingPDF/function.json | 2 +- .../FileFormRecSubmissionPDF/__init__.py | 23 ++++---- .../FileFormRecSubmissionPDF/function.json | 2 +- functions/FileLayoutParsingOther/__init__.py | 12 ++-- .../FileLayoutParsingOther/function.json | 2 +- functions/FileUploadedFunc/__init__.py | 18 +++--- functions/FileUploadedFunc/function.json | 2 +- functions/ImageEnrichment/__init__.py | 7 +-- functions/ImageEnrichment/function.json | 2 +- functions/TextEnrichment/__init__.py | 27 +++++---- functions/TextEnrichment/function.json | 2 +- functions/requirements.txt | 7 ++- functions/shared_code/status_log.py | 2 +- functions/shared_code/utilities.py | 59 +++---------------- functions/shared_code/utilities_helper.py | 13 ++-- infra/core/db/cosmosdb.tf | 10 ++-- .../core/host/enrichmentapp/enrichmentapp.tf | 3 - infra/core/host/functions/functions.tf | 9 +-- infra/core/host/webapp/variables.tf | 2 +- infra/core/host/webapp/webapp.tf | 10 +++- infra/core/storage/storage-account.tf | 12 ---- infra/main.tf | 2 +- scripts/extract-content.py | 33 ++++++----- scripts/functional-tests.sh | 2 +- scripts/inf-import-state.sh | 3 - scripts/json-to-env.function.debug.sh | 9 +-- scripts/json-to-env.sh | 2 +- scripts/json-to-env.webapp.debug.sh | 4 +- scripts/merge-databases.py | 2 +- scripts/tf-dependencies.json | 25 -------- tests/debug_tests.py | 4 +- tests/run_tests.py | 11 ++-- 45 files changed, 210 insertions(+), 289 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index be3394284..c43dea498 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -86,8 +86,8 @@ "request": "launch", "program": "debug_tests.py", "args": [ - "--storage_account_connection_str", - "${env:STORAGE_ACCOUNT_CONNECTION_STR},", + "--storage_account_url", + "${env:AZURE_BLOB_STORAGE_ENDPOINT},", "--search_service_endpoint", "${env:SEARCH_SERVICE_ENDPOINT}", "--search_index", @@ -98,7 +98,7 @@ "60" ], "env": { - "STORAGE_ACCOUNT_CONNECTION_STR": "${env:BLOB_CONNECTION_STRING}", + "storage_account_url": "${env:AZURE_BLOB_STORAGE_ENDPOINT}", "SEARCH_SERVICE_ENDPOINT": "${env:AZURE_SEARCH_SERVICE_ENDPOINT}", "SEARCH_INDEX": "${env:AZURE_SEARCH_INDEX}", "SEARCH_KEY": "${env:AZURE_SEARCH_SERVICE_KEY}" diff --git a/app/backend/app.py b/app/backend/app.py index 093387ed6..4abffb857 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -24,7 +24,7 @@ from approaches.gpt_direct_approach import GPTDirectApproach from approaches.approach import Approaches from azure.core.credentials import AzureKeyCredential -from azure.identity import DefaultAzureCredential, AzureAuthorityHosts +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient from azure.search.documents import SearchClient from azure.storage.blob import ( @@ -56,7 +56,6 @@ ENV = { "AZURE_BLOB_STORAGE_ACCOUNT": None, "AZURE_BLOB_STORAGE_ENDPOINT": None, - "AZURE_BLOB_STORAGE_KEY": None, "AZURE_BLOB_STORAGE_CONTAINER": "content", "AZURE_BLOB_STORAGE_UPLOAD_CONTAINER": "upload", "AZURE_SEARCH_SERVICE": "gptkb", @@ -104,7 +103,8 @@ "ENABLE_MATH_ASSISTANT": "false", "ENABLE_TABULAR_DATA_ASSISTANT": "false", "ENABLE_MULTIMEDIA": "false", - "MAX_CSV_FILE_SIZE": "7" + "MAX_CSV_FILE_SIZE": "7", + "LOCAL_DEBUG": "false" } for key, value in ENV.items(): @@ -142,10 +142,13 @@ class StatusResponse(pydantic.BaseModel): # 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 DefaultAzureCredntial resolution, you can exclude the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) -azure_credential = DefaultAzureCredential(authority=AUTHORITY) +if ENV["LOCAL_DEBUG"] == "true": + azure_credential = DefaultAzureCredential() +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) # Comment these two lines out if using keys, set your API key in the OPENAI_API_KEY environment variable instead -# openai.api_type = "azure_ad" -# openai_token = azure_credential.get_token("https://cognitiveservices.azure.com/.default") +openai.api_type = "azure_ad" +openai_token = azure_credential.get_token("https://cognitiveservices.azure.com/.default") openai.api_key = ENV["AZURE_OPENAI_SERVICE_KEY"] # Setup StatusLog to allow access to CosmosDB for logging @@ -165,7 +168,7 @@ class StatusResponse(pydantic.BaseModel): ) blob_client = BlobServiceClient( account_url=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], - credential=ENV["AZURE_BLOB_STORAGE_KEY"], + credential=azure_credential, ) blob_container = blob_client.get_container_client(ENV["AZURE_BLOB_STORAGE_CONTAINER"]) @@ -342,33 +345,16 @@ async def chat(request: Request): -@app.get("/getblobclienturl") -async def get_blob_client_url(): - """Get a URL for a file in Blob Storage with SAS token. +@app.get("/getblobclient") +async def get_blob_client(): + """Get an authenticated blob client. - This function generates a Shared Access Signature (SAS) token for accessing a file in Blob Storage. - The generated URL includes the SAS token as a query parameter. + This function generates a Blob Client for accessing the Blob Storage Account. Returns: - dict: A dictionary containing the URL with the SAS token. + dict: A dictionary containing the Blob Client object. """ - sas_token = generate_account_sas( - ENV["AZURE_BLOB_STORAGE_ACCOUNT"], - ENV["AZURE_BLOB_STORAGE_KEY"], - resource_types=ResourceTypes(object=True, service=True, container=True), - permission=AccountSasPermissions( - read=True, - write=True, - list=True, - delete=False, - add=True, - create=True, - update=True, - process=False, - ), - expiry=datetime.utcnow() + timedelta(hours=1), - ) - return {"url": f"{blob_client.url}?{sas_token}"} + return {"client": blob_client} @app.post("/getalluploadstatus") async def get_all_upload_status(request: Request): @@ -396,7 +382,7 @@ async def get_all_upload_status(request: Request): # retrieve tags for each file # Initialize an empty list to hold the tags items = [] - cosmos_client = CosmosClient(url=statusLog._url, credential=statusLog._key) + cosmos_client = CosmosClient(url=statusLog._url, credential=statusLog._key, consistency_level='Session') database = cosmos_client.get_database_client(statusLog._database_name) container = database.get_container_client(statusLog._container_name) query_string = "SELECT DISTINCT VALUE t FROM c JOIN t IN c.tags" @@ -534,7 +520,7 @@ async def get_tags(request: Request): try: # Initialize an empty list to hold the tags items = [] - cosmos_client = CosmosClient(url=statusLog._url, credential=statusLog._key) + cosmos_client = CosmosClient(url=statusLog._url, credential=statusLog._key, consistency_level='Session') database = cosmos_client.get_database_client(statusLog._database_name) container = database.get_container_client(statusLog._container_name) query_string = "SELECT DISTINCT VALUE t FROM c JOIN t IN c.tags" diff --git a/app/backend/requirements.txt b/app/backend/requirements.txt index 92309ce06..48ab12972 100644 --- a/app/backend/requirements.txt +++ b/app/backend/requirements.txt @@ -7,7 +7,7 @@ openai==1.35.8 # azure-search-documents==11.4.0 azure-search-documents==11.4.0b11 azure-storage-blob==12.16.0 -azure-cosmos == 4.3.1 +azure-cosmos == 4.7.0 tiktoken == 0.7.0 fastapi == 0.109.1 fastapi-utils == 0.2.1 diff --git a/app/backend/testsuite.py b/app/backend/testsuite.py index 9a1c56e9c..66308eef1 100644 --- a/app/backend/testsuite.py +++ b/app/backend/testsuite.py @@ -2,6 +2,7 @@ import re import pytest from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient +from azure.identity import DefaultAzureCredential import os from fastapi.testclient import TestClient from dotenv import load_dotenv @@ -13,6 +14,8 @@ load_dotenv(dotenv_path=f'../../scripts/environments/infrastructure.debug.env') +azure_credentials = DefaultAzureCredential() + from app import app client = TestClient(app) @@ -210,10 +213,10 @@ def test_work_compare_web_chat_api(): assert "Satya" in content or "I am not sure." in content -def test_get_blob_client_url(): - response = client.get("/getblobclienturl") +def test_get_blob_client(): + response = client.get("/getblobclient") assert response.status_code == 200 - assert "blob.core.windows.net" in response.json()["url"] + assert "blob.core.windows.net" in response.json()["client"].url def test_get_all_upload_status(): response = client.post("/getalluploadstatus", json={ @@ -312,10 +315,9 @@ def test_get_feature_flags(): assert response.json() == expected_response def test_upload_blob(): - account_name = os.getenv("AZURE_BLOB_STORAGE_ACCOUNT") - account_key = os.getenv("AZURE_BLOB_STORAGE_KEY") + storage_account_url=os.getenv("BLOB_STORAGE_ACCOUNT_ENDPOINT") container_name = os.getenv("AZURE_BLOB_STORAGE_UPLOAD_CONTAINER") - blob_service_client = BlobServiceClient(account_url=f"https://{account_name}.blob.core.windows.net", credential=account_key) + blob_service_client = BlobServiceClient(account_url=storage_account_url, credential=azure_credentials) # Create a container client container_client = blob_service_client.get_container_client(container_name) diff --git a/app/enrichment/app.py b/app/enrichment/app.py index 6487b96e8..34b12c103 100644 --- a/app/enrichment/app.py +++ b/app/enrichment/app.py @@ -15,6 +15,7 @@ from azure.storage.queue import QueueClient, TextBase64EncodePolicy from azure.search.documents import SearchClient from azure.core.credentials import AzureKeyCredential +from azure.identity import ManagedIdentityCredential from data_model import (EmbeddingResponse, ModelInfo, ModelListResponse, StatusResponse) from fastapi import FastAPI, HTTPException @@ -32,7 +33,6 @@ # === ENV Setup === ENV = { - "AZURE_BLOB_STORAGE_KEY": None, "EMBEDDINGS_QUEUE": None, "LOG_LEVEL": "DEBUG", # Will be overwritten by LOG_LEVEL in Environment "DEQUEUE_MESSAGE_BATCH_SIZE": 1, @@ -53,7 +53,6 @@ "AZURE_SEARCH_INDEX": None, "AZURE_SEARCH_SERVICE_KEY": None, "AZURE_SEARCH_SERVICE": None, - "BLOB_CONNECTION_STRING": None, "TARGET_EMBEDDINGS_MODEL": None, "EMBEDDING_VECTOR_SIZE": None, "AZURE_SEARCH_SERVICE_ENDPOINT": None, @@ -118,7 +117,6 @@ def encode(self, texts) -> None: utilities_helper = UtilitiesHelper( azure_blob_storage_account=ENV["AZURE_BLOB_STORAGE_ACCOUNT"], azure_blob_storage_endpoint=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], - azure_blob_storage_key=ENV["AZURE_BLOB_STORAGE_KEY"], ) statusLog = StatusLog(ENV["COSMOSDB_URL"], ENV["COSMOSDB_KEY"], ENV["COSMOSDB_LOG_DATABASE_NAME"], ENV["COSMOSDB_LOG_CONTAINER_NAME"]) @@ -126,6 +124,8 @@ def encode(self, texts) -> None: start_time = datetime.now() +azure_credential = ManagedIdentityCredential() + IS_READY = False #download models @@ -279,16 +279,13 @@ def get_tags(blob_path): # Remove the container prefix path_parts = blob_path.split('/') blob_path = '/'.join(path_parts[1:]) - - blob_service_client = BlobServiceClient.from_connection_string(ENV["BLOB_CONNECTION_STRING"]) - # container_client = blob_service_client.get_container_client(ENV["AZURE_BLOB_STORAGE_CONTAINER"]) + + blob_service_client = BlobServiceClient(ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + credential=azure_credential) blob_client = blob_service_client.get_blob_client( container=ENV["AZURE_BLOB_STORAGE_UPLOAD_CONTAINER"], blob=blob_path) - - # blob_client = container_client.get_blob_client( - # blob_client = container_client.get_blob_client(container_client=container_client, blob=blob_path) blob_properties = blob_client.get_blob_properties() tags = blob_properties.metadata.get("tags") if tags != '' and tags is not None: @@ -308,9 +305,9 @@ def poll_queue() -> None: log.debug("Skipping poll_queue call, models not yet loaded") return - queue_client = QueueClient.from_connection_string( - conn_str=ENV["BLOB_CONNECTION_STRING"], queue_name=ENV["EMBEDDINGS_QUEUE"] - ) + queue_client = QueueClient(account_url=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + queue_name=ENV["EMBEDDINGS_QUEUE"], + credential=azure_credential) log.debug("Polling embeddings queue for messages...") response = queue_client.receive_messages(max_messages=int(ENV["DEQUEUE_MESSAGE_BATCH_SIZE"])) @@ -336,7 +333,8 @@ def poll_queue() -> None: statusLog.upsert_document(blob_path, f'Embeddings process started with model {target_embeddings_model}', StatusClassification.INFO, State.PROCESSING) file_name, file_extension, file_directory = utilities_helper.get_filename_and_extension(blob_path) chunk_folder_path = file_directory + file_name + file_extension - blob_service_client = BlobServiceClient.from_connection_string(ENV["BLOB_CONNECTION_STRING"]) + blob_service_client = BlobServiceClient(ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + credential=azure_credential) container_client = blob_service_client.get_container_client(ENV["AZURE_BLOB_STORAGE_CONTAINER"]) index_chunks = [] @@ -432,10 +430,10 @@ def poll_queue() -> None: if requeue_count <= int(ENV["MAX_EMBEDDING_REQUEUE_COUNT"]): message_json['embeddings_queued_count'] = requeue_count # Requeue with a random backoff within limits - queue_client = QueueClient.from_connection_string( - ENV["BLOB_CONNECTION_STRING"], - ENV["EMBEDDINGS_QUEUE"], - message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + queue_name=ENV["EMBEDDINGS_QUEUE"], + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_string = json.dumps(message_json) max_seconds = int(ENV["EMBEDDING_REQUEUE_BACKOFF"]) * (requeue_count**2) backoff = random.randint( diff --git a/app/enrichment/requirements.txt b/app/enrichment/requirements.txt index 3cffb9cb5..e5587e720 100644 --- a/app/enrichment/requirements.txt +++ b/app/enrichment/requirements.txt @@ -10,7 +10,8 @@ uvicorn == 0.23.2 azure-storage-queue == 12.6.0 azure-storage-blob==12.16.0 azure.search.documents==11.4.0b11 -azure-cosmos == 4.3.1 -azure-core == 1.26.4 +azure-cosmos == 4.7.0 +azure-core == 1.30.2 +azure-identity==1.16.1 tenacity == 8.2.3 openai==1.17.0 diff --git a/app/frontend/src/api/api.ts b/app/frontend/src/api/api.ts index 91b072f15..80eb9518f 100644 --- a/app/frontend/src/api/api.ts +++ b/app/frontend/src/api/api.ts @@ -3,7 +3,7 @@ import { ChatResponse, ChatRequest, - BlobClientUrlResponse, + BlobClientResponse, AllFilesUploadStatus, GetUploadStatusRequest, GetInfoResponse, @@ -64,20 +64,20 @@ export function getCitationFilePath(citation: string): string { return `${encodeURIComponent(citation)}`; } -export async function getBlobClientUrl(): Promise { - const response = await fetch("/getblobclienturl", { +export async function getBlobClient(): Promise { + const response = await fetch("/getblobclient", { method: "GET", headers: { "Content-Type": "application/json" } }); - const parsedResponse: BlobClientUrlResponse = await response.json(); + const parsedResponse: BlobClientResponse = await response.json(); if (response.status > 299 || !response.ok) { throw Error(parsedResponse.error || "Unknown error"); } - return parsedResponse.url; + return parsedResponse.client; } export async function getAllUploadStatus(options: GetUploadStatusRequest): Promise { diff --git a/app/frontend/src/api/models.ts b/app/frontend/src/api/models.ts index 34a13191e..1b498b02f 100644 --- a/app/frontend/src/api/models.ts +++ b/app/frontend/src/api/models.ts @@ -67,8 +67,8 @@ export type ChatRequest = { thought_chain: { [key: string]: string }; }; -export type BlobClientUrlResponse = { - url: string; +export type BlobClientResponse = { + client: any; error?: string; }; diff --git a/app/frontend/src/components/FolderPicker/FolderPicker.tsx b/app/frontend/src/components/FolderPicker/FolderPicker.tsx index 1fff39175..7cc62531e 100644 --- a/app/frontend/src/components/FolderPicker/FolderPicker.tsx +++ b/app/frontend/src/components/FolderPicker/FolderPicker.tsx @@ -20,7 +20,7 @@ import { IIconProps } from '@fluentui/react'; import { IButtonProps } from '@fluentui/react/lib/Button'; import { BlobServiceClient } from "@azure/storage-blob"; -import { getBlobClientUrl } from "../../api"; +import { getBlobClient } from "../../api"; import styles from "./FolderPicker.module.css"; var allowNewFolders = false; @@ -85,8 +85,7 @@ export const FolderPicker = ({allowFolderCreation, onSelectedKeyChange, preSelec async function fetchBlobFolderData() { try { - const blobClientUrl = await getBlobClientUrl(); - const blobServiceClient = new BlobServiceClient(blobClientUrl); + const blobServiceClient = await getBlobClient() as BlobServiceClient; var containerClient = blobServiceClient.getContainerClient("upload"); const delimiter = "/"; const prefix = ""; diff --git a/app/frontend/src/components/filepicker/file-picker.tsx b/app/frontend/src/components/filepicker/file-picker.tsx index d4ca1bf05..30a625fd9 100644 --- a/app/frontend/src/components/filepicker/file-picker.tsx +++ b/app/frontend/src/components/filepicker/file-picker.tsx @@ -8,7 +8,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { DropZone } from "./drop-zone" import styles from "./file-picker.module.css"; import { FilesList } from "./files-list"; -import { getBlobClientUrl, logStatus, StatusLogClassification, StatusLogEntry, StatusLogState } from "../../api" +import { getBlobClient, logStatus, StatusLogClassification, StatusLogEntry, StatusLogState } from "../../api" interface Props { folderPath: string; @@ -50,8 +50,7 @@ const FilePicker = ({folderPath, tags}: Props) => { setUploadStarted(true); // create an instance of the BlobServiceClient - const blobClientUrl = await getBlobClientUrl(); - const blobServiceClient = new BlobServiceClient(blobClientUrl); + const blobServiceClient = await getBlobClient() as BlobServiceClient; const containerClient = blobServiceClient.getContainerClient("upload"); var counter = 1; diff --git a/docs/function_debug.md b/docs/function_debug.md index 30491540b..f78930431 100644 --- a/docs/function_debug.md +++ b/docs/function_debug.md @@ -26,10 +26,8 @@ Next you will need to create local configuration values that are used by the fun "AZURE_FORM_RECOGNIZER_KEY": "", "BLOB_STORAGE_ACCOUNT": "", "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME": "content", - "AZURE_BLOB_STORAGE_KEY": "", "CHUNK_TARGET_SIZE": "750", "FR_API_VERSION": "2023-02-28-preview", - "BLOB_CONNECTION_STRING": "", "CHUNK_TARGET_SIZE": "750", "FR_API_VERSION": "2022-08-31", "MAX_POLLING_REQUEUE_COUNT": "10", diff --git a/functions/FileDeletion/__init__.py b/functions/FileDeletion/__init__.py index 1f7194914..00772eb40 100644 --- a/functions/FileDeletion/__init__.py +++ b/functions/FileDeletion/__init__.py @@ -9,9 +9,10 @@ from azure.core.credentials import AzureKeyCredential from azure.search.documents import SearchClient from azure.storage.blob import BlobServiceClient +from azure.identity import ManagedIdentityCredential from shared_code.status_log import State, StatusClassification, StatusLog -blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] +azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] blob_storage_account_upload_container_name = os.environ[ "BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] blob_storage_account_output_container_name = os.environ[ @@ -29,6 +30,8 @@ cosmosdb_log_database_name, cosmosdb_log_container_name) +azure_credential = ManagedIdentityCredential() + def chunks(data, size): '''max number of blobs to delete in one request is 256, so this breaks chunks the dictionary''' @@ -131,7 +134,7 @@ def main(mytimer: func.TimerRequest) -> None: logging.info('Python timer trigger function ran at %s', utc_timestamp) # Create Blob Service Client - blob_service_client = BlobServiceClient.from_connection_string(blob_connection_string) + blob_service_client = BlobServiceClient(account_url=azure_blob_storage_endpoint, credential=azure_credential) deleted_blobs = get_deleted_blobs(blob_service_client) diff --git a/functions/FileFormRecPollingPDF/__init__.py b/functions/FileFormRecPollingPDF/__init__.py index 6e041df6e..106a8c54c 100644 --- a/functions/FileFormRecPollingPDF/__init__.py +++ b/functions/FileFormRecPollingPDF/__init__.py @@ -1,18 +1,18 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. -import azure.functions as func -from azure.storage.queue import QueueClient, TextBase64EncodePolicy import logging import os import json -import requests -from azure.storage.queue import QueueClient, TextBase64EncodePolicy -from shared_code.status_log import StatusLog, State, StatusClassification -from shared_code.utilities import Utilities, MediaType import random from collections import namedtuple import time +import azure.functions as func +from azure.storage.queue import QueueClient, TextBase64EncodePolicy +from azure.identity import ManagedIdentityCredential +import requests +from shared_code.status_log import StatusLog, State, StatusClassification +from shared_code.utilities import Utilities, MediaType from requests.exceptions import RequestException from tenacity import retry, stop_after_attempt, wait_fixed @@ -23,15 +23,12 @@ def string_to_bool(s): azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] azure_blob_drop_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] -azure_blob_storage_key = os.environ["AZURE_BLOB_STORAGE_KEY"] -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] azure_blob_log_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME"] CHUNK_TARGET_SIZE = int(os.environ["CHUNK_TARGET_SIZE"]) FR_API_VERSION = os.environ["FR_API_VERSION"] # ALL or Custom page numbers for multi-page documents(PDF/TIFF). Input the page numbers and/or # ranges of pages you want to get in the result. For a range of pages, use a hyphen, like pages="1-3, 5-6". # Separate each page number or range with a comma. -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] cosmosdb_url = os.environ["COSMOSDB_URL"] cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] @@ -51,10 +48,10 @@ def string_to_bool(s): enableDevCode = string_to_bool(os.environ["ENABLE_DEV_CODE"]) function_name = "FileFormRecPollingPDF" -utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, azure_blob_storage_key) +utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container) FR_MODEL = "prebuilt-layout" - +azure_credential = ManagedIdentityCredential() def main(msg: func.QueueMessage) -> None: @@ -105,7 +102,10 @@ def main(msg: func.QueueMessage) -> None: statusLog.upsert_document(blob_name, f'{function_name} - Chunking complete, {chunk_count} chunks created.', StatusClassification.DEBUG) # submit message to the enrichment queue to continue processing - queue_client = QueueClient.from_connection_string(azure_blob_connection_string, queue_name=text_enrichment_queue, message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=text_enrichment_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_json["text_enrichment_queued_count"] = 1 message_string = json.dumps(message_json) queue_client.send_message(message_string) @@ -119,7 +119,10 @@ def main(msg: func.QueueMessage) -> None: queued_count += 1 message_json['polling_queue_count'] = queued_count statusLog.upsert_document(blob_name, f"{function_name} - FR has not completed processing, requeuing. Polling back off of attempt {queued_count} of {max_polling_requeue_count} for {backoff} seconds", StatusClassification.DEBUG, State.QUEUED) - queue_client = QueueClient.from_connection_string(azure_blob_connection_string, queue_name=pdf_polling_queue, message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=pdf_polling_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_json_str = json.dumps(message_json) queue_client.send_message(message_json_str, visibility_timeout=backoff) else: @@ -128,7 +131,10 @@ def main(msg: func.QueueMessage) -> None: # unexpected status returned by FR, such as internal capacity overload, so requeue if submit_queued_count < max_submit_requeue_count: statusLog.upsert_document(blob_name, f'{function_name} - unhandled response from Form Recognizer- code: {response.status_code} status: {response_status} - text: {response.text}. Document will be resubmitted', StatusClassification.ERROR) - queue_client = QueueClient.from_connection_string(azure_blob_connection_string, pdf_submit_queue, message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=pdf_submit_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) submit_queued_count += 1 message_json["submit_queued_count"] = submit_queued_count message_string = json.dumps(message_json) diff --git a/functions/FileFormRecPollingPDF/function.json b/functions/FileFormRecPollingPDF/function.json index 8bbc1e5d6..664548722 100644 --- a/functions/FileFormRecPollingPDF/function.json +++ b/functions/FileFormRecPollingPDF/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "pdf-polling-queue", - "connection": "BLOB_CONNECTION_STRING" + "connection": "STORAGE_CONNECTION_STRING" } ] } \ No newline at end of file diff --git a/functions/FileFormRecSubmissionPDF/__init__.py b/functions/FileFormRecSubmissionPDF/__init__.py index 0e7d739a3..8a561b1c1 100644 --- a/functions/FileFormRecSubmissionPDF/__init__.py +++ b/functions/FileFormRecSubmissionPDF/__init__.py @@ -8,6 +8,7 @@ import azure.functions as func import requests from azure.storage.queue import QueueClient, TextBase64EncodePolicy +from azure.identity import ManagedIdentityCredential from shared_code.status_log import State, StatusClassification, StatusLog from shared_code.utilities import Utilities @@ -19,8 +20,6 @@ azure_blob_content_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] -azure_blob_storage_key = os.environ["AZURE_BLOB_STORAGE_KEY"] -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] cosmosdb_url = os.environ["COSMOSDB_URL"] cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] @@ -40,11 +39,11 @@ azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, - azure_blob_storage_key, ) FUNCTION_NAME = "FileFormRecSubmissionPDF" FR_MODEL = "prebuilt-layout" +azure_credential = ManagedIdentityCredential() def main(msg: func.QueueMessage) -> None: '''This function is triggered by a message in the pdf-submit-queue. @@ -108,11 +107,10 @@ def main(msg: func.QueueMessage) -> None: result_id = response.headers.get("apim-request-id") message_json["FR_resultId"] = result_id message_json["polling_queue_count"] = 1 - queue_client = QueueClient.from_connection_string( - azure_blob_connection_string, - queue_name=pdf_polling_queue, - message_encode_policy=TextBase64EncodePolicy(), - ) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=pdf_polling_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_json_str = json.dumps(message_json) queue_client.send_message( message_json_str, visibility_timeout=poll_queue_submit_backoff @@ -139,11 +137,10 @@ def main(msg: func.QueueMessage) -> None: f"{FUNCTION_NAME} - Throttled on PDF submission to FR, requeuing. Back off of {backoff} seconds", StatusClassification.DEBUG, ) - queue_client = QueueClient.from_connection_string( - azure_blob_connection_string, - queue_name=pdf_submit_queue, - message_encode_policy=TextBase64EncodePolicy(), - ) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=pdf_submit_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_json_str = json.dumps(message_json) queue_client.send_message(message_json_str, visibility_timeout=backoff) statusLog.upsert_document( diff --git a/functions/FileFormRecSubmissionPDF/function.json b/functions/FileFormRecSubmissionPDF/function.json index 7e187de44..cda624b76 100644 --- a/functions/FileFormRecSubmissionPDF/function.json +++ b/functions/FileFormRecSubmissionPDF/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "pdf-submit-queue", - "connection": "BLOB_CONNECTION_STRING" + "connection": "STORAGE_CONNECTION_STRING" } ] } \ No newline at end of file diff --git a/functions/FileLayoutParsingOther/__init__.py b/functions/FileLayoutParsingOther/__init__.py index d9c8a0b8f..445ca5253 100644 --- a/functions/FileLayoutParsingOther/__init__.py +++ b/functions/FileLayoutParsingOther/__init__.py @@ -9,6 +9,7 @@ import azure.functions as func from azure.storage.blob import generate_blob_sas from azure.storage.queue import QueueClient, TextBase64EncodePolicy +from azure.identity import ManagedIdentityCredential from shared_code.status_log import StatusLog, State, StatusClassification from shared_code.utilities import Utilities, MediaType @@ -18,8 +19,6 @@ azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] azure_blob_drop_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] -azure_blob_storage_key = os.environ["AZURE_BLOB_STORAGE_KEY"] -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] azure_blob_log_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME"] cosmosdb_url = os.environ["COSMOSDB_URL"] cosmosdb_key = os.environ["COSMOSDB_KEY"] @@ -32,9 +31,11 @@ CHUNK_TARGET_SIZE = int(os.environ["CHUNK_TARGET_SIZE"]) -utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, azure_blob_storage_key) +utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container) function_name = "FileLayoutParsingOther" +azure_credential = ManagedIdentityCredential() + class UnstructuredError(Exception): pass @@ -189,7 +190,10 @@ def main(msg: func.QueueMessage) -> None: statusLog.upsert_document(blob_name, f'{function_name} - chunking stored.', StatusClassification.DEBUG) # submit message to the text enrichment queue to continue processing - queue_client = QueueClient.from_connection_string(azure_blob_connection_string, queue_name=text_enrichment_queue, message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=text_enrichment_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_json["text_enrichment_queued_count"] = 1 message_string = json.dumps(message_json) queue_client.send_message(message_string) diff --git a/functions/FileLayoutParsingOther/function.json b/functions/FileLayoutParsingOther/function.json index eb220c214..cc4480cda 100644 --- a/functions/FileLayoutParsingOther/function.json +++ b/functions/FileLayoutParsingOther/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "non-pdf-submit-queue", - "connection": "BLOB_CONNECTION_STRING" + "connection": "STORAGE_CONNECTION_STRING" } ] } \ No newline at end of file diff --git a/functions/FileUploadedFunc/__init__.py b/functions/FileUploadedFunc/__init__.py index 75328fc62..341fde7f2 100644 --- a/functions/FileUploadedFunc/__init__.py +++ b/functions/FileUploadedFunc/__init__.py @@ -8,15 +8,15 @@ import time from shared_code.status_log import StatusLog, State, StatusClassification import azure.functions as func -from azure.storage.blob import BlobServiceClient, generate_blob_sas +from azure.storage.blob import BlobServiceClient from azure.storage.queue import QueueClient, TextBase64EncodePolicy +from azure.identity import ManagedIdentityCredential from azure.search.documents import SearchClient from azure.core.credentials import AzureKeyCredential from shared_code.utilities_helper import UtilitiesHelper from urllib.parse import unquote -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] cosmosdb_url = os.environ["COSMOSDB_URL"] cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] @@ -29,7 +29,6 @@ max_seconds_hide_on_upload = int(os.environ["MAX_SECONDS_HIDE_ON_UPLOAD"]) azure_blob_content_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] -azure_blob_key = os.environ["AZURE_BLOB_STORAGE_KEY"] azure_blob_upload_container = os.environ["BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] azure_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_search_service_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] @@ -44,10 +43,10 @@ utilities_helper = UtilitiesHelper( azure_blob_storage_account=azure_storage_account, azure_blob_storage_endpoint=azure_blob_endpoint, - azure_blob_storage_key=azure_blob_key, ) statusLog = StatusLog(cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name) +azure_credential = ManagedIdentityCredential() def get_tags_and_upload_to_cosmos(blob_service_client, blob_path): """ Gets the tags from the blob metadata and uploads them to cosmos db""" @@ -112,8 +111,8 @@ def main(myblob: func.InputStream): blob_client = BlobServiceClient( account_url=azure_blob_endpoint, - credential=azure_blob_key, - ) + credential=azure_credential, + ) myblob_filename = myblob.name.split("/", 1)[1] # Check if the blob has been marked as 'do not process' and abort if so @@ -158,12 +157,15 @@ def main(myblob: func.InputStream): logging.debug("No items to delete from AI Search index.") # write tags to cosmos db once per file/message - blob_service_client = BlobServiceClient.from_connection_string(azure_blob_connection_string) + blob_service_client = BlobServiceClient(account_url=azure_blob_endpoint, credential=azure_credential) upload_container_client = blob_service_client.get_container_client(azure_blob_upload_container) tag_list = get_tags_and_upload_to_cosmos(upload_container_client, myblob.name) # Queue message with a random backoff so as not to put the next function under unnecessary load - queue_client = QueueClient.from_connection_string(azure_blob_connection_string, queue_name, message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=azure_blob_endpoint, + queue_name=queue_name, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) backoff = random.randint(1, max_seconds_hide_on_upload) queue_client.send_message(message_string, visibility_timeout = backoff) statusLog.upsert_document(myblob.name, f'{function_name} - {file_extension} file sent to submit queue. Visible in {backoff} seconds', StatusClassification.DEBUG, State.QUEUED) diff --git a/functions/FileUploadedFunc/function.json b/functions/FileUploadedFunc/function.json index cd8bd4e7f..ee7415f7b 100644 --- a/functions/FileUploadedFunc/function.json +++ b/functions/FileUploadedFunc/function.json @@ -6,7 +6,7 @@ "type": "blobTrigger", "direction": "in", "path": "upload", - "connection": "BLOB_CONNECTION_STRING" + "connection": "STORAGE_CONNECTION_STRING" } ], "retry": { diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index ee99e86f1..7081eb4a1 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -6,6 +6,7 @@ import azure.functions as func import requests from azure.storage.blob import BlobServiceClient +from azure.identity import ManagedIdentityCredential from shared_code.status_log import State, StatusClassification, StatusLog from shared_code.utilities import Utilities, MediaType from azure.search.documents import SearchClient @@ -21,8 +22,6 @@ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] -azure_blob_storage_key = os.environ["AZURE_BLOB_STORAGE_KEY"] -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] azure_blob_content_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] @@ -105,13 +104,13 @@ FUNCTION_NAME = "ImageEnrichment" +azure_credential = ManagedIdentityCredential() utilities = Utilities( azure_blob_storage_account=azure_blob_storage_account, azure_blob_storage_endpoint=azure_blob_storage_endpoint, azure_blob_drop_storage_container=azure_blob_drop_storage_container, azure_blob_content_storage_container=azure_blob_content_storage_container, - azure_blob_storage_key=azure_blob_storage_key ) @@ -296,7 +295,7 @@ def main(msg: func.QueueMessage) -> None: # Get the tags from metadata on the blob path = file_directory + file_name + file_extension - blob_service_client = BlobServiceClient.from_connection_string(azure_blob_connection_string) + blob_service_client = BlobServiceClient(account_url=azure_blob_storage_endpoint, credential=azure_credential) blob_client = blob_service_client.get_blob_client(container=azure_blob_drop_storage_container, blob=path) blob_properties = blob_client.get_blob_properties() tags = blob_properties.metadata.get("tags") diff --git a/functions/ImageEnrichment/function.json b/functions/ImageEnrichment/function.json index d59dec889..9b17f12de 100644 --- a/functions/ImageEnrichment/function.json +++ b/functions/ImageEnrichment/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "image-enrichment-queue", - "connection": "BLOB_CONNECTION_STRING" + "connection": "STORAGE_CONNECTION_STRING" } ] } \ No newline at end of file diff --git a/functions/TextEnrichment/__init__.py b/functions/TextEnrichment/__init__.py index c570fbe07..4e4376aef 100644 --- a/functions/TextEnrichment/__init__.py +++ b/functions/TextEnrichment/__init__.py @@ -1,6 +1,7 @@ import logging import azure.functions as func from azure.storage.queue import QueueClient, TextBase64EncodePolicy +from azure.identity import ManagedIdentityCredential from azure.storage.blob import BlobServiceClient from shared_code.utilities import Utilities import os @@ -20,8 +21,6 @@ azure_blob_content_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] -azure_blob_storage_key = os.environ["AZURE_BLOB_STORAGE_KEY"] -azure_blob_connection_string = os.environ["BLOB_CONNECTION_STRING"] azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] cosmosdb_url = os.environ["COSMOSDB_URL"] @@ -50,12 +49,13 @@ azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, - azure_blob_storage_key, ) statusLog = StatusLog( cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name -) +) + +azure_credential = ManagedIdentityCredential() def main(msg: func.QueueMessage) -> None: '''This function is triggered by a message in the text-enrichment-queue. @@ -86,7 +86,10 @@ def main(msg: func.QueueMessage) -> None: # Detect language of the document chunk_content = '' - blob_service_client = BlobServiceClient.from_connection_string(azure_blob_connection_string) + blob_service_client = BlobServiceClient( + account_url=azure_blob_storage_endpoint, + credential=azure_credential, + ) container_client = blob_service_client.get_container_client(azure_blob_content_storage_container) # Iterate over the chunks in the container, retrieving up to the max number of chars required chunk_list = container_client.list_blobs(name_starts_with=chunk_folder_path) @@ -217,7 +220,10 @@ def main(msg: func.QueueMessage) -> None: block_blob_client.upload_blob(json_str, overwrite=True) # Queue message to embeddings queue for downstream processing - queue_client = QueueClient.from_connection_string(azure_blob_connection_string, queueName, message_encode_policy=TextBase64EncodePolicy()) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=queueName, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) embeddings_queue_backoff = random.randint(1, 60) message_string = json.dumps(message_json) queue_client.send_message(message_string, visibility_timeout = embeddings_queue_backoff) @@ -287,11 +293,10 @@ def requeue(response, message_json): ) queued_count += 1 message_json["text_enrichment_queued_count"] = queued_count - queue_client = QueueClient.from_connection_string( - azure_blob_connection_string, - queue_name=text_enrichment_queue, - message_encode_policy=TextBase64EncodePolicy(), - ) + queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_name=text_enrichment_queue, + credential=azure_credential, + message_encode_policy=TextBase64EncodePolicy()) message_json_str = json.dumps(message_json) queue_client.send_message(message_json_str, visibility_timeout=backoff) statusLog.upsert_document( diff --git a/functions/TextEnrichment/function.json b/functions/TextEnrichment/function.json index 4bcea1ca5..8a4dde318 100644 --- a/functions/TextEnrichment/function.json +++ b/functions/TextEnrichment/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "text-enrichment-queue", - "connection": "BLOB_CONNECTION_STRING" + "connection": "STORAGE_CONNECTION_STRING" } ] } \ No newline at end of file diff --git a/functions/requirements.txt b/functions/requirements.txt index 99db4d606..973046950 100644 --- a/functions/requirements.txt +++ b/functions/requirements.txt @@ -8,9 +8,9 @@ azure-functions == 1.17.0 tiktoken==0.4.0 azure.ai.formrecognizer==3.2.1 azure-storage-blob==12.16.0 -azure-core == 1.26.4 +azure-core == 1.30.2 lxml == 4.9.2 -azure-cosmos == 4.3.1 +azure-cosmos == 4.7.0 azure-storage-queue == 12.6.0 nltk == 3.8.1 tenacity == 8.2.3 @@ -18,4 +18,5 @@ azure-ai-vision == 0.15.1b1 unstructured[csv,doc,docx,email,html,md,msg,ppt,pptx,text,xlsx,xml] == 0.12.5 pyoo == 1.4 azure-search-documents == 11.4.0b11 -beautifulsoup4 == 4.12.2 \ No newline at end of file +beautifulsoup4 == 4.12.2 +azure-identity==1.16.1 \ No newline at end of file diff --git a/functions/shared_code/status_log.py b/functions/shared_code/status_log.py index b6445426a..35bac4053 100644 --- a/functions/shared_code/status_log.py +++ b/functions/shared_code/status_log.py @@ -44,7 +44,7 @@ def __init__(self, url, key, database_name, container_name): self._key = key self._database_name = database_name self._container_name = container_name - self.cosmos_client = CosmosClient(url=self._url, credential=self._key) + self.cosmos_client = CosmosClient(url=self._url, credential=self._key, consistency_level='Session') self._log_document = {} # Select a database (will create it if it doesn't exist) diff --git a/functions/shared_code/utilities.py b/functions/shared_code/utilities.py index 45adfd7a7..7af75d30b 100644 --- a/functions/shared_code/utilities.py +++ b/functions/shared_code/utilities.py @@ -9,6 +9,7 @@ import zipfile import os from azure.storage.blob import BlobServiceClient +from azure.identity import ManagedIdentityCredential from shared_code.utilities_helper import UtilitiesHelper from nltk.tokenize import sent_tokenize import tiktoken @@ -17,6 +18,8 @@ nltk.download('punkt') from bs4 import BeautifulSoup +azure_credential = ManagedIdentityCredential() + punkt_dir = os.path.join(nltk.data.path[0], 'tokenizers/punkt') # Check if the 'punkt' directory exists @@ -69,17 +72,14 @@ def __init__(self, azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, - azure_blob_content_storage_container, - azure_blob_storage_key + azure_blob_content_storage_container ): self.azure_blob_storage_account = azure_blob_storage_account self.azure_blob_storage_endpoint = azure_blob_storage_endpoint self.azure_blob_drop_storage_container = azure_blob_drop_storage_container self.azure_blob_content_storage_container = azure_blob_content_storage_container - self.azure_blob_storage_key = azure_blob_storage_key self.utilities_helper = UtilitiesHelper(azure_blob_storage_account, - azure_blob_storage_endpoint, - azure_blob_storage_key) + azure_blob_storage_endpoint) def write_blob(self, output_container, content, output_filename, folder_set=""): """ Function to write a generic blob """ @@ -87,7 +87,8 @@ def write_blob(self, output_container, content, output_filename, folder_set=""): # Get path and file name minus the root container blob_service_client = BlobServiceClient( self.azure_blob_storage_endpoint, - self.azure_blob_storage_key) + credential=azure_credential + ) block_blob_client = blob_service_client.get_blob_client( container=output_container, blob=f'{folder_set}{output_filename}') block_blob_client.upload_blob(content, overwrite=True) @@ -107,50 +108,6 @@ def get_blob_and_sas(self, blob_path): """ Function to retrieve the uri and sas token for a given blob in azure storage""" return self.utilities_helper.get_blob_and_sas(blob_path) - # def table_to_html(self, table): - # """ Function to take an output FR table json structure and convert to HTML """ - # header_processing_complete = False - # table_html = "" - # rows = [sorted([cell for cell in table["cells"] if cell["rowIndex"] == i], - # key=lambda cell: cell["columnIndex"]) for i in range(table["rowCount"])] - # for row_cells in rows: - # is_row_a_header = False - # row_html = "" - # for cell in row_cells: - # tag = "td" - # #if hasattr(cell, 'kind'): - # if 'kind' in cell: - # if (cell["kind"] == "columnHeader" or cell["kind"] == "rowHeader"): - # tag = "th" - # if (cell["kind"] == "columnHeader"): - # is_row_a_header = True - # else: - # # we have encountered a cell that isn't tagged as a header, - # # so assume we have now rerached regular table cells - # header_processing_complete = True - # cell_spans = "" - # #if hasattr(cell, 'columnSpan'): - # if 'columnSpan' in cell: - # if cell["columnSpan"] > 1: - # cell_spans += f" colSpan={cell['columnSpan']}" - # #if hasattr(cell, 'rowSpan'): - # if 'rowSpan' in cell: - # if cell["rowSpan"] > 1: - # cell_spans += f" rowSpan={cell['rowSpan']}" - # row_html += f"<{tag}{cell_spans}>{html.escape(cell['content'])}" - # row_html += "" - - # if is_row_a_header and header_processing_complete == False: - # row_html = "" + row_html + "" - # table_html += row_html - # table_html += "
" - # return table_html - - - - - - def table_to_html(self, table): """ Function to take an output FR table json structure and convert to HTML """ table_html = "" @@ -370,7 +327,7 @@ def write_chunk(self, myblob_name, myblob_uri, file_number, chunk_size, chunk_te file_name, file_extension, file_directory = self.get_filename_and_extension(myblob_name) blob_service_client = BlobServiceClient( self.azure_blob_storage_endpoint, - self.azure_blob_storage_key) + credential=azure_credential) json_str = json.dumps(chunk_output, indent=2, ensure_ascii=False) block_blob_client = blob_service_client.get_blob_client( container=self.azure_blob_content_storage_container, diff --git a/functions/shared_code/utilities_helper.py b/functions/shared_code/utilities_helper.py index 2958697c1..36f14cc6c 100644 --- a/functions/shared_code/utilities_helper.py +++ b/functions/shared_code/utilities_helper.py @@ -5,18 +5,18 @@ import logging import urllib.parse from datetime import datetime, timedelta -from azure.storage.blob import generate_blob_sas, BlobSasPermissions +from azure.storage.blob import generate_blob_sas, BlobSasPermissions, BlobServiceClient +from azure.identity import ManagedIdentityCredential class UtilitiesHelper: """ Helper class for utility functions""" def __init__(self, azure_blob_storage_account, - azure_blob_storage_endpoint, - azure_blob_storage_key + azure_blob_storage_endpoint ): self.azure_blob_storage_account = azure_blob_storage_account self.azure_blob_storage_endpoint = azure_blob_storage_endpoint - self.azure_blob_storage_key = azure_blob_storage_key + self.blob_service_client = BlobServiceClient(azure_blob_storage_endpoint, credential=ManagedIdentityCredential()) def get_filename_and_extension(self, path): """ Function to return the file name & type""" @@ -40,12 +40,15 @@ def get_blob_and_sas(self, blob_path): container_name = separator.join( blob_path.split(separator)[0:1]) + # Obtain the user delegation key + user_delegation_key = self.blob_service_client.get_user_delegation_key(key_start_time=datetime.now(datetime.UTC), key_expiry_time=datetime.now(datetime.UTC) + timedelta(hours=12)) + # Gen SAS token sas_token = generate_blob_sas( account_name=self.azure_blob_storage_account, container_name=container_name, blob_name=file_path_w_name_no_cont, - account_key=self.azure_blob_storage_key, + user_delegation_key=user_delegation_key, permission=BlobSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=1) ) diff --git a/infra/core/db/cosmosdb.tf b/infra/core/db/cosmosdb.tf index b72d90ac5..6d1f66f48 100644 --- a/infra/core/db/cosmosdb.tf +++ b/infra/core/db/cosmosdb.tf @@ -35,7 +35,7 @@ resource "azurerm_cosmosdb_account" "cosmosdb_account" { kind = "GlobalDocumentDB" tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true - local_authentication_disabled = var.is_secure_mode ? true : false + #local_authentication_disabled = var.is_secure_mode ? true : false consistency_policy { consistency_level = var.defaultConsistencyLevel @@ -47,6 +47,10 @@ resource "azurerm_cosmosdb_account" "cosmosdb_account" { location = var.location failover_priority = 0 } + + capabilities { + name = "EnableServerless" + } } resource "azurerm_cosmosdb_sql_database" "log_database" { @@ -62,10 +66,6 @@ resource "azurerm_cosmosdb_sql_container" "log_container" { database_name = azurerm_cosmosdb_sql_database.log_database.name partition_key_path = "/file_name" - - autoscale_settings { - max_throughput = var.autoscaleMaxThroughput - } } module "cosmos_db_key" { diff --git a/infra/core/host/enrichmentapp/enrichmentapp.tf b/infra/core/host/enrichmentapp/enrichmentapp.tf index 4e54f346a..73481df8c 100644 --- a/infra/core/host/enrichmentapp/enrichmentapp.tf +++ b/infra/core/host/enrichmentapp/enrichmentapp.tf @@ -108,9 +108,6 @@ resource "azurerm_linux_web_app" "enrichmentapp" { "AZURE_SEARCH_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-SEARCH-SERVICE-KEY)" "COSMOSDB_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" "AZURE_AI_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-AI-KEY)" - "AZURE_BLOB_STORAGE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-BLOB-STORAGE-KEY)" - "BLOB_CONNECTION_STRING" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BLOB-CONNECTION-STRING)" - "AZURE_STORAGE_CONNECTION_STRING" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BLOB-CONNECTION-STRING)" "AZURE_OPENAI_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-OPENAI-SERVICE-KEY)" "KEY_EXPIRATION_DATE" = timeadd(timestamp(), "4320h") # Added expiration date setting for keys "WEBSITE_PULL_IMAGE_OVER_VNET" = var.is_secure_mode ? "true" : "false" diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index d12ff43a4..000bf52c1 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -88,7 +88,8 @@ resource "azurerm_linux_function_app" "function_app" { resource_group_name = var.resourceGroupName service_plan_id = azurerm_service_plan.funcServicePlan.id storage_account_name = var.blobStorageAccountName - storage_account_access_key = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-BLOB-STORAGE-KEY)" + storage_uses_managed_identity = true + #storage_account_access_key = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-BLOB-STORAGE-KEY)" https_only = true tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true @@ -119,7 +120,7 @@ resource "azurerm_linux_function_app" "function_app" { } connection_string { - name = "BLOB_CONNECTION_STRING" + name = "STORAGE_CONNECTION_STRING" type = "Custom" value = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BLOB-CONNECTION-STRING)" } @@ -130,6 +131,7 @@ resource "azurerm_linux_function_app" "function_app" { SCM_DO_BUILD_DURING_DEPLOYMENT = "false" ENABLE_ORYX_BUILD = "false" AzureWebJobsStorage = "DefaultEndpointsProtocol=https;AccountName=${var.blobStorageAccountName};EndpointSuffix=${var.endpointSuffix};AccountKey=${data.azurerm_storage_account.existing_sa.primary_access_key}" + AzureWebJobsStorage__accountName = var.blobStorageAccountName WEBSITE_CONTENTAZUREFILECONNECTIONSTRING = "DefaultEndpointsProtocol=https;AccountName=${var.blobStorageAccountName};EndpointSuffix=${var.endpointSuffix};AccountKey=${data.azurerm_storage_account.existing_sa.primary_access_key}" WEBSITE_CONTENTSHARE = "funcfileshare" FUNCTIONS_WORKER_RUNTIME = var.runtime @@ -142,13 +144,11 @@ resource "azurerm_linux_function_app" "function_app" { BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME = var.blobStorageAccountUploadContainerName BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME = var.blobStorageAccountOutputContainerName BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME = var.blobStorageAccountLogContainerName - AZURE_BLOB_STORAGE_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-BLOB-STORAGE-KEY)" CHUNK_TARGET_SIZE = var.chunkTargetSize TARGET_PAGES = var.targetPages FR_API_VERSION = var.formRecognizerApiVersion AZURE_FORM_RECOGNIZER_ENDPOINT = var.formRecognizerEndpoint AZURE_FORM_RECOGNIZER_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-FORM-RECOGNIZER-KEY)" - BLOB_CONNECTION_STRING = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BLOB-CONNECTION-STRING)" COSMOSDB_URL = var.CosmosDBEndpointURL COSMOSDB_LOG_DATABASE_NAME = var.CosmosDBLogDatabaseName COSMOSDB_LOG_CONTAINER_NAME = var.CosmosDBLogContainerName @@ -182,6 +182,7 @@ resource "azurerm_linux_function_app" "function_app" { AZURE_AI_TRANSLATION_DOMAIN = var.azure_ai_translation_domain AZURE_AI_TEXT_ANALYTICS_DOMAIN = var.azure_ai_text_analytics_domain WEBSITE_PULL_IMAGE_OVER_VNET = var.is_secure_mode ? "true" : "false" + STORAGE_CONNECTION_STRING__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" } } diff --git a/infra/core/host/webapp/variables.tf b/infra/core/host/webapp/variables.tf index 017e39d14..4ac219f1a 100644 --- a/infra/core/host/webapp/variables.tf +++ b/infra/core/host/webapp/variables.tf @@ -131,7 +131,7 @@ variable "vnet_name" { type = string } -variable "subnet_id" { +variable "subnet_name" { type = string } diff --git a/infra/core/host/webapp/webapp.tf b/infra/core/host/webapp/webapp.tf index 6b74dcebc..eba807d7e 100644 --- a/infra/core/host/webapp/webapp.tf +++ b/infra/core/host/webapp/webapp.tf @@ -117,7 +117,6 @@ resource "azurerm_linux_web_app" "app_service" { "AZURE_SEARCH_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-SEARCH-SERVICE-KEY)" "COSMOSDB_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" "BING_SEARCH_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BINGSEARCH-KEY)" - "AZURE_BLOB_STORAGE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-BLOB-STORAGE-KEY)" "AZURE_AI_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-AI-KEY)" "AZURE_OPENAI_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-OPENAI-SERVICE-KEY)" "WEBSITE_PULL_IMAGE_OVER_VNET" = var.is_secure_mode ? "true" : "false" @@ -250,12 +249,19 @@ resource "azurerm_key_vault_access_policy" "policy" { ] } +data "azurerm_subnet" "subnet" { + count = var.is_secure_mode ? 1 : 0 + name = var.subnet_name + virtual_network_name = var.vnet_name + resource_group_name = var.resourceGroupName +} + resource "azurerm_private_endpoint" "backendPrivateEndpoint" { count = var.is_secure_mode ? 1 : 0 name = "${var.name}-private-endpoint" location = var.location resource_group_name = var.resourceGroupName - subnet_id = var.subnet_id + subnet_id = data.azurerm_subnet.subnet[0].id tags = var.tags custom_network_interface_name = "infoasstwebnic" diff --git a/infra/core/storage/storage-account.tf b/infra/core/storage/storage-account.tf index 4d2db92c2..928651008 100644 --- a/infra/core/storage/storage-account.tf +++ b/infra/core/storage/storage-account.tf @@ -279,18 +279,6 @@ resource "azurerm_private_endpoint" "queuePrivateEndpoint" { } } -module "storage_connection_string" { - source = "../security/keyvaultSecret" - resourceGroupName = var.resourceGroupName - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - key_vault_name = var.key_vault_name - secret_name = "BLOB-CONNECTION-STRING" - secret_value = azurerm_storage_account.storage.primary_connection_string - tags = var.tags - alias = "blobConn" - kv_secret_expiration = var.kv_secret_expiration -} - module "storage_key" { source = "../security/keyvaultSecret" resourceGroupName = var.resourceGroupName diff --git a/infra/main.tf b/infra/main.tf index 164b46b55..6439ef1c1 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -355,7 +355,7 @@ module "webapp" { keyVaultName = module.kvModule.keyVaultName tenantId = var.tenantId is_secure_mode = var.is_secure_mode - subnet_id = var.is_secure_mode ? module.network[0].snetApp_id : null + subnet_name = var.is_secure_mode ? module.network[0].snetApp_name : null vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null snetIntegration_id = var.is_secure_mode ? module.network[0].snetIntegration_id : null private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneApp[0].privateDnsZoneResourceId] : null diff --git a/scripts/extract-content.py b/scripts/extract-content.py index bbbbc8f73..c299daba7 100755 --- a/scripts/extract-content.py +++ b/scripts/extract-content.py @@ -14,7 +14,7 @@ from azure.keyvault.secrets import SecretClient from azure.search.documents import SearchClient from azure.core.credentials import AzureKeyCredential -from azure.storage.blob import BlobServiceClient, ContainerClient +from azure.storage.blob import BlobServiceClient from azure.storage.blob import generate_container_sas, ContainerSasPermissions from datetime import datetime, timedelta import urllib.parse @@ -25,7 +25,6 @@ skip_upload_container = False skip_content_container = False - # Helper function for getting the appropriate Azure CLI Vault URL def get_keyvault_url(keyvault_name, resource_group=None): """ Return vault url @@ -161,19 +160,15 @@ def get_storage_account_endpoint(storage_account_name): old_cosmosdb_url = f'https://infoasst-cosmos-{old_random_text}.documents.azure.com:443/' old_cosmosdb_key = old_secret_client.get_secret('COSMOSDB-KEY').value old_search_endpoint = f'https://infoasst-search-{old_random_text}.search.windows.net' -old_blob_connection_string = old_secret_client.get_secret('BLOB-CONNECTION-STRING').value old_search_key = old_secret_client.get_secret('AZURE-SEARCH-SERVICE-KEY').value old_azure_blob_storage_account = f"infoasststore{old_random_text}" -old_azure_blob_storage_key = old_secret_client.get_secret('AZURE-BLOB-STORAGE-KEY').value old_azure_blob_storage_endpoint = get_storage_account_endpoint(old_azure_blob_storage_account) new_search_key = new_secret_client.get_secret('AZURE-SEARCH-SERVICE-KEY').value new_search_endpoint = f'https://infoasst-search-{new_random_text}.search.windows.net' new_cosmosdb_url = f'https://infoasst-cosmos-{new_random_text}.documents.azure.com:443/' new_cosmosdb_key = new_secret_client.get_secret('COSMOSDB-KEY').value -new_blob_connection_string = new_secret_client.get_secret('BLOB-CONNECTION-STRING').value new_azure_blob_storage_account = f"infoasststore{new_random_text}" -new_azure_blob_storage_key = new_secret_client.get_secret('AZURE-BLOB-STORAGE-KEY').value new_azure_blob_storage_endpoint = get_storage_account_endpoint(new_azure_blob_storage_account) index_name = 'vector-index' @@ -188,7 +183,7 @@ def get_storage_account_endpoint(storage_account_name): # Migrate Search if skip_search_index == False: print(f.renderText('Search Index')) - blob_service_client = BlobServiceClient.from_connection_string(old_blob_connection_string) + blob_service_client = BlobServiceClient(old_azure_blob_storage_endpoint, credential=credential) container_name = "content" container_client = blob_service_client.get_container_client(container_name) @@ -293,12 +288,12 @@ def get_storage_account_endpoint(storage_account_name): max_item_count = 1 # Get old status docs - old_cosmos_client = CosmosClient(old_cosmosdb_url, old_cosmosdb_key) + old_cosmos_client = CosmosClient(old_cosmosdb_url, old_cosmosdb_key, consistency_level='Session') old_status_database = old_cosmos_client.get_database_client('statusdb') old_status_container = old_status_database.get_container_client('statuscontainer') old_tags_database = old_cosmos_client.get_database_client('tagdb') old_tags_container = old_tags_database.get_container_client('tagcontainer') - new_cosmos_client = CosmosClient(new_cosmosdb_url, new_cosmosdb_key) + new_cosmos_client = CosmosClient(new_cosmosdb_url, new_cosmosdb_key, consistency_level='Session') new_status_database = new_cosmos_client.get_database_client('statusdb') new_status_container = new_status_database.get_container_client('statuscontainer') @@ -377,9 +372,9 @@ def get_storage_account_endpoint(storage_account_name): download_and_install_azcopy() upload_container_error = "" container_name = "upload" - old_blob_service_client = BlobServiceClient.from_connection_string(old_blob_connection_string) + old_blob_service_client = BlobServiceClient(old_azure_blob_storage_endpoint, credential=credential) old_container_client = old_blob_service_client.get_container_client(container_name) - new_blob_service_client = BlobServiceClient.from_connection_string(new_blob_connection_string) + new_blob_service_client = BlobServiceClient(new_azure_blob_storage_endpoint, credential=credential) new_container_client = new_blob_service_client.get_container_client(container_name) file_count = 0 @@ -388,21 +383,27 @@ def get_storage_account_endpoint(storage_account_name): blobs_processed_count = 0 for blob in blob_list: + # Obtain the user delegation key + old_user_delegation_key = old_blob_service_client.get_user_delegation_key(key_start_time=datetime.now(datetime.UTC), key_expiry_time=datetime.now(datetime.UTC) + timedelta(hours=12)) + # Generate SAS token for old blob old_sas_token = generate_container_sas( account_name=old_azure_blob_storage_account, container_name=container_name, - account_key=old_azure_blob_storage_key, + user_delegation_key=old_user_delegation_key, permission=ContainerSasPermissions(read=True, write=False, delete=False, list=True), # Adjust permissions as needed - expiry=datetime.utcnow() + timedelta(hours=12) + expiry=datetime.utcnow() + timedelta(hours=12) ) source_url = f"https://{old_azure_blob_storage_account}.blob.core.windows.net/{container_name}/{urllib.parse.quote(blob.name)}?{old_sas_token}" + # Obtain the user delegation key + new_user_delegation_key = new_blob_service_client.get_user_delegation_key(key_start_time=datetime.now(datetime.UTC), key_expiry_time=datetime.now(datetime.UTC) + timedelta(hours=12)) + # Generate SAS token for new blob new_sas_token = generate_container_sas( account_name=new_azure_blob_storage_account, container_name=container_name, - account_key=new_azure_blob_storage_key, + user_delegation_key=new_user_delegation_key, permission=ContainerSasPermissions(read=True, write=True, delete=False, list=True), # Adjust permissions as needed expiry=datetime.utcnow() + timedelta(hours=12) ) @@ -434,9 +435,9 @@ def get_storage_account_endpoint(storage_account_name): print(f.renderText('Storage content container')) container_name = "content" content_container_error = "" - old_blob_service_client = BlobServiceClient.from_connection_string(old_blob_connection_string) + old_blob_service_client = BlobServiceClient(old_azure_blob_storage_endpoint, credential=credential) old_container_client = old_blob_service_client.get_container_client(container_name) - new_blob_service_client = BlobServiceClient.from_connection_string(new_blob_connection_string) + new_blob_service_client = BlobServiceClient(new_azure_blob_storage_endpoint, credential=credential) new_container_client = new_blob_service_client.get_container_client(container_name) chunks_processed_count = 0 diff --git a/scripts/functional-tests.sh b/scripts/functional-tests.sh index b01c06e62..56f13ae9a 100755 --- a/scripts/functional-tests.sh +++ b/scripts/functional-tests.sh @@ -29,7 +29,7 @@ BASE_PATH=$(realpath "$DIR/..") # Pipeline functional test python run_tests.py \ - --storage_account_connection_str "${BLOB_CONNECTION_STRING}" \ + --storage_account_url "${BLOB_STORAGE_ACCOUNT_ENDPOINT}" \ --search_service_endpoint "${AZURE_SEARCH_SERVICE_ENDPOINT}" \ --search_index "${AZURE_SEARCH_INDEX}" \ --search_key "${AZURE_SEARCH_SERVICE_KEY}" \ diff --git a/scripts/inf-import-state.sh b/scripts/inf-import-state.sh index bb7127d51..217bec3a5 100755 --- a/scripts/inf-import-state.sh +++ b/scripts/inf-import-state.sh @@ -492,9 +492,6 @@ import_resource_if_needed "$module_path[5]" "$url" url="https://$name.queue.core.windows.net/embeddings-queue" import_resource_if_needed "$module_path[6]" "$url" -secret_id=$(get_secret "BLOB-CONNECTION-STRING") -module_path="module.storage.azurerm_key_vault_secret.storage_connection_string" -import_resource_if_needed "$module_path" "$secret_id" secret_id=$(get_secret "AZURE-BLOB-STORAGE-KEY") module_path="module.storage.azurerm_key_vault_secret.storage_key" import_resource_if_needed "$module_path" "$secret_id" diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index e94bfb575..c3b03d63f 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -20,20 +20,13 @@ secrets="{" keyVaultName=$(cat inf_output.json | jq -r .DEPLOYMENT_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "BLOB-CONNECTION-STRING" "COSMOSDB-KEY" "AZURE-FORM-RECOGNIZER-KEY" "AZURE-AI-KEY") -azWebJobSecretName="BLOB-CONNECTION-STRING" -azWebJobVarName="AzureWebJobsStorage" +secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-FORM-RECOGNIZER-KEY" "AZURE-AI-KEY") # Retrieve and export each secret for secretName in "${secretNames[@]}"; do secretValue=$(az keyvault secret show --name $secretName --vault-name $keyVaultName --query value -o tsv) envVarName=$(echo $secretName | tr '-' '_') secrets+="\"$envVarName\": \"$secretValue\"," - - if [ "$secretName" == "$azWebJobSecretName" ]; then - export $azWebJobVarName=$secretValue - secrets+="\"$azWebJobVarName\": \"$secretValue\"," - fi done secrets=${secrets%?} # Remove the trailing comma secrets+="}" diff --git a/scripts/json-to-env.sh b/scripts/json-to-env.sh index c64037922..814d630a5 100755 --- a/scripts/json-to-env.sh +++ b/scripts/json-to-env.sh @@ -162,7 +162,7 @@ fi # Name of your Key Vault keyVaultName=$(cat inf_output.json | jq -r .DEPLOYMENT_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "BLOB-CONNECTION-STRING" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY") +secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY") # Retrieve and export each secret for secretName in "${secretNames[@]}"; do diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index 681208e3c..99cc9a6ad 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -210,9 +210,9 @@ keyVaultName=$(cat inf_output.json | jq -r .DEPLOYMENT_KEYVAULT_NAME.value) # Names of your secrets if [ -n "${SECURE_MODE}" ]; then - secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "BLOB-CONNECTION-STRING" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY" "AZURE-CLIENT-SECRET" "AZURE-AI-KEY") + secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY" "AZURE-CLIENT-SECRET" "AZURE-AI-KEY") else - secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "BLOB-CONNECTION-STRING" "COSMOSDB-KEY" "BINGSEARCH-KEY" "AZURE-OPENAI-SERVICE-KEY" "AZURE-CLIENT-SECRET" "AZURE-AI-KEY") + secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "BINGSEARCH-KEY" "AZURE-OPENAI-SERVICE-KEY" "AZURE-CLIENT-SECRET" "AZURE-AI-KEY") fi diff --git a/scripts/merge-databases.py b/scripts/merge-databases.py index ba0b78f17..3294fc9a0 100755 --- a/scripts/merge-databases.py +++ b/scripts/merge-databases.py @@ -84,7 +84,7 @@ def get_keyvault_url(keyvault_name, resource_group=None): # ************************************************************************* # Migrate Cosmos DB tags from the old tags container and database to the # status container and database as these have now been merged -client = CosmosClient(cosmosdb_url, cosmosdb_key.value) +client = CosmosClient(cosmosdb_url, cosmosdb_key.value, consistency_level='Session') try: # Get old status docs diff --git a/scripts/tf-dependencies.json b/scripts/tf-dependencies.json index 092fee67a..a755030d8 100644 --- a/scripts/tf-dependencies.json +++ b/scripts/tf-dependencies.json @@ -685,7 +685,6 @@ "module.logging.azurerm_application_insights.applicationInsights", "module.logging.azurerm_log_analytics_workspace.logAnalytics", "module.searchServices.azurerm_search_service.search", - "module.storage.azurerm_key_vault_secret.storage_connection_string", "module.storage.azurerm_key_vault_secret.storage_key", "module.storage.azurerm_storage_account.storage", "module.storage.azurerm_storage_blob.config", @@ -725,7 +724,6 @@ "module.logging.azurerm_application_insights.applicationInsights", "module.logging.azurerm_log_analytics_workspace.logAnalytics", "module.searchServices.azurerm_search_service.search", - "module.storage.azurerm_key_vault_secret.storage_connection_string", "module.storage.azurerm_key_vault_secret.storage_key", "module.storage.azurerm_storage_account.storage", "module.storage.azurerm_storage_blob.config", @@ -759,7 +757,6 @@ "module.kvModule.azurerm_key_vault.kv", "module.kvModule.azurerm_key_vault_secret.spClientKeySecret", "module.kvModule.data.azurerm_client_config.current", - "module.storage.azurerm_key_vault_secret.storage_connection_string", "module.storage.azurerm_key_vault_secret.storage_key", "module.storage.azurerm_storage_account.storage", "module.storage.azurerm_storage_blob.config", @@ -792,7 +789,6 @@ "module.kvModule.azurerm_key_vault.kv", "module.kvModule.azurerm_key_vault_secret.spClientKeySecret", "module.kvModule.data.azurerm_client_config.current", - "module.storage.azurerm_key_vault_secret.storage_connection_string", "module.storage.azurerm_key_vault_secret.storage_key", "module.storage.azurerm_storage_account.storage", "module.storage.azurerm_storage_blob.config", @@ -1058,26 +1054,6 @@ } ] }, - { - "mode": "managed", - "type": "azurerm_key_vault_secret", - "name": "storage_connection_string", - "module": "module.storage", - "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", - "instances": [ - { - "dependencies": [ - "azurerm_resource_group.rg", - "data.azurerm_client_config.current", - "module.kvModule.azurerm_key_vault.kv", - "module.kvModule.data.azurerm_client_config.current", - "module.storage.azurerm_storage_account.storage", - "random_string.random" - ], - "index_key": null - } - ] - }, { "mode": "managed", "type": "azurerm_key_vault_secret", @@ -1321,7 +1297,6 @@ "module.logging.azurerm_application_insights.applicationInsights", "module.logging.azurerm_log_analytics_workspace.logAnalytics", "module.searchServices.azurerm_search_service.search", - "module.storage.azurerm_key_vault_secret.storage_connection_string", "module.storage.azurerm_key_vault_secret.storage_key", "module.storage.azurerm_storage_account.storage", "module.storage.azurerm_storage_blob.config", diff --git a/tests/debug_tests.py b/tests/debug_tests.py index 353b156b8..c44d0d4b8 100644 --- a/tests/debug_tests.py +++ b/tests/debug_tests.py @@ -4,14 +4,14 @@ import subprocess import os -BLOB_CONNECTION_STRING = os.environ.get("BLOB_CONNECTION_STRING") +STORAGE_ACCOUNT_URL = os.environ.get("AZURE_BLOB_STORAGE_ENDPOINT") AZURE_SEARCH_SERVICE_ENDPOINT = os.environ.get("AZURE_SEARCH_SERVICE_ENDPOINT") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") AZURE_SEARCH_SERVICE_KEY = os.environ.get("AZURE_SEARCH_SERVICE_KEY") ENRICHMENT_APPSERVICE_NAME = os.environ.get("ENRICHMENT_APPSERVICE_NAME") AZURE_WEBSITE_DOMAIN = os.environ.get("TF_VAR_azure_websites_domain") or "false" -subprocess.call(['python', 'run_tests.py', '--storage_account_connection_str', BLOB_CONNECTION_STRING, \ +subprocess.call(['python', 'run_tests.py', '--storage_account_url', STORAGE_ACCOUNT_URL, \ '--search_service_endpoint', AZURE_SEARCH_SERVICE_ENDPOINT, \ '--search_index', AZURE_SEARCH_INDEX, \ '--search_key', AZURE_SEARCH_SERVICE_KEY, \ diff --git a/tests/run_tests.py b/tests/run_tests.py index e9e89c9b3..8d307fce0 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -12,9 +12,12 @@ from rich.console import Console import rich.traceback from azure.storage.blob import BlobServiceClient +from azure.identity import DefaultAzureCredential from azure.search.documents import SearchClient from azure.core.credentials import AzureKeyCredential +azure_credential = DefaultAzureCredential() + rich.traceback.install() console = Console() @@ -49,9 +52,9 @@ def parse_arguments(): """ parser = argparse.ArgumentParser() parser.add_argument( - "--storage_account_connection_str", + "--storage_account_url", required=True, - help="Storage account connection string (set in extract-env)") + help="Storage account endpoint string (set in extract-env)") parser.add_argument( "--search_service_endpoint", required=True, @@ -226,8 +229,8 @@ def get_files_by_extension(folder_path, extensions): if __name__ == '__main__': args = parse_arguments() try: - storage_blob_service_client = BlobServiceClient.from_connection_string( - args.storage_account_connection_str) + storage_blob_service_client = BlobServiceClient( + args.storage_account_url, credential=azure_credential) # Get a list of files with specified extensions in the test_data folder test_file_names = get_files_by_extension(FILE_PATH, args.file_extensions) From 965524e8605426b4578823ba6111e9a5ef28a53c Mon Sep 17 00:00:00 2001 From: bjakems <165402330+bjakems@users.noreply.github.com> Date: Tue, 9 Jul 2024 20:20:26 +0100 Subject: [PATCH 02/33] adding CognitiveServicesUser and StorageQueueDataMesssageProcessor user and service account roles for web app and func --- infra/core/host/functions/outputs.tf | 6 +++- infra/main.tf | 48 +++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/infra/core/host/functions/outputs.tf b/infra/core/host/functions/outputs.tf index 0bc42fa88..485f36280 100644 --- a/infra/core/host/functions/outputs.tf +++ b/infra/core/host/functions/outputs.tf @@ -17,4 +17,8 @@ output "name" { output "subnet_integration_id" { value = var.subnetIntegration_id -} \ No newline at end of file +} + +output "identityPrincipalId" { + value = azurerm_linux_function_app.function_app.identity.0.principal_id +} \ No newline at end of file diff --git a/infra/main.tf b/infra/main.tf index 6439ef1c1..1e1ab0f11 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -2,8 +2,10 @@ locals { tags = { ProjectName = "Information Assistant", BuildNumber = var.buildNumber } azure_roles = jsondecode(file("${path.module}/azure_roles.json")) selected_roles = ["CognitiveServicesOpenAIUser", + "CognitiveServicesUser", "StorageBlobDataReader", - "StorageBlobDataContributor", + "StorageBlobDataContributor", + "StorageQueueDataMessageProcessor", "SearchIndexDataReader", "SearchIndexDataContributor"] } @@ -696,6 +698,50 @@ module "openAiRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } +module "webAppCognitiveServicesUserBackend" { + source = "./core/security/role" + + scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + principalId = module.webapp.identityPrincipalId + roleDefinitionId = local.azure_roles.CognitiveServicesUser + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + +module "functionAppCognitiveServicesUserBackend" { + source = "./core/security/role" + + scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + principalId = module.functions.identityPrincipalId + roleDefinitionId = local.azure_roles.CognitiveServicesUser + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + +module "webAppStorageQueueDataMessageProcessorBackend" { + source = "./core/security/role" + + scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + principalId = module.webapp.identityPrincipalId + roleDefinitionId = local.azure_roles.StorageQueueDataMessageProcessor + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + +module "functionAppStorageQueueDataMessageProcessorBackend" { + source = "./core/security/role" + + scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + principalId = module.functions.identityPrincipalId + roleDefinitionId = local.azure_roles.StorageQueueDataMessageProcessor + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + module "storageRoleBackend" { source = "./core/security/role" From 78ce55971c4bb3e074c099d6df549ad28cb838cf Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Wed, 10 Jul 2024 00:51:26 +0100 Subject: [PATCH 03/33] Switch search, openai, translation, and text ai services to AAD auth --- .vscode/launch.json | 3 +- app/backend/app.py | 59 +++++++---------- .../approaches/chatreadretrieveread.py | 27 ++++---- app/backend/approaches/chatwebretrieveread.py | 14 +++- app/backend/approaches/comparewebwithwork.py | 14 ++-- app/backend/approaches/compareworkwithweb.py | 25 +++++-- app/backend/approaches/gpt_direct_approach.py | 14 ++-- app/backend/approaches/mathassistant.py | 46 +++++-------- app/enrichment/app.py | 43 ++++++++---- docs/deployment/deployment.md | 1 - .../setting_up_sandbox_environment.md | 2 +- functions/FileDeletion/__init__.py | 24 +++++-- functions/FileFormRecPollingPDF/__init__.py | 22 ++++++- .../FileFormRecSubmissionPDF/__init__.py | 28 ++++++-- functions/FileLayoutParsingOther/__init__.py | 24 +++++-- functions/FileUploadedFunc/__init__.py | 26 ++++++-- functions/ImageEnrichment/__init__.py | 43 +++++++----- functions/TextEnrichment/__init__.py | 65 ++++++++++--------- functions/shared_code/utilities.py | 9 ++- functions/shared_code/utilities_helper.py | 6 +- .../core/ai/openaiservices/openaiservices.tf | 12 ---- .../core/host/enrichmentapp/enrichmentapp.tf | 3 - infra/core/host/functions/functions.tf | 5 +- infra/core/host/functions/variables.tf | 17 ++--- infra/core/host/webapp/webapp.tf | 3 - infra/core/search/search-services.tf | 12 ---- infra/core/storage/outputs.tf | 6 +- infra/main.tf | 17 +++-- infra/outputs.tf | 24 +++---- infra/variables.tf | 8 --- pipelines/devcontainer-ci.env | 1 - pipelines/templates/make-command.yml | 1 - scripts/deploy-search-indexes.sh | 12 ++-- .../AzureEnvironments/AzureCloud.env | 2 - .../AzureEnvironments/AzureUSGovernment.env | 2 - scripts/environments/local.env.example | 1 - scripts/extract-content.py | 5 +- scripts/functional-tests.sh | 1 - scripts/json-to-env.function.debug.sh | 19 +++--- scripts/json-to-env.sh | 6 +- scripts/json-to-env.webapp.debug.sh | 23 +++---- scripts/prepare-tf-variables.sh | 1 - tests/debug_tests.py | 2 - tests/run_tests.py | 19 ++---- 44 files changed, 365 insertions(+), 332 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c43dea498..84a61a14f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -100,8 +100,7 @@ "env": { "storage_account_url": "${env:AZURE_BLOB_STORAGE_ENDPOINT}", "SEARCH_SERVICE_ENDPOINT": "${env:AZURE_SEARCH_SERVICE_ENDPOINT}", - "SEARCH_INDEX": "${env:AZURE_SEARCH_INDEX}", - "SEARCH_KEY": "${env:AZURE_SEARCH_SERVICE_KEY}" + "SEARCH_INDEX": "${env:AZURE_SEARCH_INDEX}" }, "cwd": "${workspaceFolder}/tests", "envFile": "${workspaceFolder}/scripts/environments/infrastructure.debug.env", diff --git a/app/backend/app.py b/app/backend/app.py index 4abffb857..f4c61ef9f 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -23,19 +23,12 @@ from approaches.chatwebretrieveread import ChatWebRetrieveRead from approaches.gpt_direct_approach import GPTDirectApproach from approaches.approach import Approaches -from azure.core.credentials import AzureKeyCredential from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient from azure.search.documents import SearchClient -from azure.storage.blob import ( - AccountSasPermissions, - BlobServiceClient, - ResourceTypes, - generate_account_sas, -) +from azure.storage.blob import BlobServiceClient from approaches.mathassistant import( generate_response, - process_agent_scratch_pad, process_agent_response, stream_agent_responses ) @@ -45,7 +38,6 @@ process_agent_response as td_agent_response, process_agent_scratch_pad as td_agent_scratch_pad, get_images_in_temp - ) from shared_code.status_log import State, StatusClassification, StatusLog, StatusQueryLevel from azure.cosmos import CosmosClient @@ -60,7 +52,6 @@ "AZURE_BLOB_STORAGE_UPLOAD_CONTAINER": "upload", "AZURE_SEARCH_SERVICE": "gptkb", "AZURE_SEARCH_SERVICE_ENDPOINT": None, - "AZURE_SEARCH_SERVICE_KEY": None, "AZURE_SEARCH_INDEX": "gptkbindex", "USE_SEMANTIC_RERANKER": "true", "AZURE_OPENAI_SERVICE": "myopenai", @@ -74,7 +65,6 @@ "EMBEDDING_DEPLOYMENT_NAME": "", "AZURE_OPENAI_EMBEDDINGS_MODEL_NAME": "", "AZURE_OPENAI_EMBEDDINGS_VERSION": "", - "AZURE_OPENAI_SERVICE_KEY": None, "AZURE_SUBSCRIPTION_ID": None, "AZURE_ARM_MANAGEMENT_API": "https://management.azure.com", "CHAT_WARNING_BANNER_TEXT": "", @@ -92,9 +82,7 @@ "ENRICHMENT_APPSERVICE_URL": "enrichment", "TARGET_TRANSLATION_LANGUAGE": "en", "AZURE_AI_ENDPOINT": None, - "AZURE_AI_KEY": None, "AZURE_AI_LOCATION": "", - "AZURE_AI_TRANSLATION_DOMAIN": "api.cognitive.microsofttranslator.com", "BING_SEARCH_ENDPOINT": "https://api.bing.microsoft.com/", "BING_SEARCH_KEY": "", "ENABLE_BING_SAFE_SEARCH": "true", @@ -104,7 +92,8 @@ "ENABLE_TABULAR_DATA_ASSISTANT": "false", "ENABLE_MULTIMEDIA": "false", "MAX_CSV_FILE_SIZE": "7", - "LOCAL_DEBUG": "false" + "LOCAL_DEBUG": "false", + "AZURE_AI_CREDENTIAL_DOMAIN": "cognitiveservices.azure.com" } for key, value in ENV.items(): @@ -138,18 +127,20 @@ class StatusResponse(pydantic.BaseModel): else: AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD openai.api_version = "2024-02-01" -# Use the current user identity to authenticate with Azure OpenAI, Cognitive 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 DefaultAzureCredntial resolution, you can exclude the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) if ENV["LOCAL_DEBUG"] == "true": - azure_credential = DefaultAzureCredential() + azure_credential = DefaultAzureCredential(authority=AUTHORITY) else: azure_credential = ManagedIdentityCredential(authority=AUTHORITY) # Comment these two lines out if using keys, set your API key in the OPENAI_API_KEY environment variable instead openai.api_type = "azure_ad" -openai_token = azure_credential.get_token("https://cognitiveservices.azure.com/.default") -openai.api_key = ENV["AZURE_OPENAI_SERVICE_KEY"] +token_provider = get_bearer_token_provider(azure_credential, f'https://{ENV["AZURE_AI_CREDENTIAL_DOMAIN"]}/.default') +openai.azure_ad_token_provider = token_provider +#openai.api_key = ENV["AZURE_OPENAI_SERVICE_KEY"] # Setup StatusLog to allow access to CosmosDB for logging statusLog = StatusLog( @@ -159,12 +150,11 @@ class StatusResponse(pydantic.BaseModel): ENV["COSMOSDB_LOG_CONTAINER_NAME"] ) -azure_search_key_credential = AzureKeyCredential(ENV["AZURE_SEARCH_SERVICE_KEY"]) # Set up clients for Cognitive Search and Storage search_client = SearchClient( endpoint=ENV["AZURE_SEARCH_SERVICE_ENDPOINT"], index_name=ENV["AZURE_SEARCH_INDEX"], - credential=azure_search_key_credential, + credential=azure_credential, ) blob_client = BlobServiceClient( account_url=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], @@ -206,7 +196,6 @@ class StatusResponse(pydantic.BaseModel): Approaches.ReadRetrieveRead: ChatReadRetrieveReadApproach( search_client, ENV["AZURE_OPENAI_ENDPOINT"], - ENV["AZURE_OPENAI_SERVICE_KEY"], ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["KB_FIELDS_SOURCEFILE"], ENV["KB_FIELDS_CONTENT"], @@ -221,9 +210,8 @@ class StatusResponse(pydantic.BaseModel): ENV["ENRICHMENT_APPSERVICE_URL"], ENV["TARGET_TRANSLATION_LANGUAGE"], ENV["AZURE_AI_ENDPOINT"], - ENV["AZURE_AI_KEY"], ENV["AZURE_AI_LOCATION"], - ENV["AZURE_AI_TRANSLATION_DOMAIN"], + token_provider, str_to_bool.get(ENV["USE_SEMANTIC_RERANKER"]) ), Approaches.ChatWebRetrieveRead: ChatWebRetrieveRead( @@ -232,20 +220,23 @@ class StatusResponse(pydantic.BaseModel): ENV["TARGET_TRANSLATION_LANGUAGE"], ENV["BING_SEARCH_ENDPOINT"], ENV["BING_SEARCH_KEY"], - str_to_bool.get(ENV["ENABLE_BING_SAFE_SEARCH"]) - ), + str_to_bool.get(ENV["ENABLE_BING_SAFE_SEARCH"]), + ENV["AZURE_OPENAI_ENDPOINT"], + token_provider + ), Approaches.CompareWorkWithWeb: CompareWorkWithWeb( model_name, ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["TARGET_TRANSLATION_LANGUAGE"], ENV["BING_SEARCH_ENDPOINT"], ENV["BING_SEARCH_KEY"], - str_to_bool.get(ENV["ENABLE_BING_SAFE_SEARCH"]) - ), + str_to_bool.get(ENV["ENABLE_BING_SAFE_SEARCH"]), + ENV["AZURE_OPENAI_ENDPOINT"], + token_provider + ), Approaches.CompareWebWithWork: CompareWebWithWork( search_client, ENV["AZURE_OPENAI_ENDPOINT"], - ENV["AZURE_OPENAI_SERVICE_KEY"], ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["KB_FIELDS_SOURCEFILE"], ENV["KB_FIELDS_CONTENT"], @@ -260,14 +251,12 @@ class StatusResponse(pydantic.BaseModel): ENV["ENRICHMENT_APPSERVICE_URL"], ENV["TARGET_TRANSLATION_LANGUAGE"], ENV["AZURE_AI_ENDPOINT"], - ENV["AZURE_AI_KEY"], ENV["AZURE_AI_LOCATION"], - ENV["AZURE_AI_TRANSLATION_DOMAIN"], + token_provider, str_to_bool.get(ENV["USE_SEMANTIC_RERANKER"]) ), Approaches.GPTDirect: GPTDirectApproach( - ENV["AZURE_OPENAI_SERVICE"], - ENV["AZURE_OPENAI_SERVICE_KEY"], + token_provider, ENV["AZURE_OPENAI_CHATGPT_DEPLOYMENT"], ENV["QUERY_TERM_LANGUAGE"], model_name, diff --git a/app/backend/approaches/chatreadretrieveread.py b/app/backend/approaches/chatreadretrieveread.py index 783d7deb8..9303c6ff1 100644 --- a/app/backend/approaches/chatreadretrieveread.py +++ b/app/backend/approaches/chatreadretrieveread.py @@ -88,7 +88,6 @@ def __init__( self, search_client: SearchClient, oai_endpoint: str, - oai_service_key: str, chatgpt_deployment: str, source_file_field: str, content_field: str, @@ -103,11 +102,9 @@ def __init__( enrichment_appservice_uri: str, target_translation_language: str, azure_ai_endpoint:str, - azure_ai_key:str, azure_ai_location:str, - azure_ai_translation_domain: str, + azure_ai_token_provider:str, use_semantic_reranker: bool - ): self.search_client = search_client self.chatgpt_deployment = chatgpt_deployment @@ -123,21 +120,19 @@ def __init__( self.escaped_target_model = re.sub(r'[^a-zA-Z0-9_\-.]', '_', target_embedding_model) self.target_translation_language=target_translation_language self.azure_ai_endpoint=azure_ai_endpoint - self.azure_ai_key=azure_ai_key self.azure_ai_location=azure_ai_location + self.azure_ai_token_provider=azure_ai_token_provider self.oai_endpoint=oai_endpoint self.embedding_service_url = enrichment_appservice_uri - self.azure_ai_translation_domain=azure_ai_translation_domain self.use_semantic_reranker=use_semantic_reranker openai.api_base = oai_endpoint openai.api_type = 'azure' - openai.api_key = oai_service_key openai.api_version = "2024-02-01" self.client = AsyncAzureOpenAI( - azure_endpoint = openai.api_base, - api_key=openai.api_key, + azure_endpoint = openai.api_base, + azure_ad_token_provider=azure_ai_token_provider, api_version=openai.api_version) @@ -222,10 +217,10 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] response = requests.post(url, json=data,headers=headers,timeout=60) if response.status_code == 200: response_data = response.json() - embedded_query_vector =response_data.get('data') + embedded_query_vector =response_data.get('data') else: # Generate an error message if the embedding generation fails - log.error(f"Error generating embedding:: {response.status_code}") + log.error(f"Error generating embedding:: {response.status_code} - {response.text}") yield json.dumps({"error": "Error generating embedding"}) + "\n" return # Go no further except Exception as e: @@ -444,9 +439,9 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] def detect_language(self, text: str) -> str: """ Function to detect the language of the text""" try: - api_detect_endpoint = f"https://{self.azure_ai_translation_domain}/detect?api-version=3.0" + api_detect_endpoint = f'{self.azure_ai_endpoint}translator/text/v3.0/detect' headers = { - 'Ocp-Apim-Subscription-Key': self.azure_ai_key, + 'Authorization': f'Bearer {self.azure_ai_token_provider()}', 'Content-type': 'application/json', 'Ocp-Apim-Subscription-Region': self.azure_ai_location } @@ -457,15 +452,15 @@ def detect_language(self, text: str) -> str: detected_language = response.json()[0]['language'] return detected_language else: - raise Exception(f"Error detecting language: {response.status_code}") + raise Exception(f"Error detecting language: {response.status_code} - {response.text}") except Exception as e: raise Exception(f"An error occurred during language detection: {str(e)}") from e def translate_response(self, response: str, target_language: str) -> str: """ Function to translate the response to target language""" - api_translate_endpoint = f"https://{self.azure_ai_translation_domain}/translate?api-version=3.0" + api_translate_endpoint = f"{self.azure_ai_endpoint}translator/text/v3.0/translate" headers = { - 'Ocp-Apim-Subscription-Key': self.azure_ai_key, + 'Authorization': f'Bearer {self.azure_ai_token_provider()}', 'Content-type': 'application/json', 'Ocp-Apim-Subscription-Region': self.azure_ai_location } diff --git a/app/backend/approaches/chatwebretrieveread.py b/app/backend/approaches/chatwebretrieveread.py index 66141c4a3..680e89f49 100644 --- a/app/backend/approaches/chatwebretrieveread.py +++ b/app/backend/approaches/chatwebretrieveread.py @@ -74,7 +74,15 @@ class ChatWebRetrieveRead(Approach): citations = {} approach_class = "" - def __init__(self, model_name: str, chatgpt_deployment: str, query_term_language: str, bing_search_endpoint: str, bing_search_key: str, bing_safe_search: bool): + def __init__(self, model_name: str, + chatgpt_deployment: str, + query_term_language: str, + bing_search_endpoint: str, + bing_search_key: str, + bing_safe_search: bool, + oai_endpoint: str, + azure_ai_token_provider:str + ): self.name = "ChatBingSearch" self.model_name = model_name self.chatgpt_deployment = chatgpt_deployment @@ -84,14 +92,14 @@ def __init__(self, model_name: str, chatgpt_deployment: str, query_term_language self.bing_search_key = bing_search_key self.bing_safe_search = bing_safe_search - # openai.api_base = oai_endpoint + openai.api_base = oai_endpoint openai.api_type = 'azure' openai.api_version = "2024-02-01" self.client = AsyncAzureOpenAI( azure_endpoint = openai.api_base , - api_key=openai.api_key, + azure_ad_token_provider=azure_ai_token_provider, api_version=openai.api_version) diff --git a/app/backend/approaches/comparewebwithwork.py b/app/backend/approaches/comparewebwithwork.py index d6f25af2a..e3cc453de 100644 --- a/app/backend/approaches/comparewebwithwork.py +++ b/app/backend/approaches/comparewebwithwork.py @@ -46,7 +46,6 @@ def __init__( self, search_client: SearchClient, oai_service_name: str, - oai_service_key: str, chatgpt_deployment: str, source_file_field: str, content_field: str, @@ -61,9 +60,8 @@ def __init__( enrichment_appservice_url: str, target_translation_language: str, azure_ai_endpoint:str, - azure_ai_key:str, azure_ai_location: str, - azure_ai_translation_domain: str, + azure_ai_token_provider: str, use_semantic_reranker: bool ): self.search_client = search_client @@ -79,14 +77,12 @@ def __init__( self.escaped_target_model = re.sub(r'[^a-zA-Z0-9_\-.]', '_', target_embedding_model) self.target_translation_language=target_translation_language self.azure_ai_endpoint=azure_ai_endpoint - self.azure_ai_key=azure_ai_key self.azure_ai_location = azure_ai_location + self.azure_ai_token_provider=azure_ai_token_provider self.oai_service_name = oai_service_name - self.oai_service_key = oai_service_key self.model_name = model_name self.model_version = model_version self.enrichment_appservice_url = enrichment_appservice_url - self.azure_ai_translation_domain = azure_ai_translation_domain self.use_semantic_reranker = use_semantic_reranker # openai.api_base = oai_endpoint @@ -95,7 +91,7 @@ def __init__( self.client = AsyncAzureOpenAI( azure_endpoint = openai.api_base, - api_key=openai.api_key, + azure_ad_token_provider=azure_ai_token_provider, api_version=openai.api_version) async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any], web_citation_lookup: dict[str, Any], thought_chain: dict[str, Any]) -> Any: @@ -112,7 +108,6 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] chat_rrr_approach = ChatReadRetrieveReadApproach( self.search_client, self.oai_service_name, - self.oai_service_key, self.chatgpt_deployment, self.source_file_field, self.content_field, @@ -128,8 +123,7 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] self.target_translation_language, self.azure_ai_endpoint, self.azure_ai_location, - self.azure_ai_key, - self.azure_ai_translation_domain, + self.azure_ai_token_provider, self.use_semantic_reranker ) rrr_response = chat_rrr_approach.run(history, overrides, {}, thought_chain) diff --git a/app/backend/approaches/compareworkwithweb.py b/app/backend/approaches/compareworkwithweb.py index a58b76b57..5334698e2 100644 --- a/app/backend/approaches/compareworkwithweb.py +++ b/app/backend/approaches/compareworkwithweb.py @@ -39,7 +39,15 @@ class CompareWorkWithWeb(Approach): web_citations = {} - def __init__(self, model_name: str, chatgpt_deployment: str, query_term_language: str, bing_search_endpoint: str, bing_search_key: str, bing_safe_search: bool): + def __init__(self, model_name: str, + chatgpt_deployment: str, + query_term_language: str, + bing_search_endpoint: str, + bing_search_key: str, + bing_safe_search: bool, + oai_endpoint: str, + azure_ai_token_provider:str + ): """ Initializes the CompareWorkWithWeb approach. @@ -59,14 +67,16 @@ def __init__(self, model_name: str, chatgpt_deployment: str, query_term_language self.bing_search_endpoint = bing_search_endpoint self.bing_search_key = bing_search_key self.bing_safe_search = bing_safe_search + self.oai_endpoint = oai_endpoint + self.azure_ai_token_provider = azure_ai_token_provider # openai.api_base = oai_endpoint openai.api_type = 'azure' openai.api_version = "2024-02-01" self.client = AsyncAzureOpenAI( - azure_endpoint = openai.api_base, - api_key=openai.api_key, + azure_endpoint = openai.api_base, + azure_ad_token_provider=azure_ai_token_provider, api_version=openai.api_version) async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any], work_citation_lookup: dict[str, Any], thought_chain: dict[str, Any]) -> Any: @@ -81,7 +91,14 @@ async def run(self, history: Sequence[dict[str, str]], overrides: dict[str, Any] Any: The result of the comparative analysis. """ # Step 1: Call bing Search Approach for a Bing LLM Response and Citations - chat_bing_search = ChatWebRetrieveRead(self.model_name, self.chatgpt_deployment, self.query_term_language, self.bing_search_endpoint, self.bing_search_key, self.bing_safe_search) + chat_bing_search = ChatWebRetrieveRead(self.model_name, + self.chatgpt_deployment, + self.query_term_language, + self.bing_search_endpoint, + self.bing_search_key, + self.bing_safe_search, + self.oai_endpoint, + self.azure_ai_token_provider) bing_search_response = chat_bing_search.run(history, overrides, {}, thought_chain) content = "" diff --git a/app/backend/approaches/gpt_direct_approach.py b/app/backend/approaches/gpt_direct_approach.py index a7bf3738c..711def7a3 100644 --- a/app/backend/approaches/gpt_direct_approach.py +++ b/app/backend/approaches/gpt_direct_approach.py @@ -66,8 +66,7 @@ class GPTDirectApproach(Approach): def __init__( self, - oai_service_name: str, - oai_service_key: str, + azure_openai_token_provider: str, chatgpt_deployment: str, query_term_language: str, model_name: str, @@ -79,19 +78,16 @@ def __init__( self.chatgpt_token_limit = get_token_limit(model_name) openai.api_base = azure_openai_endpoint - openai.api_type = 'azure' - openai.api_key = oai_service_key + openai.api_type = "azure_ad" + openai.azure_ad_token_provider = azure_openai_token_provider + openai.api_version = "2024-02-01" self.model_name = model_name self.model_version = model_version - - openai.api_type = 'azure' - openai.api_version = "2024-02-01" - self.client = AsyncAzureOpenAI( azure_endpoint = openai.api_base, - api_key=openai.api_key, + azure_ad_token_provider=azure_openai_token_provider, api_version=openai.api_version) # def run(self, history: list[dict], overrides: dict) -> any: diff --git a/app/backend/approaches/mathassistant.py b/app/backend/approaches/mathassistant.py index 44ebe978d..3816cdef0 100644 --- a/app/backend/approaches/mathassistant.py +++ b/app/backend/approaches/mathassistant.py @@ -8,44 +8,32 @@ import os # import openai from dotenv import load_dotenv - -#-------------------------------------------------------------------------- -#variables needed for testing -OPENAI_API_TYPE = "azure" -OPENAI_API_VERSION = "2024-02-01" -OPENAI_API_BASE = " " -OPENAI_API_KEY = " " -OPENAI_DEPLOYMENT_NAME = " " -MODEL_NAME = " " -AZURE_OPENAI_ENDPOINT = ' ' -AZURE_OPENAI_SERVICE_KEY = ' ' - -os.environ["OPENAI_API_TYPE"] = OPENAI_API_TYPE -os.environ["OPENAI_API_VERSION"] = OPENAI_API_VERSION - +from langchain_openai import AzureChatOpenAI +from langchain.agents import initialize_agent, load_tools, AgentType +from langchain.prompts import ChatPromptTemplate +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider load_dotenv() - -azure_openai_chatgpt_deployment = os.getenv("AZURE_OPENAI_CHATGPT_DEPLOYMENT") - -deployment_name = azure_openai_chatgpt_deployment -OPENAI_DEPLOYMENT_NAME = deployment_name - OPENAI_API_BASE = os.environ.get("AZURE_OPENAI_ENDPOINT") -OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_SERVICE_KEY") -OPENAI_DEPLOYMENT_NAME = azure_openai_chatgpt_deployment +OPENAI_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_CHATGPT_DEPLOYMENT") -from langchain_openai import AzureChatOpenAI -from langchain.agents import initialize_agent, load_tools, AgentType -from langchain.prompts import ChatPromptTemplate +if os.environ.get("AZURE_OPENAI_AUTHORITY_HOST") == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD +if os.environ.get("LOCAL_DEBUG") == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +token_provider = get_bearer_token_provider(azure_credential, f'https://{os.environ.get("AZURE_AI_CREDENTIAL_DOMAIN")}/.default') model = AzureChatOpenAI( - api_key= OPENAI_API_KEY, + azure_ad_token_provider=token_provider, azure_endpoint=OPENAI_API_BASE, - openai_api_version=OPENAI_API_VERSION , - deployment_name=OPENAI_DEPLOYMENT_NAME) + openai_api_version="2024-02-01" , + deployment_name=OPENAI_DEPLOYMENT_NAME) #-------------------------------------------------------------------------------------------------------------------------------------------------- # Addition of custom tools diff --git a/app/enrichment/app.py b/app/enrichment/app.py index 34b12c103..ce3b49fda 100644 --- a/app/enrichment/app.py +++ b/app/enrichment/app.py @@ -14,8 +14,7 @@ import random from azure.storage.queue import QueueClient, TextBase64EncodePolicy from azure.search.documents import SearchClient -from azure.core.credentials import AzureKeyCredential -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, DefaultAzureCredential, get_bearer_token_provider, AzureAuthorityHosts from data_model import (EmbeddingResponse, ModelInfo, ModelListResponse, StatusResponse) from fastapi import FastAPI, HTTPException @@ -47,16 +46,16 @@ "MAX_EMBEDDING_REQUEUE_COUNT": 5, "EMBEDDING_REQUEUE_BACKOFF": 60, "AZURE_OPENAI_SERVICE": None, - "AZURE_OPENAI_SERVICE_KEY": None, "AZURE_OPENAI_ENDPOINT": None, "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME": None, "AZURE_SEARCH_INDEX": None, - "AZURE_SEARCH_SERVICE_KEY": None, "AZURE_SEARCH_SERVICE": None, "TARGET_EMBEDDINGS_MODEL": None, "EMBEDDING_VECTOR_SIZE": None, "AZURE_SEARCH_SERVICE_ENDPOINT": None, - "AZURE_BLOB_STORAGE_ENDPOINT": None + "LOCAL_DEBUG": "false", + "AZURE_AI_CREDENTIAL_DOMAIN": None, + "AZURE_OPENAI_AUTHORITY_HOST": None } for key, value in ENV.items(): @@ -66,16 +65,33 @@ elif value is None: raise ValueError(f"Environment variable {key} not set") -search_creds = AzureKeyCredential(ENV["AZURE_SEARCH_SERVICE_KEY"]) - openai.api_base = ENV["AZURE_OPENAI_ENDPOINT"] openai.api_type = "azure" -openai.api_key = ENV["AZURE_OPENAI_SERVICE_KEY"] +if ENV["AZURE_OPENAI_AUTHORITY_HOST"] == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD openai.api_version = "2024-02-01" +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if ENV["LOCAL_DEBUG"] == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +# Comment these two lines out if using keys, set your API key in the OPENAI_API_KEY environment variable instead +openai.api_type = "azure_ad" +token_provider = get_bearer_token_provider(azure_credential, + f'https://{ENV["AZURE_AI_CREDENTIAL_DOMAIN"]}/.default') +openai.azure_ad_token_provider = token_provider +#openai.api_key = ENV["AZURE_OPENAI_SERVICE_KEY"] + client = AzureOpenAI( - azure_endpoint = openai.api_base, - api_key=openai.api_key, + azure_endpoint = openai.api_base, + azure_ad_token_provider=token_provider, api_version=openai.api_version) class AzOAIEmbedding(object): @@ -117,6 +133,7 @@ def encode(self, texts) -> None: utilities_helper = UtilitiesHelper( azure_blob_storage_account=ENV["AZURE_BLOB_STORAGE_ACCOUNT"], azure_blob_storage_endpoint=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + credential=azure_credential ) statusLog = StatusLog(ENV["COSMOSDB_URL"], ENV["COSMOSDB_KEY"], ENV["COSMOSDB_LOG_DATABASE_NAME"], ENV["COSMOSDB_LOG_CONTAINER_NAME"]) @@ -124,8 +141,6 @@ def encode(self, texts) -> None: start_time = datetime.now() -azure_credential = ManagedIdentityCredential() - IS_READY = False #download models @@ -256,7 +271,7 @@ def index_sections(chunks): """ search_client = SearchClient(endpoint=ENV["AZURE_SEARCH_SERVICE_ENDPOINT"], index_name=ENV["AZURE_SEARCH_INDEX"], - credential=search_creds) + credential=azure_credential) results = search_client.upload_documents(documents=chunks) succeeded = sum([1 for r in results if r.succeeded]) @@ -305,7 +320,7 @@ def poll_queue() -> None: log.debug("Skipping poll_queue call, models not yet loaded") return - queue_client = QueueClient(account_url=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + queue_client = QueueClient(account_url=ENV["AZURE_QUEUE_STORAGE_ENDPOINT"], queue_name=ENV["EMBEDDINGS_QUEUE"], credential=azure_credential) diff --git a/docs/deployment/deployment.md b/docs/deployment/deployment.md index 9906e2921..5dbcc24b4 100644 --- a/docs/deployment/deployment.md +++ b/docs/deployment/deployment.md @@ -67,7 +67,6 @@ SKIP_PLAN_CHECK | No | If this value is set to 1, then the Terraform deployment USE_EXISTING_AOAI | Yes | Defaults to false. Set this value to "true" if you want to use an existing Azure Open AI service instance in your subscription. This can be useful when there are limits to the number of AOAI instances you can have in one subscription. When the value is set to "false" and Terraform will create a new Azure Open AI service instance in your resource group. AZURE_OPENAI_RESOURCE_GROUP | No | If you have set **USE_EXISTING_AOAI** to "true" then use this parameter to provide the name of the resource group that hosts the Azure Open AI service instance in your subscription. AZURE_OPENAI_SERVICE_NAME | No | If you have set **USE_EXISTING_AOAI** to "true" then use this parameter to provide the name of the Azure Open AI service instance in your subscription. -AZURE_OPENAI_SERVICE_KEY | No | If you have set **USE_EXISTING_AOAI** to "true" then use this parameter to provide the Key for the Azure Open AI service instance in your subscription. AZURE_OPENAI_CHATGPT_DEPLOYMENT | No | If you have set **USE_EXISTING_AOAI** to "true" then use this parameter to provide the name of a deployment of the "gpt-35-turbo" model in the Azure Open AI service instance in your subscription. USE_AZURE_OPENAI_EMBEDDINGS | Yes | Defaults to "true". When set to "true" this value indicates to Information Assistant to use Azure OpenAI models for embedding text values. If set to "false", Information Assistant will use the open source language model that is provided in the values below. AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME| No | If you have set **USE_AZURE_OPENAI_EMBEDDINGS** to "true" then use this parameter to provide the name of a deployment of the "text-embedding-ada-002" model in the Azure Open AI service instance in your subscription. diff --git a/docs/deployment/setting_up_sandbox_environment.md b/docs/deployment/setting_up_sandbox_environment.md index a57522c6b..895cd989e 100644 --- a/docs/deployment/setting_up_sandbox_environment.md +++ b/docs/deployment/setting_up_sandbox_environment.md @@ -49,7 +49,7 @@ To set up an Azure DevOps CI/CD pipeline for deploying code from a GitHub reposi SUBSCRIPTION_ID | The ID of the subscription that should be deployed to. TENANT_ID | The ID of the tenant that should be deployed to. CONTAINER_REGISTRY_ADDRESS | Azure Container Registry where the Info Assistant development container will be cached during pipeline runs - AZURE_OPENAI_SERVICE_NAME
AZURE_OPENAI_SERVICE_KEY
AZURE_OPENAI_CHATGPT_DEPLOYMENT
AZURE_OPENAI_GPT_DEPLOYMENT | It is recommended to point the pipeline to an existing installation of Azure OpenAI. These values will be used to target that instance. + AZURE_OPENAI_SERVICE_NAME
AZURE_OPENAI_CHATGPT_DEPLOYMENT
AZURE_OPENAI_GPT_DEPLOYMENT | It is recommended to point the pipeline to an existing installation of Azure OpenAI. These values will be used to target that instance. environment | The environment name that matches an environment variable file located in `./scripts/environments`. For example if the pipeline parameter is set to "demo" there needs to be a corresponding file at `/scripts/environment/demo.env` TF_BACKEND_ACCESS_KEY | Terraform is used to create Infrastructure as Code. This is the key to the Terraform State in a Storage Account. TF_BACKEND_CONTAINER | Terraform is used to create Infrastructure as Code. This is the container that the Terraform State is stored within a Storage Account. diff --git a/functions/FileDeletion/__init__.py b/functions/FileDeletion/__init__.py index 00772eb40..8ec91c4bb 100644 --- a/functions/FileDeletion/__init__.py +++ b/functions/FileDeletion/__init__.py @@ -6,10 +6,9 @@ from datetime import datetime, timezone from itertools import islice import azure.functions as func -from azure.core.credentials import AzureKeyCredential from azure.search.documents import SearchClient from azure.storage.blob import BlobServiceClient -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from shared_code.status_log import State, StatusClassification, StatusLog azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] @@ -19,18 +18,33 @@ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_search_service_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] azure_search_index = os.environ["AZURE_SEARCH_INDEX"] -azure_search_service_key = os.environ["AZURE_SEARCH_SERVICE_KEY"] cosmosdb_url = os.environ["COSMOSDB_URL"] cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] +local_debug = os.environ["LOCAL_DEBUG"] or "false" +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] +azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] status_log = StatusLog(cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name) -azure_credential = ManagedIdentityCredential() +if azure_openai_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD + +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if local_debug == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) def chunks(data, size): '''max number of blobs to delete in one request is 256, so this breaks @@ -102,7 +116,7 @@ def delete_search_entries(deleted_content_blobs: dict) -> None: Search index.''' search_client = SearchClient(azure_search_service_endpoint, azure_search_index, - AzureKeyCredential(azure_search_service_key)) + azure_credential) search_id_list_to_delete = [] for file_path in deleted_content_blobs.keys(): diff --git a/functions/FileFormRecPollingPDF/__init__.py b/functions/FileFormRecPollingPDF/__init__.py index 106a8c54c..02dff2c3e 100644 --- a/functions/FileFormRecPollingPDF/__init__.py +++ b/functions/FileFormRecPollingPDF/__init__.py @@ -9,7 +9,7 @@ import time import azure.functions as func from azure.storage.queue import QueueClient, TextBase64EncodePolicy -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider import requests from shared_code.status_log import StatusLog, State, StatusClassification from shared_code.utilities import Utilities, MediaType @@ -46,13 +46,29 @@ def string_to_bool(s): polling_backoff = int(os.environ["POLLING_BACKOFF"]) max_read_attempts = int(os.environ["MAX_READ_ATTEMPTS"]) enableDevCode = string_to_bool(os.environ["ENABLE_DEV_CODE"]) +local_debug = os.environ["LOCAL_DEBUG"] +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] +azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] function_name = "FileFormRecPollingPDF" -utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container) FR_MODEL = "prebuilt-layout" -azure_credential = ManagedIdentityCredential() +if azure_openai_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if local_debug == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) + +utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, azure_credential) def main(msg: func.QueueMessage) -> None: diff --git a/functions/FileFormRecSubmissionPDF/__init__.py b/functions/FileFormRecSubmissionPDF/__init__.py index 8a561b1c1..795911564 100644 --- a/functions/FileFormRecSubmissionPDF/__init__.py +++ b/functions/FileFormRecSubmissionPDF/__init__.py @@ -8,7 +8,7 @@ import azure.functions as func import requests from azure.storage.queue import QueueClient, TextBase64EncodePolicy -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from shared_code.status_log import State, StatusClassification, StatusLog from shared_code.utilities import Utilities @@ -32,18 +32,36 @@ max_submit_requeue_count = int(os.environ["MAX_SUBMIT_REQUEUE_COUNT"]) poll_queue_submit_backoff = int(os.environ["POLL_QUEUE_SUBMIT_BACKOFF"]) pdf_submit_queue_backoff = int(os.environ["PDF_SUBMIT_QUEUE_BACKOFF"]) +local_debug = os.environ["LOCAL_DEBUG"] +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] +azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] +FUNCTION_NAME = "FileFormRecSubmissionPDF" +FR_MODEL = "prebuilt-layout" + +if azure_openai_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD + +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if local_debug == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) + utilities = Utilities( azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, + azure_credential, ) -FUNCTION_NAME = "FileFormRecSubmissionPDF" -FR_MODEL = "prebuilt-layout" - -azure_credential = ManagedIdentityCredential() def main(msg: func.QueueMessage) -> None: '''This function is triggered by a message in the pdf-submit-queue. diff --git a/functions/FileLayoutParsingOther/__init__.py b/functions/FileLayoutParsingOther/__init__.py index 445ca5253..84c05aff0 100644 --- a/functions/FileLayoutParsingOther/__init__.py +++ b/functions/FileLayoutParsingOther/__init__.py @@ -9,7 +9,7 @@ import azure.functions as func from azure.storage.blob import generate_blob_sas from azure.storage.queue import QueueClient, TextBase64EncodePolicy -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from shared_code.status_log import StatusLog, State, StatusClassification from shared_code.utilities import Utilities, MediaType @@ -29,12 +29,28 @@ pdf_submit_queue = os.environ["PDF_SUBMIT_QUEUE"] text_enrichment_queue = os.environ["TEXT_ENRICHMENT_QUEUE"] CHUNK_TARGET_SIZE = int(os.environ["CHUNK_TARGET_SIZE"]) +local_debug = os.environ["LOCAL_DEBUG"] +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] +azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] - -utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container) function_name = "FileLayoutParsingOther" -azure_credential = ManagedIdentityCredential() +if azure_openai_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD + +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if local_debug == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) + +utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, azure_credential) class UnstructuredError(Exception): pass diff --git a/functions/FileUploadedFunc/__init__.py b/functions/FileUploadedFunc/__init__.py index 341fde7f2..744880a6b 100644 --- a/functions/FileUploadedFunc/__init__.py +++ b/functions/FileUploadedFunc/__init__.py @@ -10,9 +10,8 @@ import azure.functions as func from azure.storage.blob import BlobServiceClient from azure.storage.queue import QueueClient, TextBase64EncodePolicy -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from azure.search.documents import SearchClient -from azure.core.credentials import AzureKeyCredential from shared_code.utilities_helper import UtilitiesHelper from urllib.parse import unquote @@ -33,21 +32,34 @@ azure_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_search_service_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] azure_search_service_index = os.environ["AZURE_SEARCH_INDEX"] -azure_search_service_key = os.environ["AZURE_SEARCH_SERVICE_KEY"] - +local_debug = os.environ["LOCAL_DEBUG"] +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] +azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] +if azure_openai_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if local_debug == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) function_name = "FileUploadedFunc" utilities_helper = UtilitiesHelper( azure_blob_storage_account=azure_storage_account, azure_blob_storage_endpoint=azure_blob_endpoint, + credential=azure_credential ) statusLog = StatusLog(cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name) -azure_credential = ManagedIdentityCredential() - def get_tags_and_upload_to_cosmos(blob_service_client, blob_path): """ Gets the tags from the blob metadata and uploads them to cosmos db""" file_name, file_extension, file_directory = utilities_helper.get_filename_and_extension(blob_path) @@ -142,7 +154,7 @@ def main(myblob: func.InputStream): # instantiate the search sdk elements search_client = SearchClient(azure_search_service_endpoint, azure_search_service_index, - AzureKeyCredential(azure_search_service_key)) + azure_credential) search_id_list_to_delete = [] # Iterate through the blobs and delete each one from blob and the search index diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 7081eb4a1..39fdfdc42 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -6,11 +6,10 @@ import azure.functions as func import requests from azure.storage.blob import BlobServiceClient -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, DefaultAzureCredential, get_bearer_token_provider, AzureAuthorityHosts from shared_code.status_log import State, StatusClassification, StatusLog from shared_code.utilities import Utilities, MediaType from azure.search.documents import SearchClient -from azure.core.credentials import AzureKeyCredential from datetime import datetime @@ -28,7 +27,9 @@ azure_blob_content_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] -azure_ai_translation_domain = os.environ["AZURE_AI_TRANSLATION_DOMAIN"] +# Authentication settings +azure_authority_host = os.environ["AZURE_AUTHORITY_HOST"] +local_debug = os.environ.get("LOCAL_DEBUG", False) # Cosmos DB cosmosdb_url = os.environ["COSMOSDB_URL"] @@ -37,35 +38,46 @@ cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] # Cognitive Services -cognitive_services_key = os.environ["AZURE_AI_KEY"] -cognitive_services_endpoint = os.environ["AZURE_AI_ENDPOINT"] -cognitive_services_account_location = os.environ["AZURE_AI_LOCATION"] +azure_ai_key = os.environ["AZURE_AI_KEY"] +azure_ai_endpoint = os.environ["AZURE_AI_ENDPOINT"] +azure_ai_location = os.environ["AZURE_AI_LOCATION"] +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] # Search Service AZURE_SEARCH_SERVICE_ENDPOINT = os.environ.get("AZURE_SEARCH_SERVICE_ENDPOINT") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") or "gptkbindex" -SEARCH_CREDS = AzureKeyCredential(os.environ.get("AZURE_SEARCH_SERVICE_KEY")) + +if azure_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD +if local_debug: + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +token_provider = get_bearer_token_provider(azure_credential, + f'https://{azure_ai_credential_domain}/.default') # Translation params for OCR'd text targetTranslationLanguage = os.environ["TARGET_TRANSLATION_LANGUAGE"] API_DETECT_ENDPOINT = ( - f"https://{azure_ai_translation_domain}/detect?api-version=3.0" + f"{azure_ai_endpoint}translator/text/v3.0/detect" ) API_TRANSLATE_ENDPOINT = ( - f"https://{azure_ai_translation_domain}/translate?api-version=3.0" + f"{azure_ai_endpoint}translator/text/v3.0/translate" ) MAX_CHARS_FOR_DETECTION = 1000 translator_api_headers = { - "Ocp-Apim-Subscription-Key": cognitive_services_key, + "Auhorization": f"Bearer {token_provider()}", "Content-type": "application/json", - "Ocp-Apim-Subscription-Region": cognitive_services_account_location, + "Ocp-Apim-Subscription-Region": azure_ai_location, } # Vision SDK vision_service_options = visionsdk.VisionServiceOptions( - endpoint=cognitive_services_endpoint, key=cognitive_services_key + endpoint=azure_ai_endpoint, key=azure_ai_key ) analysis_options = visionsdk.ImageAnalysisOptions() @@ -74,7 +86,7 @@ # Korea Central, North Europe, Southeast Asia, West Europe, West US). Remove "CAPTION" and "DENSE_CAPTIONS" # from the list below if your Computer Vision key is not from one of those regions. -if cognitive_services_account_location in [ +if azure_ai_location in [ "eastus", "francecentral", "koreacentral", @@ -104,13 +116,12 @@ FUNCTION_NAME = "ImageEnrichment" -azure_credential = ManagedIdentityCredential() - utilities = Utilities( azure_blob_storage_account=azure_blob_storage_account, azure_blob_storage_endpoint=azure_blob_storage_endpoint, azure_blob_drop_storage_container=azure_blob_drop_storage_container, azure_blob_content_storage_container=azure_blob_content_storage_container, + azure_credential ) @@ -354,6 +365,6 @@ def index_section(index_content, file_name, file_directory, chunk_id, chunk_file search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE_ENDPOINT, index_name=AZURE_SEARCH_INDEX, - credential=SEARCH_CREDS) + credential=azure_credential) search_client.upload_documents(documents=batch) diff --git a/functions/TextEnrichment/__init__.py b/functions/TextEnrichment/__init__.py index 4e4376aef..d11d0d157 100644 --- a/functions/TextEnrichment/__init__.py +++ b/functions/TextEnrichment/__init__.py @@ -1,7 +1,7 @@ import logging import azure.functions as func from azure.storage.queue import QueueClient, TextBase64EncodePolicy -from azure.identity import ManagedIdentityCredential +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from azure.storage.blob import BlobServiceClient from shared_code.utilities import Utilities import os @@ -28,43 +28,56 @@ cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] text_enrichment_queue = os.environ["TEXT_ENRICHMENT_QUEUE"] -enrichmentKey = os.environ["AZURE_AI_KEY"] -enrichmentEndpoint = os.environ["AZURE_AI_ENDPOINT"] -targetTranslationLanguage = os.environ["TARGET_TRANSLATION_LANGUAGE"] +azure_ai_endpoint = os.environ["AZURE_AI_ENDPOINT"] +targetTranslationLanguage = os.environ["TARGET_TRANSLATION_LANGUAGE"] max_requeue_count = int(os.environ["MAX_ENRICHMENT_REQUEUE_COUNT"]) enrichment_backoff = int(os.environ["ENRICHMENT_BACKOFF"]) azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] queueName = os.environ["EMBEDDINGS_QUEUE"] -azure_ai_translation_domain = os.environ["AZURE_AI_TRANSLATION_DOMAIN"] -azure_ai_text_analytics_domain = os.environ["AZURE_AI_TEXT_ANALYTICS_DOMAIN"] -endpoint_region = os.environ["AZURE_AI_LOCATION"] -enrich_endpoint = os.environ["AZURE_AI_ENDPOINT"] +azure_ai_location = os.environ["AZURE_AI_LOCATION"] +local_debug = os.environ["LOCAL_DEBUG"] +azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] +azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] FUNCTION_NAME = "TextEnrichment" MAX_CHARS_FOR_DETECTION = 1000 +statusLog = StatusLog( + cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name +) + +if azure_openai_authority_host == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD + +# When debugging in VSCode, use the current user identity to authenticate with Azure OpenAI, +# Cognitive Search and Blob Storage (no secrets needed, just use 'az login' locally) +# Use managed identity when deployed on Azure. +# If you encounter a blocking error during a DefaultAzureCredntial resolution, you can exclude +# the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True) +if local_debug == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +token_provider = get_bearer_token_provider(azure_credential, f'https://{azure_ai_credential_domain}/.default') + utilities = Utilities( azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, + azure_credential, ) - -statusLog = StatusLog( - cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name -) - -azure_credential = ManagedIdentityCredential() - def main(msg: func.QueueMessage) -> None: '''This function is triggered by a message in the text-enrichment-queue. It will first determine the language, and if this differs from the target language, it will translate the chunks to the target language.''' try: - apiDetectEndpoint = f"https://{azure_ai_translation_domain}/detect?api-version=3.0" - apiTranslateEndpoint = f"https://{azure_ai_translation_domain}/translate?api-version=3.0" + apiDetectEndpoint = f"{azure_ai_endpoint}translator/text/v3.0/detect" + apiTranslateEndpoint = f"{azure_ai_endpoint}translator/text/v3.0/translate" message_body = msg.get_body().decode("utf-8") message_json = json.loads(message_body) @@ -110,9 +123,9 @@ def main(msg: func.QueueMessage) -> None: # detect language headers = { - 'Ocp-Apim-Subscription-Key': enrichmentKey, + 'Authorization': f'Bearer {token_provider()}', 'Content-type': 'application/json', - 'Ocp-Apim-Subscription-Region': endpoint_region + 'Ocp-Apim-Subscription-Region': azure_ai_location } data = [{"text": chunk_content}] @@ -155,11 +168,7 @@ def main(msg: func.QueueMessage) -> None: for field in fields_to_enrich: translate_and_set(field, chunk_dict, headers, params, message_json, detected_language, targetTranslationLanguage, apiTranslateEndpoint) - # Extract entities for index - enrich_headers = { - 'Ocp-Apim-Subscription-Key': enrichmentKey, - 'Content-type': 'application/json' - } + # Extract entities for index target_content = chunk_dict['translated_title'] + " " + chunk_dict['translated_subtitle'] + " " + chunk_dict['translated_section'] + " " + chunk_dict['translated_content'] enrich_data = { "kind": "EntityRecognition", @@ -176,7 +185,7 @@ def main(msg: func.QueueMessage) -> None: ] } } - response = requests.post(enrich_endpoint, headers=enrich_headers, json=enrich_data, params=params) + response = requests.post(azure_ai_endpoint, headers=headers, json=enrich_data, params=params) try: entities = response.json()['results']['documents'][0]['entities'] except: @@ -187,10 +196,6 @@ def main(msg: func.QueueMessage) -> None: chunk_dict[f"entities"] = entities_collection # Extract key phrases for index - enrich_headers = { - 'Ocp-Apim-Subscription-Key': enrichmentKey, - 'Content-type': 'application/json' - } target_content = chunk_dict['translated_title'] + " " + chunk_dict['translated_subtitle'] + " " + chunk_dict['translated_section'] + " " + chunk_dict['translated_content'] enrich_data = { "kind": "KeyPhraseExtraction", @@ -207,7 +212,7 @@ def main(msg: func.QueueMessage) -> None: ] } } - response = requests.post(enrich_endpoint, headers=enrich_headers, json=enrich_data, params=params) + response = requests.post(azure_ai_endpoint, headers=headers, json=enrich_data, params=params) try: key_phrases = response.json()['results']['documents'][0]['keyPhrases'] except: diff --git a/functions/shared_code/utilities.py b/functions/shared_code/utilities.py index 7af75d30b..020c427c0 100644 --- a/functions/shared_code/utilities.py +++ b/functions/shared_code/utilities.py @@ -9,7 +9,6 @@ import zipfile import os from azure.storage.blob import BlobServiceClient -from azure.identity import ManagedIdentityCredential from shared_code.utilities_helper import UtilitiesHelper from nltk.tokenize import sent_tokenize import tiktoken @@ -18,8 +17,6 @@ nltk.download('punkt') from bs4 import BeautifulSoup -azure_credential = ManagedIdentityCredential() - punkt_dir = os.path.join(nltk.data.path[0], 'tokenizers/punkt') # Check if the 'punkt' directory exists @@ -72,14 +69,16 @@ def __init__(self, azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, - azure_blob_content_storage_container + azure_blob_content_storage_container, + azure_credential ): self.azure_blob_storage_account = azure_blob_storage_account self.azure_blob_storage_endpoint = azure_blob_storage_endpoint self.azure_blob_drop_storage_container = azure_blob_drop_storage_container self.azure_blob_content_storage_container = azure_blob_content_storage_container self.utilities_helper = UtilitiesHelper(azure_blob_storage_account, - azure_blob_storage_endpoint) + azure_blob_storage_endpoint, + azure_credential) def write_blob(self, output_container, content, output_filename, folder_set=""): """ Function to write a generic blob """ diff --git a/functions/shared_code/utilities_helper.py b/functions/shared_code/utilities_helper.py index 36f14cc6c..ce04fd46d 100644 --- a/functions/shared_code/utilities_helper.py +++ b/functions/shared_code/utilities_helper.py @@ -6,17 +6,17 @@ import urllib.parse from datetime import datetime, timedelta from azure.storage.blob import generate_blob_sas, BlobSasPermissions, BlobServiceClient -from azure.identity import ManagedIdentityCredential class UtilitiesHelper: """ Helper class for utility functions""" def __init__(self, azure_blob_storage_account, - azure_blob_storage_endpoint + azure_blob_storage_endpoint, + credential ): self.azure_blob_storage_account = azure_blob_storage_account self.azure_blob_storage_endpoint = azure_blob_storage_endpoint - self.blob_service_client = BlobServiceClient(azure_blob_storage_endpoint, credential=ManagedIdentityCredential()) + self.blob_service_client = BlobServiceClient(azure_blob_storage_endpoint, credential=credential) def get_filename_and_extension(self, path): """ Function to return the file name & type""" diff --git a/infra/core/ai/openaiservices/openaiservices.tf b/infra/core/ai/openaiservices/openaiservices.tf index 2afa5dac7..2aec40e03 100644 --- a/infra/core/ai/openaiservices/openaiservices.tf +++ b/infra/core/ai/openaiservices/openaiservices.tf @@ -87,16 +87,4 @@ resource "azurerm_private_endpoint" "openaiPrivateEndpoint" { private_dns_zone_ids = var.private_dns_zone_ids } -} - -module "openaiServiceKeySecret" { - source = "../../security/keyvaultSecret" - key_vault_name = var.key_vault_name - secret_name = "AZURE-OPENAI-SERVICE-KEY" - secret_value = var.useExistingAOAIService ? var.openaiServiceKey : azurerm_cognitive_account.openaiAccount[0].primary_access_key - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - resourceGroupName = var.resourceGroupName - tags = var.tags - alias = "openaikey" - kv_secret_expiration = var.kv_secret_expiration } \ No newline at end of file diff --git a/infra/core/host/enrichmentapp/enrichmentapp.tf b/infra/core/host/enrichmentapp/enrichmentapp.tf index 73481df8c..8713fc084 100644 --- a/infra/core/host/enrichmentapp/enrichmentapp.tf +++ b/infra/core/host/enrichmentapp/enrichmentapp.tf @@ -105,10 +105,7 @@ resource "azurerm_linux_web_app" "enrichmentapp" { "SCM_DO_BUILD_DURING_DEPLOYMENT" = lower(tostring(var.scmDoBuildDuringDeployment)) "ENABLE_ORYX_BUILD" = tostring(var.enableOryxBuild) "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.applicationInsightsConnectionString - "AZURE_SEARCH_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-SEARCH-SERVICE-KEY)" "COSMOSDB_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" - "AZURE_AI_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-AI-KEY)" - "AZURE_OPENAI_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-OPENAI-SERVICE-KEY)" "KEY_EXPIRATION_DATE" = timeadd(timestamp(), "4320h") # Added expiration date setting for keys "WEBSITE_PULL_IMAGE_OVER_VNET" = var.is_secure_mode ? "true" : "false" "WEBSITES_PORT" = "6000" diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 000bf52c1..1e656da5a 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -144,6 +144,7 @@ resource "azurerm_linux_function_app" "function_app" { BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME = var.blobStorageAccountUploadContainerName BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME = var.blobStorageAccountOutputContainerName BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME = var.blobStorageAccountLogContainerName + AZURE_QUEUE_STORAGE_ENDPOINT = var.queueStorageAccountEndpoint CHUNK_TARGET_SIZE = var.chunkTargetSize TARGET_PAGES = var.targetPages FR_API_VERSION = var.formRecognizerApiVersion @@ -175,14 +176,12 @@ resource "azurerm_linux_function_app" "function_app" { ENRICHMENT_BACKOFF = var.enrichmentBackoff ENABLE_DEV_CODE = tostring(var.enableDevCode) EMBEDDINGS_QUEUE = var.EMBEDDINGS_QUEUE - AZURE_SEARCH_SERVICE_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-SEARCH-SERVICE-KEY)" COSMOSDB_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" AZURE_SEARCH_SERVICE_ENDPOINT = var.azureSearchServiceEndpoint AZURE_SEARCH_INDEX = var.azureSearchIndex - AZURE_AI_TRANSLATION_DOMAIN = var.azure_ai_translation_domain - AZURE_AI_TEXT_ANALYTICS_DOMAIN = var.azure_ai_text_analytics_domain WEBSITE_PULL_IMAGE_OVER_VNET = var.is_secure_mode ? "true" : "false" STORAGE_CONNECTION_STRING__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" + AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_credential_domain } } diff --git a/infra/core/host/functions/variables.tf b/infra/core/host/functions/variables.tf index 1fb455148..c41631ae8 100644 --- a/infra/core/host/functions/variables.tf +++ b/infra/core/host/functions/variables.tf @@ -88,6 +88,11 @@ variable "blobStorageAccountLogContainerName" { type = string } +variable "queueStorageAccountEndpoint" { + description = "Azure Queue Storage Account Endpoint" + type = string +} + variable "chunkTargetSize" { description = "Chunk Target Size" type = string @@ -248,14 +253,6 @@ variable "endpointSuffix" { default = "core.windows.net" } -variable "azure_ai_translation_domain" { - type = string -} - -variable "azure_ai_text_analytics_domain" { - type = string -} - variable "vnet_name" { type = string } @@ -322,4 +319,8 @@ variable "logAnalyticsWorkspaceResourceId" { variable "azure_environment" { type = string +} + +variable "azure_ai_credential_domain" { + type = string } \ No newline at end of file diff --git a/infra/core/host/webapp/webapp.tf b/infra/core/host/webapp/webapp.tf index eba807d7e..11fd005a4 100644 --- a/infra/core/host/webapp/webapp.tf +++ b/infra/core/host/webapp/webapp.tf @@ -114,11 +114,8 @@ resource "azurerm_linux_web_app" "app_service" { "SCM_DO_BUILD_DURING_DEPLOYMENT" = lower(tostring(var.scmDoBuildDuringDeployment)) "ENABLE_ORYX_BUILD" = lower(tostring(var.enableOryxBuild)) "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.applicationInsightsConnectionString - "AZURE_SEARCH_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-SEARCH-SERVICE-KEY)" "COSMOSDB_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" "BING_SEARCH_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BINGSEARCH-KEY)" - "AZURE_AI_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-AI-KEY)" - "AZURE_OPENAI_SERVICE_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-OPENAI-SERVICE-KEY)" "WEBSITE_PULL_IMAGE_OVER_VNET" = var.is_secure_mode ? "true" : "false" "WEBSITES_PORT" = "6000" "WEBSITES_CONTAINER_START_TIME_LIMIT" = "1600" diff --git a/infra/core/search/search-services.tf b/infra/core/search/search-services.tf index 1ffeec9a7..02bb827e3 100644 --- a/infra/core/search/search-services.tf +++ b/infra/core/search/search-services.tf @@ -16,18 +16,6 @@ resource "azurerm_search_service" "search" { } } -module "search_service_key" { - source = "../security/keyvaultSecret" - key_vault_name = var.key_vault_name - resourceGroupName = var.resourceGroupName - secret_name = "AZURE-SEARCH-SERVICE-KEY" - secret_value = azurerm_search_service.search.primary_key - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - alias = "searchkey" - tags = var.tags - kv_secret_expiration = var.kv_secret_expiration -} - data "azurerm_subnet" "subnet" { count = var.is_secure_mode ? 1 : 0 name = var.subnet_name diff --git a/infra/core/storage/outputs.tf b/infra/core/storage/outputs.tf index 6fd1c7240..6788bc538 100644 --- a/infra/core/storage/outputs.tf +++ b/infra/core/storage/outputs.tf @@ -2,10 +2,14 @@ output "name" { value = azurerm_storage_account.storage.name } -output "primary_endpoints" { +output "primary_blob_endpoint" { value = azurerm_storage_account.storage.primary_blob_endpoint } +output "primary_queue_endpoint" { + value = azurerm_storage_account.storage.primary_queue_endpoint +} + output "storage_account_id" { value = azurerm_storage_account.storage.id } diff --git a/infra/main.tf b/infra/main.tf index 1e1ab0f11..59e32d8ff 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -312,7 +312,8 @@ module "enrichmentApp" { AZURE_BLOB_STORAGE_ACCOUNT = module.storage.name AZURE_BLOB_STORAGE_CONTAINER = var.contentContainerName AZURE_BLOB_STORAGE_UPLOAD_CONTAINER = var.uploadContainerName - AZURE_BLOB_STORAGE_ENDPOINT = module.storage.primary_endpoints + AZURE_BLOB_STORAGE_ENDPOINT = module.storage.primary_blob_endpoint + AZURE_QUEUE_STORAGE_ENDPOINT = module.storage.primary_queue_endpoint COSMOSDB_URL = module.cosmosdb.CosmosDBEndpointURL COSMOSDB_LOG_DATABASE_NAME = module.cosmosdb.CosmosDBLogDatabaseName COSMOSDB_LOG_CONTAINER_NAME = module.cosmosdb.CosmosDBLogContainerName @@ -326,6 +327,8 @@ module "enrichmentApp" { TARGET_EMBEDDINGS_MODEL = var.useAzureOpenAIEmbeddings ? "azure-openai_${var.azureOpenAIEmbeddingDeploymentName}" : var.sentenceTransformersModelName EMBEDDING_VECTOR_SIZE = var.useAzureOpenAIEmbeddings ? 1536 : var.sentenceTransformerEmbeddingVectorSize AZURE_SEARCH_SERVICE_ENDPOINT = module.searchServices.endpoint + AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_private_link_domain + AZURE_OPENAI_AUTHORITY_HOST = var.azure_openai_authority_host } } @@ -373,7 +376,7 @@ module "webapp" { appSettings = { APPLICATIONINSIGHTS_CONNECTION_STRING = module.logging.applicationInsightsConnectionString AZURE_BLOB_STORAGE_ACCOUNT = module.storage.name - AZURE_BLOB_STORAGE_ENDPOINT = module.storage.primary_endpoints + AZURE_BLOB_STORAGE_ENDPOINT = module.storage.primary_blob_endpoint AZURE_BLOB_STORAGE_CONTAINER = var.contentContainerName AZURE_BLOB_STORAGE_UPLOAD_CONTAINER = var.uploadContainerName AZURE_OPENAI_SERVICE = var.useExistingAOAIService ? var.azureOpenAIServiceName : module.openaiServices.name @@ -403,7 +406,6 @@ module "webapp" { AZURE_AI_ENDPOINT = module.cognitiveServices.cognitiveServiceEndpoint AZURE_AI_LOCATION = var.location APPLICATION_TITLE = var.applicationtitle == "" ? "Information Assistant, built with Azure OpenAI" : var.applicationtitle - AZURE_AI_TRANSLATION_DOMAIN = var.azure_ai_translation_domain USE_SEMANTIC_RERANKER = var.use_semantic_reranker BING_SEARCH_ENDPOINT = var.enableWebChat ? module.bingSearch[0].endpoint : "" ENABLE_WEB_CHAT = var.enableWebChat @@ -413,6 +415,7 @@ module "webapp" { ENABLE_TABULAR_DATA_ASSISTANT = var.enableTabularDataAssistant ENABLE_MULTIMEDIA = var.enableMultimedia MAX_CSV_FILE_SIZE = var.maxCsvFileSize + AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_private_link_domain } aadClientId = module.entraObjects.azure_ad_web_app_client_id @@ -441,10 +444,11 @@ module "functions" { appInsightsConnectionString = module.logging.applicationInsightsConnectionString appInsightsInstrumentationKey = module.logging.applicationInsightsInstrumentationKey blobStorageAccountName = module.storage.name - blobStorageAccountEndpoint = module.storage.primary_endpoints + blobStorageAccountEndpoint = module.storage.primary_blob_endpoint blobStorageAccountOutputContainerName = var.contentContainerName blobStorageAccountUploadContainerName = var.uploadContainerName - blobStorageAccountLogContainerName = var.functionLogsContainerName + blobStorageAccountLogContainerName = var.functionLogsContainerName + queueStorageAccountEndpoint = module.storage.primary_queue_endpoint formRecognizerEndpoint = module.aiDocIntelligence.formRecognizerAccountEndpoint CosmosDBEndpointURL = module.cosmosdb.CosmosDBEndpointURL CosmosDBLogDatabaseName = module.cosmosdb.CosmosDBLogDatabaseName @@ -477,8 +481,6 @@ module "functions" { azureSearchIndex = var.searchIndexName azureSearchServiceEndpoint = module.searchServices.endpoint endpointSuffix = var.azure_storage_domain - azure_ai_text_analytics_domain = var.azure_ai_text_analytics_domain - azure_ai_translation_domain = var.azure_ai_translation_domain logAnalyticsWorkspaceResourceId = module.logging.logAnalyticsId is_secure_mode = var.is_secure_mode vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null @@ -490,6 +492,7 @@ module "functions" { container_registry_admin_password = module.acr.admin_password container_registry_id = module.acr.acr_id azure_environment = var.azure_environment + azure_ai_credential_domain = var.azure_ai_private_link_domain } module "openaiServices" { diff --git a/infra/outputs.tf b/infra/outputs.tf index 5e598ea77..aa9c84d07 100644 --- a/infra/outputs.tf +++ b/infra/outputs.tf @@ -23,10 +23,6 @@ output "AZURE_STORAGE_ACCOUNT" { value = module.storage.name } -output "AZURE_STORAGE_ACCOUNT_ENDPOINT" { - value = module.storage.primary_endpoints -} - output "AZURE_STORAGE_CONTAINER" { value = var.contentContainerName } @@ -120,7 +116,11 @@ output "AZURE_SUBSCRIPTION_ID" { } output "BLOB_STORAGE_ACCOUNT_ENDPOINT" { - value = module.storage.primary_endpoints + value = module.storage.primary_blob_endpoint +} + +output "AZURE_QUEUE_STORAGE_ENDPOINT" { + value = module.sotrage.primary_queue_endpoint } output "EMBEDDING_VECTOR_SIZE" { @@ -151,7 +151,7 @@ output "ENRICHMENT_APPSERVICE_URL" { value = module.enrichmentApp.uri } -output "DEPLOYMENT_KEYVAULT_NAME" { +output "AZURE_KEYVAULT_NAME" { value = module.kvModule.keyVaultName } @@ -179,14 +179,6 @@ output "ENABLE_BING_SAFE_SEARCH" { value = var.enableBingSafeSearch } -output "AZURE_AI_TRANSLATION_DOMAIN" { - value = var.azure_ai_translation_domain -} - -output "AZURE_AI_TEXT_ANALYTICS_DOMAIN" { - value = var.azure_ai_text_analytics_domain -} - output "AZURE_ARM_MANAGEMENT_API" { value = var.azure_arm_management_api } @@ -210,4 +202,8 @@ output "CONTAINER_REGISTRY_PASSWORD" { output "DNS_PRIVATE_RESOLVER_IP" { value = var.is_secure_mode ? module.network[0].dns_private_resolver_ip : "" +} + +output "AZURE_AI_CREDENTIAL_DOMAIN" { + value = var.azure_ai_private_link_domain } \ No newline at end of file diff --git a/infra/variables.tf b/infra/variables.tf index d845d4692..4604579f1 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -107,14 +107,6 @@ variable "azure_arm_management_api" { type = string } -variable "azure_ai_translation_domain" { - type = string -} - -variable "azure_ai_text_analytics_domain" { - type = string -} - variable "azure_ai_form_recognizer_domain" { type = string } diff --git a/pipelines/devcontainer-ci.env b/pipelines/devcontainer-ci.env index b6698038b..3edfdf142 100644 --- a/pipelines/devcontainer-ci.env +++ b/pipelines/devcontainer-ci.env @@ -13,7 +13,6 @@ AD_MGMTAPP_CLIENT_ID AD_MGMTAPP_CLIENT_SECRET AD_MGMT_SERVICE_PRINCIPAL_ID AZURE_OPENAI_SERVICE_NAME -AZURE_OPENAI_SERVICE_KEY AZURE_OPENAI_RESOURCE_GROUP AZURE_OPENAI_CHATGPT_DEPLOYMENT AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME diff --git a/pipelines/templates/make-command.yml b/pipelines/templates/make-command.yml index f02410f3d..143e4f44d 100644 --- a/pipelines/templates/make-command.yml +++ b/pipelines/templates/make-command.yml @@ -34,7 +34,6 @@ steps: ARM_SUBSCRIPTION_ID: $(SUBSCRIPTION_ID) AD_WEBAPP_CLIENT_ID: $(WEBAPP_CLIENT_ID) AZURE_OPENAI_SERVICE_NAME: $(AZURE_OPENAI_SERVICE_NAME) - AZURE_OPENAI_SERVICE_KEY: $(AZURE_OPENAI_SERVICE_KEY) AZURE_OPENAI_RESOURCE_GROUP: $(AZURE_OPENAI_RESOURCE_GROUP) AZURE_OPENAI_CHATGPT_DEPLOYMENT: $(AZURE_OPENAI_CHATGPT_DEPLOYMENT) AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: $(AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME) diff --git a/scripts/deploy-search-indexes.sh b/scripts/deploy-search-indexes.sh index 1bc912e42..01d6531b6 100755 --- a/scripts/deploy-search-indexes.sh +++ b/scripts/deploy-search-indexes.sh @@ -24,14 +24,14 @@ fi search_url="${AZURE_SEARCH_SERVICE_ENDPOINT}" -# Get the Search Admin Key -search_key=$(az search admin-key show --resource-group $RESOURCE_GROUP_NAME --service-name $AZURE_SEARCH_SERVICE --query primaryKey -o tsv) -export AZURE_SEARCH_ADMIN_KEY=$search_key +# Obtain an access token for Azure Search +access_token=$(az account get-access-token --resource https://search.azure.com --query accessToken -o tsv) + # Fetch existing index definition if it exists index_vector_json=$(cat ${DIR}/../azure_search/create_vector_index.json | envsubst | tr -d "\n" | tr -d "\r") index_vector_name=$(echo $index_vector_json | jq -r .name ) -existing_index=$(curl -s --header "api-key: $AZURE_SEARCH_ADMIN_KEY" $search_url/indexes/$index_vector_name?api-version=2024-05-01-preview) +existing_index=$(curl -s --header "Authorization: Bearer $access_token" $search_url/indexes/$index_vector_name?api-version=2024-05-01-preview) if [[ "$existing_index" != *"No index with the name"* ]]; then existing_dimensions=$(echo "$existing_index" | jq -r '.fields | map(select(.name == "contentVector")) | .[0].dimensions') @@ -46,7 +46,7 @@ if [[ "$existing_index" != *"No index with the name"* ]]; then exit 0 else echo "Deleting the existing index $existing_index_name..." - curl -X DELETE --header "api-key: $AZURE_SEARCH_ADMIN_KEY" $search_url/indexes/$existing_index_name?api-version=2024-05-01-preview + curl -X DELETE --header "Authorization: Bearer $access_token" $search_url/indexes/$existing_index_name?api-version=2024-05-01-preview echo "Index $index_vector_name deleted." fi fi @@ -54,7 +54,7 @@ fi # Create vector index echo "Creating index $index_vector_name ..." -curl -s -X PUT --header "Content-Type: application/json" --header "api-key: $AZURE_SEARCH_ADMIN_KEY" --data "$index_vector_json" $search_url/indexes/$index_vector_name?api-version=2024-05-01-preview +curl -s -X PUT --header "Content-Type: application/json" --header "Authorization: Bearer $access_token" --data "$index_vector_json" $search_url/indexes/$index_vector_name?api-version=2024-05-01-preview echo -e "\n" echo "Successfully deployed $index_vector_name." diff --git a/scripts/environments/AzureEnvironments/AzureCloud.env b/scripts/environments/AzureEnvironments/AzureCloud.env index 59eba15da..04af10bd0 100644 --- a/scripts/environments/AzureEnvironments/AzureCloud.env +++ b/scripts/environments/AzureEnvironments/AzureCloud.env @@ -1,7 +1,5 @@ export TF_VAR_arm_template_schema_mgmt_api="https://schema.management.azure.com" export TF_VAR_azure_portal_domain="https://portal.azure.com" -export TF_VAR_azure_ai_translation_domain="api.cognitive.microsofttranslator.com" -export TF_VAR_azure_ai_text_analytics_domain="api.cognitive.microsoft.com" export TF_VAR_azure_ai_form_recognizer_domain="api.cognitive.microsoft.com" export TF_VAR_azure_search_domain="search.windows.net" export TF_VAR_use_semantic_reranker=true diff --git a/scripts/environments/AzureEnvironments/AzureUSGovernment.env b/scripts/environments/AzureEnvironments/AzureUSGovernment.env index 7bb3a94a8..4de80a9d2 100644 --- a/scripts/environments/AzureEnvironments/AzureUSGovernment.env +++ b/scripts/environments/AzureEnvironments/AzureUSGovernment.env @@ -1,7 +1,5 @@ export TF_VAR_arm_template_schema_mgmt_api="https://schema.management.usgovcloudapi.net" export TF_VAR_azure_portal_domain="https://portal.azure.us" -export TF_VAR_azure_ai_translation_domain="api.cognitive.microsofttranslator.us" -export TF_VAR_azure_ai_text_analytics_domain="api.cognitive.microsoft.us" export TF_VAR_azure_ai_form_recognizer_domain="api.cognitive.microsoft.us" export TF_VAR_azure_search_domain="search.azure.us" export TF_VAR_use_semantic_reranker=false diff --git a/scripts/environments/local.env.example b/scripts/environments/local.env.example index f1c04277f..35df53c60 100644 --- a/scripts/environments/local.env.example +++ b/scripts/environments/local.env.example @@ -70,7 +70,6 @@ export SECRET_EXPIRATION_DAYS=120 # Required export USE_EXISTING_AOAI=false # Required export AZURE_OPENAI_RESOURCE_GROUP="" export AZURE_OPENAI_SERVICE_NAME="" -export AZURE_OPENAI_SERVICE_KEY="" export AZURE_OPENAI_CHATGPT_DEPLOYMENT="" # Choose your preferred text embedding model from below options of closed source and open source models.: diff --git a/scripts/extract-content.py b/scripts/extract-content.py index c299daba7..d207073b0 100755 --- a/scripts/extract-content.py +++ b/scripts/extract-content.py @@ -13,7 +13,6 @@ from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient from azure.search.documents import SearchClient -from azure.core.credentials import AzureKeyCredential from azure.storage.blob import BlobServiceClient from azure.storage.blob import generate_container_sas, ContainerSasPermissions from datetime import datetime, timedelta @@ -173,8 +172,8 @@ def get_storage_account_endpoint(storage_account_name): index_name = 'vector-index' -old_search_client = SearchClient(endpoint=old_search_endpoint, index_name=index_name, credential=AzureKeyCredential(old_search_key)) -new_search_client = SearchClient(endpoint=new_search_endpoint, index_name=index_name, credential=AzureKeyCredential(new_search_key)) +old_search_client = SearchClient(endpoint=old_search_endpoint, index_name=index_name, credential=credential) +new_search_client = SearchClient(endpoint=new_search_endpoint, index_name=index_name, credential=credential) error_guidance = 'If you re-run the process, you can skip sections that completed successfully by setting the corresponding skip flag to True. Read more details her' diff --git a/scripts/functional-tests.sh b/scripts/functional-tests.sh index 56f13ae9a..c38b09cc9 100755 --- a/scripts/functional-tests.sh +++ b/scripts/functional-tests.sh @@ -32,7 +32,6 @@ python run_tests.py \ --storage_account_url "${BLOB_STORAGE_ACCOUNT_ENDPOINT}" \ --search_service_endpoint "${AZURE_SEARCH_SERVICE_ENDPOINT}" \ --search_index "${AZURE_SEARCH_INDEX}" \ - --search_key "${AZURE_SEARCH_SERVICE_KEY}" \ --wait_time_seconds 60 \ --file_extensions "docx" "pdf" "html" "jpg" "png" "csv" "md" "pptx" "txt" "xlsx" "xml" diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index c3b03d63f..6b4bc6f1d 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -17,7 +17,7 @@ fi secrets="{" # Name of your Key Vault -keyVaultName=$(cat inf_output.json | jq -r .DEPLOYMENT_KEYVAULT_NAME.value) +keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-FORM-RECOGNIZER-KEY" "AZURE-AI-KEY") @@ -115,20 +115,16 @@ jq -r --arg secrets "$secrets" ' "env_var": "AZURE_SEARCH_SERVICE_ENDPOINT" }, { - "path": "DEPLOYMENT_KEYVAULT_NAME", - "env_var": "DEPLOYMENT_KEYVAULT_NAME" - }, - { - "path": "AZURE_AI_TRANSLATION_DOMAIN", - "env_var": "AZURE_AI_TRANSLATION_DOMAIN" - }, - { - "path": "AZURE_AI_TEXT_ANALYTICS_DOMAIN", - "env_var": "AZURE_AI_TEXT_ANALYTICS_DOMAIN" + "path": "AZURE_KEYVAULT_NAME", + "env_var": "AZURE_KEYVAULT_NAME" }, { "path": "AZURE_AI_LOCATION", "env_var": "AZURE_AI_LOCATION" + }, + { + "path": "AZURE_AI_CREDENTIAL_DOMAIN", + "env_var": "AZURE_AI_CREDENTIAL_DOMAIN" } ] as $env_vars_to_extract @@ -169,6 +165,7 @@ jq -r --arg secrets "$secrets" ' "EMBEDDINGS_QUEUE": "embeddings-queue", "TEXT_ENRICHMENT_QUEUE": "text-enrichment-queue", "IMAGE_ENRICHMENT_QUEUE": "image-enrichment-queue", + "LOCAL_DEBUG": "true", } + ($secrets | fromjson) )} diff --git a/scripts/json-to-env.sh b/scripts/json-to-env.sh index 814d630a5..176b23fec 100755 --- a/scripts/json-to-env.sh +++ b/scripts/json-to-env.sh @@ -103,8 +103,8 @@ jq -r ' "env_var": "ENRICHMENT_APPSERVICE_NAME" }, { - "path": "DEPLOYMENT_KEYVAULT_NAME", - "env_var": "DEPLOYMENT_KEYVAULT_NAME" + "path": "AZURE_KEYVAULT_NAME", + "env_var": "AZURE_KEYVAULT_NAME" }, { "path": "MAX_CSV_FILE_SIZE", @@ -160,7 +160,7 @@ if [ -n "${IN_AUTOMATION}" ]; then fi # Name of your Key Vault -keyVaultName=$(cat inf_output.json | jq -r .DEPLOYMENT_KEYVAULT_NAME.value) +keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY") diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index 99cc9a6ad..15cbd118b 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -88,6 +88,10 @@ jq -r ' "path": "BLOB_STORAGE_ACCOUNT_ENDPOINT", "env_var": "AZURE_BLOB_STORAGE_ENDPOINT" }, + { + "path": "AZURE_QUEUE_STORAGE_ENDPOINT", + "env_var": "AZURE_QUEUE_STORAGE_ENDPOINT" + }, { "path": "TARGET_EMBEDDINGS_MODEL", "env_var": "TARGET_EMBEDDINGS_MODEL" @@ -113,8 +117,8 @@ jq -r ' "env_var": "ENRICHMENT_APPSERVICE_URL" }, { - "path": "DEPLOYMENT_KEYVAULT_NAME", - "env_var": "DEPLOYMENT_KEYVAULT_NAME" + "path": "AZURE_KEYVAULT_NAME", + "env_var": "AZURE_KEYVAULT_NAME" }, { "path": "AZURE_OPENAI_CHATGPT_MODEL_NAME", @@ -140,14 +144,6 @@ jq -r ' "path": "ENABLEE_BING_SAFE_SEARCH", "env_var": "ENABLE_BING_SAFE_SEARCH" }, - { - "path": "AZURE_AI_TRANSLATION_DOMAIN", - "env_var": "AZURE_AI_TRANSLATION_DOMAIN" - }, - { - "path": "AZURE_AI_TEXT_ANALYTICS_DOMAIN", - "env_var": "AZURE_AI_TEXT_ANALYTICS_DOMAIN" - }, { "path": "AZURE_ARM_MANAGEMENT_API", "env_var": "AZURE_ARM_MANAGEMENT_API" @@ -159,6 +155,10 @@ jq -r ' { "path": "AZURE_AI_LOCATION", "env_var": "AZURE_AI_LOCATION" + }, + { + "path": "AZURE_AI_CREDENTIAL_DOMAIN", + "env_var": "AZURE_AI_CREDENTIAL_DOMAIN" } ] as $env_vars_to_extract @@ -195,6 +195,7 @@ jq -r ' echo "ENABLE_MATH_ASSISTANT=$ENABLE_MATH_ASSISTANT" echo "ENABLE_TABULAR_DATA_ASSISTANT=$ENABLE_TABULAR_DATA_ASSISTANT" echo "ENABLE_MULTIMEDIA=$ENABLE_MULTIMEDIA" + echo "LOCAL_DEBUG=true" if [ -n "${IN_AUTOMATION}" ]; then if [ -n "${AZURE_ENVIRONMENT}" ] && [[ "$AZURE_ENVIRONMENT" == "AzureUSGovernment" ]]; then @@ -206,7 +207,7 @@ if [ -n "${IN_AUTOMATION}" ]; then fi # Name of your Key Vault -keyVaultName=$(cat inf_output.json | jq -r .DEPLOYMENT_KEYVAULT_NAME.value) +keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets if [ -n "${SECURE_MODE}" ]; then diff --git a/scripts/prepare-tf-variables.sh b/scripts/prepare-tf-variables.sh index 05e25b728..ed2509fbe 100755 --- a/scripts/prepare-tf-variables.sh +++ b/scripts/prepare-tf-variables.sh @@ -13,7 +13,6 @@ export TF_VAR_subscriptionId=$SUBSCRIPTION_ID export TF_VAR_useExistingAOAIService=$USE_EXISTING_AOAI export TF_VAR_azureOpenAIResourceGroup=$AZURE_OPENAI_RESOURCE_GROUP export TF_VAR_azureOpenAIServiceName=$AZURE_OPENAI_SERVICE_NAME -export TF_VAR_azureOpenAIServiceKey=$AZURE_OPENAI_SERVICE_KEY export TF_VAR_chatGptDeploymentName=$AZURE_OPENAI_CHATGPT_DEPLOYMENT export TF_VAR_chatGptModelName=$AZURE_OPENAI_CHATGPT_MODEL_NAME export TF_VAR_chatGptModelVersion=$AZURE_OPENAI_CHATGPT_MODEL_VERSION diff --git a/tests/debug_tests.py b/tests/debug_tests.py index c44d0d4b8..78b3b6ad7 100644 --- a/tests/debug_tests.py +++ b/tests/debug_tests.py @@ -7,14 +7,12 @@ STORAGE_ACCOUNT_URL = os.environ.get("AZURE_BLOB_STORAGE_ENDPOINT") AZURE_SEARCH_SERVICE_ENDPOINT = os.environ.get("AZURE_SEARCH_SERVICE_ENDPOINT") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") -AZURE_SEARCH_SERVICE_KEY = os.environ.get("AZURE_SEARCH_SERVICE_KEY") ENRICHMENT_APPSERVICE_NAME = os.environ.get("ENRICHMENT_APPSERVICE_NAME") AZURE_WEBSITE_DOMAIN = os.environ.get("TF_VAR_azure_websites_domain") or "false" subprocess.call(['python', 'run_tests.py', '--storage_account_url', STORAGE_ACCOUNT_URL, \ '--search_service_endpoint', AZURE_SEARCH_SERVICE_ENDPOINT, \ '--search_index', AZURE_SEARCH_INDEX, \ - '--search_key', AZURE_SEARCH_SERVICE_KEY, \ '--wait_time_seconds', '60', \ '--file_extensions', 'docx', 'pdf', 'html', 'jpg', 'png', 'csv', 'md', 'pptx', 'txt', 'xlsx', 'xml']) diff --git a/tests/run_tests.py b/tests/run_tests.py index 8d307fce0..0c416a143 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -8,13 +8,11 @@ import base64 import os import time -from datetime import datetime, timedelta, timezone from rich.console import Console import rich.traceback from azure.storage.blob import BlobServiceClient from azure.identity import DefaultAzureCredential from azure.search.documents import SearchClient -from azure.core.credentials import AzureKeyCredential azure_credential = DefaultAzureCredential() @@ -63,10 +61,6 @@ def parse_arguments(): "--search_index", required=True, help="Azure Search Index") - parser.add_argument( - "--search_key", - required=True, - help="Azure Search Key") parser.add_argument( "--wait_time_seconds", required=False, @@ -144,14 +138,13 @@ def main(blob_service_client, wait_time_seconds, test_file_names): raise ex # Check Search Index for specific content uploaded by test -def check_index(search_service_endpoint, search_index, search_key ): +def check_index(search_service_endpoint, search_index): """Function to check the index for specific content uploaded by the test""" try: - azure_search_key_credential = AzureKeyCredential(search_key) search_client = SearchClient( endpoint=search_service_endpoint, index_name=search_index, - credential=azure_search_key_credential, + credential=azure_credential, ) console.print("Begining index search") for extension, query in search_queries.items(): @@ -174,17 +167,16 @@ def check_index(search_service_endpoint, search_index, search_key ): console.log(f'[red]❌ {ex}[/red]') raise ex -def cleanup_after_test(blob_service_client, search_service_endpoint, search_index, search_key, test_file_names): +def cleanup_after_test(blob_service_client, search_service_endpoint, search_index, test_file_names): """Function to cleanup after tests""" console.print("Cleaning up after tests...") upload_container_client = blob_service_client.get_container_client(UPLOAD_CONTAINER_NAME) output_container_client = blob_service_client.get_container_client(OUTPUT_CONTAINER_NAME) - azure_search_key_credential = AzureKeyCredential(search_key) search_client = SearchClient( endpoint=search_service_endpoint, index_name=search_index, - credential=azure_search_key_credential, + credential=azure_credential, ) # Cleanup upload container @@ -235,10 +227,9 @@ def get_files_by_extension(folder_path, extensions): test_file_names = get_files_by_extension(FILE_PATH, args.file_extensions) main(storage_blob_service_client, args.wait_time_seconds, test_file_names) - check_index(args.search_service_endpoint, args.search_index, args.search_key) + check_index(args.search_service_endpoint, args.search_index) finally: cleanup_after_test(storage_blob_service_client, args.search_service_endpoint, args.search_index, - args.search_key, test_file_names) From 4045aad02d0286227a025e809def619b6ff3c304 Mon Sep 17 00:00:00 2001 From: bjakems <165402330+bjakems@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:09:53 +0100 Subject: [PATCH 04/33] Add CognitiveServicesOpenAIUser to enrichment. Add SearchIndexDataReader to function and enrichment. --- infra/main.tf | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/infra/main.tf b/infra/main.tf index 59e32d8ff..95cd14eeb 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -701,6 +701,17 @@ module "openAiRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } +module "enrichmentOpenAiRoleBackend" { + source = "./core/security/role" + + scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + principalId = module.enrichmentApp.identityPrincipalId + roleDefinitionId = local.azure_roles.CognitiveServicesOpenAIUser + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + module "webAppCognitiveServicesUserBackend" { source = "./core/security/role" @@ -767,6 +778,28 @@ module "searchRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } +module "functionSearchRoleBackend" { + source = "./core/security/role" + + scope = azurerm_resource_group.rg.id + principalId = module.functions.identityPrincipalId + roleDefinitionId = local.azure_roles.SearchIndexDataReader + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + +module "encrichmentSearchRoleBackend" { + source = "./core/security/role" + + scope = azurerm_resource_group.rg.id + principalId = module.enrichmentApp.identityPrincipalId + roleDefinitionId = local.azure_roles.SearchIndexDataReader + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + module "storageRoleFunc" { source = "./core/security/role" From 4d94b9a222e7f47287d123ec71e0f6882b2a27d5 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:58:18 +0100 Subject: [PATCH 05/33] Removing unused keys from being added as secrets in KV --- docs/function_debug.md | 1 - functions/FileFormRecPollingPDF/__init__.py | 5 +++-- functions/FileFormRecSubmissionPDF/__init__.py | 4 ++-- infra/core/ai/docintelligence/docintelligence.tf | 12 ------------ infra/core/ai/openaiservices/variables.tf | 5 ----- infra/core/host/functions/functions.tf | 2 -- infra/core/security/keyvault/keyvault.tf | 12 ------------ infra/core/storage/storage-account.tf | 12 ------------ infra/main.tf | 1 - infra/outputs.tf | 2 +- infra/variables.tf | 5 ----- scripts/extract-content.py | 2 -- scripts/inf-import-state.sh | 16 ---------------- scripts/json-to-env.function.debug.sh | 2 +- scripts/json-to-env.sh | 2 +- scripts/json-to-env.webapp.debug.sh | 4 ++-- 16 files changed, 10 insertions(+), 77 deletions(-) diff --git a/docs/function_debug.md b/docs/function_debug.md index f78930431..253cbed68 100644 --- a/docs/function_debug.md +++ b/docs/function_debug.md @@ -23,7 +23,6 @@ Next you will need to create local configuration values that are used by the fun "COSMOSDB_KEY": "", "COSMOSDB_URL": "", "AZURE_FORM_RECOGNIZER_ENDPOINT": "", - "AZURE_FORM_RECOGNIZER_KEY": "", "BLOB_STORAGE_ACCOUNT": "", "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME": "content", "CHUNK_TARGET_SIZE": "750", diff --git a/functions/FileFormRecPollingPDF/__init__.py b/functions/FileFormRecPollingPDF/__init__.py index 02dff2c3e..ac0915ed7 100644 --- a/functions/FileFormRecPollingPDF/__init__.py +++ b/functions/FileFormRecPollingPDF/__init__.py @@ -38,7 +38,6 @@ def string_to_bool(s): pdf_submit_queue = os.environ["PDF_SUBMIT_QUEUE"] text_enrichment_queue = os.environ["TEXT_ENRICHMENT_QUEUE"] endpoint = os.environ["AZURE_FORM_RECOGNIZER_ENDPOINT"] -FR_key = os.environ["AZURE_FORM_RECOGNIZER_KEY"] api_version = os.environ["FR_API_VERSION"] max_submit_requeue_count = int(os.environ["MAX_SUBMIT_REQUEUE_COUNT"]) max_polling_requeue_count = int(os.environ["MAX_POLLING_REQUEUE_COUNT"]) @@ -67,6 +66,7 @@ def string_to_bool(s): azure_credential = DefaultAzureCredential(authority=AUTHORITY) else: azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +token_provider = get_bearer_token_provider(azure_credential, f'https://{os.environ["AZURE_AI_CREDENTIAL_DOMAIN"]}/.default') utilities = Utilities(azure_blob_storage_account, azure_blob_storage_endpoint, azure_blob_drop_storage_container, azure_blob_content_storage_container, azure_credential) @@ -87,7 +87,8 @@ def main(msg: func.QueueMessage) -> None: # Construct and submmit the polling message to FR headers = { - 'Ocp-Apim-Subscription-Key': FR_key + "Content-Type": "application/json", + 'Authorization': f'Bearer {token_provider()}' } params = { diff --git a/functions/FileFormRecSubmissionPDF/__init__.py b/functions/FileFormRecSubmissionPDF/__init__.py index 795911564..957f34d40 100644 --- a/functions/FileFormRecSubmissionPDF/__init__.py +++ b/functions/FileFormRecSubmissionPDF/__init__.py @@ -27,7 +27,6 @@ pdf_polling_queue = os.environ["PDF_POLLING_QUEUE"] pdf_submit_queue = os.environ["PDF_SUBMIT_QUEUE"] endpoint = os.environ["AZURE_FORM_RECOGNIZER_ENDPOINT"] -FR_key = os.environ["AZURE_FORM_RECOGNIZER_KEY"] api_version = os.environ["FR_API_VERSION"] max_submit_requeue_count = int(os.environ["MAX_SUBMIT_REQUEUE_COUNT"]) poll_queue_submit_backoff = int(os.environ["POLL_QUEUE_SUBMIT_BACKOFF"]) @@ -54,6 +53,7 @@ azure_credential = DefaultAzureCredential(authority=AUTHORITY) else: azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +token_provider = get_bearer_token_provider(azure_credential, f'https://{os.environ["AZURE_AI_CREDENTIAL_DOMAIN"]}/.default') utilities = Utilities( azure_blob_storage_account, @@ -101,7 +101,7 @@ def main(msg: func.QueueMessage) -> None: # Construct and submmit the message to FR headers = { "Content-Type": "application/json", - "Ocp-Apim-Subscription-Key": FR_key, + 'Authorization': f'Bearer {token_provider()}' } params = {"api-version": api_version} diff --git a/infra/core/ai/docintelligence/docintelligence.tf b/infra/core/ai/docintelligence/docintelligence.tf index 6e3caea19..a28877e18 100644 --- a/infra/core/ai/docintelligence/docintelligence.tf +++ b/infra/core/ai/docintelligence/docintelligence.tf @@ -10,18 +10,6 @@ resource "azurerm_cognitive_account" "formRecognizerAccount" { tags = var.tags } -module "docIntelligenceKey" { - source = "../../security/keyvaultSecret" - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - resourceGroupName = var.resourceGroupName - key_vault_name = var.key_vault_name - secret_name = "AZURE-FORM-RECOGNIZER-KEY" - secret_value = azurerm_cognitive_account.formRecognizerAccount.primary_access_key - alias = "docintkey" - tags = var.tags - kv_secret_expiration = var.kv_secret_expiration -} - data "azurerm_subnet" "subnet" { count = var.is_secure_mode ? 1 : 0 name = var.subnet_name diff --git a/infra/core/ai/openaiservices/variables.tf b/infra/core/ai/openaiservices/variables.tf index 68666cc3c..47657659a 100644 --- a/infra/core/ai/openaiservices/variables.tf +++ b/infra/core/ai/openaiservices/variables.tf @@ -45,11 +45,6 @@ variable "useExistingAOAIService" { default = false } -variable "openaiServiceKey" { - description = "The OpenAI service key" - type = string -} - variable "is_secure_mode" { type = bool default = false diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 1e656da5a..7f9e0e807 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -89,7 +89,6 @@ resource "azurerm_linux_function_app" "function_app" { service_plan_id = azurerm_service_plan.funcServicePlan.id storage_account_name = var.blobStorageAccountName storage_uses_managed_identity = true - #storage_account_access_key = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-BLOB-STORAGE-KEY)" https_only = true tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true @@ -149,7 +148,6 @@ resource "azurerm_linux_function_app" "function_app" { TARGET_PAGES = var.targetPages FR_API_VERSION = var.formRecognizerApiVersion AZURE_FORM_RECOGNIZER_ENDPOINT = var.formRecognizerEndpoint - AZURE_FORM_RECOGNIZER_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-FORM-RECOGNIZER-KEY)" COSMOSDB_URL = var.CosmosDBEndpointURL COSMOSDB_LOG_DATABASE_NAME = var.CosmosDBLogDatabaseName COSMOSDB_LOG_CONTAINER_NAME = var.CosmosDBLogContainerName diff --git a/infra/core/security/keyvault/keyvault.tf b/infra/core/security/keyvault/keyvault.tf index 964d4ce79..87ace8edc 100644 --- a/infra/core/security/keyvault/keyvault.tf +++ b/infra/core/security/keyvault/keyvault.tf @@ -44,18 +44,6 @@ resource "azurerm_key_vault_access_policy" "infoasst" { ] } -module "spClientKeySecret" { - source = "../keyvaultSecret" - resourceGroupName = var.resourceGroupName - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - key_vault_name = azurerm_key_vault.kv.name - secret_name = "AZURE-CLIENT-SECRET" - secret_value = var.spClientSecret - tags = var.tags - alias = "clientsecret" - kv_secret_expiration = var.kv_secret_expiration -} - data "azurerm_subnet" "subnet" { count = var.is_secure_mode ? 1 : 0 name = var.subnet_name diff --git a/infra/core/storage/storage-account.tf b/infra/core/storage/storage-account.tf index 928651008..223394bd5 100644 --- a/infra/core/storage/storage-account.tf +++ b/infra/core/storage/storage-account.tf @@ -279,18 +279,6 @@ resource "azurerm_private_endpoint" "queuePrivateEndpoint" { } } -module "storage_key" { - source = "../security/keyvaultSecret" - resourceGroupName = var.resourceGroupName - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - key_vault_name = var.key_vault_name - secret_name = "AZURE-BLOB-STORAGE-KEY" - secret_value = azurerm_storage_account.storage.primary_access_key - tags = var.tags - alias = "blobkey" - kv_secret_expiration = var.kv_secret_expiration -} - // Only create the config blob if we are not in secure mode as SharePoint integration is not supported in secure mode resource "azurerm_storage_blob" "config" { depends_on = [ azurerm_resource_group_template_deployment.container ] diff --git a/infra/main.tf b/infra/main.tf index 95cd14eeb..3a1d8cb2f 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -501,7 +501,6 @@ module "openaiServices" { location = var.location tags = local.tags resourceGroupName = azurerm_resource_group.rg.name - openaiServiceKey = var.azureOpenAIServiceKey useExistingAOAIService = var.useExistingAOAIService is_secure_mode = var.is_secure_mode subnet_name = var.is_secure_mode ? module.network[0].snetAzureOpenAI_name : null diff --git a/infra/outputs.tf b/infra/outputs.tf index aa9c84d07..28eeb3e17 100644 --- a/infra/outputs.tf +++ b/infra/outputs.tf @@ -120,7 +120,7 @@ output "BLOB_STORAGE_ACCOUNT_ENDPOINT" { } output "AZURE_QUEUE_STORAGE_ENDPOINT" { - value = module.sotrage.primary_queue_endpoint + value = module.storage.primary_queue_endpoint } output "EMBEDDING_VECTOR_SIZE" { diff --git a/infra/variables.tf b/infra/variables.tf index 4604579f1..4623e2ef8 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -216,11 +216,6 @@ variable "azureOpenAIResourceGroup" { type = string } -variable "azureOpenAIServiceKey" { - type = string - sensitive = true -} - variable "openAIServiceName" { type = string default = "" diff --git a/scripts/extract-content.py b/scripts/extract-content.py index d207073b0..10143314a 100755 --- a/scripts/extract-content.py +++ b/scripts/extract-content.py @@ -159,11 +159,9 @@ def get_storage_account_endpoint(storage_account_name): old_cosmosdb_url = f'https://infoasst-cosmos-{old_random_text}.documents.azure.com:443/' old_cosmosdb_key = old_secret_client.get_secret('COSMOSDB-KEY').value old_search_endpoint = f'https://infoasst-search-{old_random_text}.search.windows.net' -old_search_key = old_secret_client.get_secret('AZURE-SEARCH-SERVICE-KEY').value old_azure_blob_storage_account = f"infoasststore{old_random_text}" old_azure_blob_storage_endpoint = get_storage_account_endpoint(old_azure_blob_storage_account) -new_search_key = new_secret_client.get_secret('AZURE-SEARCH-SERVICE-KEY').value new_search_endpoint = f'https://infoasst-search-{new_random_text}.search.windows.net' new_cosmosdb_url = f'https://infoasst-cosmos-{new_random_text}.documents.azure.com:443/' new_cosmosdb_key = new_secret_client.get_secret('COSMOSDB-KEY').value diff --git a/scripts/inf-import-state.sh b/scripts/inf-import-state.sh index 217bec3a5..80368c877 100755 --- a/scripts/inf-import-state.sh +++ b/scripts/inf-import-state.sh @@ -357,10 +357,6 @@ name="infoasst-fr-$random_text" providers="/providers/Microsoft.CognitiveServices/accounts/$name" module_path="module.formrecognizer.azurerm_cognitive_account.formRecognizerAccount" import_resource_if_needed "$module_path" "$resourceId$providers" -secret_id=$(get_secret "AZURE-FORM-RECOGNIZER-KEY") -module_path="module.formrecognizer.azurerm_key_vault_secret.docIntelligenceKey" -import_resource_if_needed "$module_path" "$secret_id" - # Cognitive Services echo @@ -381,9 +377,6 @@ keyVaultId="infoasst-kv-$random_text" providers="/providers/Microsoft.KeyVault/vaults/$keyVaultId" module_path="module.kvModule.azurerm_key_vault.kv" import_resource_if_needed "$module_path" "$resourceId$providers" -secret_id=$(get_secret "AZURE-CLIENT-SECRET") -module_path="module.kvModule.azurerm_key_vault_secret.spClientKeySecret" -import_resource_if_needed "$module_path" "$secret_id" current_user_id=$(az ad signed-in-user show --query id -o tsv) providers="/providers/Microsoft.KeyVault/vaults/$keyVaultId/objectId/$current_user_id" module_path="module.kvModule.azurerm_key_vault_access_policy.infoasst" @@ -492,11 +485,6 @@ import_resource_if_needed "$module_path[5]" "$url" url="https://$name.queue.core.windows.net/embeddings-queue" import_resource_if_needed "$module_path[6]" "$url" -secret_id=$(get_secret "AZURE-BLOB-STORAGE-KEY") -module_path="module.storage.azurerm_key_vault_secret.storage_key" -import_resource_if_needed "$module_path" "$secret_id" - - # Cosmos DB echo figlet "Cosmos DB" @@ -522,10 +510,6 @@ name="infoasst-search-$random_text" providers="/providers/Microsoft.Search/searchServices/$name" module_path="module.searchServices.azurerm_search_service.search" import_resource_if_needed "$module_path" "$resourceId$providers" -secret_id=$(get_secret "AZURE-SEARCH-SERVICE-KEY") -module_path="module.searchServices.azurerm_key_vault_secret.search_service_key" -import_resource_if_needed "$module_path" "$secret_id" - # Output log on imported services echo diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index 6b4bc6f1d..a2f9a18a6 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -20,7 +20,7 @@ secrets="{" keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-FORM-RECOGNIZER-KEY" "AZURE-AI-KEY") +secretNames=("COSMOSDB-KEY" "AZURE-AI-KEY") # Retrieve and export each secret for secretName in "${secretNames[@]}"; do diff --git a/scripts/json-to-env.sh b/scripts/json-to-env.sh index 176b23fec..8e3a6186a 100755 --- a/scripts/json-to-env.sh +++ b/scripts/json-to-env.sh @@ -162,7 +162,7 @@ fi # Name of your Key Vault keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY") +secretNames=("COSMOSDB-KEY") # Retrieve and export each secret for secretName in "${secretNames[@]}"; do diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index 15cbd118b..32d150f84 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -211,9 +211,9 @@ keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets if [ -n "${SECURE_MODE}" ]; then - secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "AZURE-OPENAI-SERVICE-KEY" "AZURE-CLIENT-SECRET" "AZURE-AI-KEY") + secretNames=("COSMOSDB-KEY" "AZURE-AI-KEY") else - secretNames=("AZURE-SEARCH-SERVICE-KEY" "AZURE-BLOB-STORAGE-KEY" "COSMOSDB-KEY" "BINGSEARCH-KEY" "AZURE-OPENAI-SERVICE-KEY" "AZURE-CLIENT-SECRET" "AZURE-AI-KEY") + secretNames=("COSMOSDB-KEY" "BINGSEARCH-KEY" "AZURE-AI-KEY") fi From cd5f4d3ec7af8a4a8319bcfe2dbad8033477355e Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:04:05 +0100 Subject: [PATCH 06/33] fix parameter error in function --- functions/ImageEnrichment/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 39fdfdc42..3ebfe4038 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -121,7 +121,7 @@ azure_blob_storage_endpoint=azure_blob_storage_endpoint, azure_blob_drop_storage_container=azure_blob_drop_storage_container, azure_blob_content_storage_container=azure_blob_content_storage_container, - azure_credential + azure_credential=azure_credential ) From 4490e54f5e3c137ac44a5fc423c72866be5abf3c Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:06:14 +0100 Subject: [PATCH 07/33] fix param error in exception handling logic --- functions/shared_code/status_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/shared_code/status_log.py b/functions/shared_code/status_log.py index 35bac4053..bb2d37423 100644 --- a/functions/shared_code/status_log.py +++ b/functions/shared_code/status_log.py @@ -305,7 +305,7 @@ def get_stack_trace(self): trc = 'Traceback (most recent call last):\n' stackstr = trc + ''.join(traceback.format_list(stack)) if exc is not None: - stackstr += ' ' + traceback.format_exc().lstrip(trc) + stackstr += ' ' + traceback.format_exc().lstrip() return stackstr def get_all_tags(self): From 707260d0f0387b0747ed85ff647c68706bbe7202 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:33:55 +0100 Subject: [PATCH 08/33] cleanup tabular and math assistant approaches --- app/backend/app.py | 14 ++-- app/backend/approaches/mathassistant.py | 12 +-- .../approaches/tabulardataassistant.py | 73 ++++++------------- 3 files changed, 32 insertions(+), 67 deletions(-) diff --git a/app/backend/app.py b/app/backend/app.py index f4c61ef9f..23031552d 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -1,18 +1,16 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. + from io import StringIO from typing import Optional import asyncio -#from sse_starlette.sse import EventSourceResponse -#from starlette.responses import StreamingResponse -from starlette.responses import Response import logging import os import json import urllib.parse import pandas as pd import pydantic -from datetime import datetime, time, timedelta +from datetime import datetime from fastapi.staticfiles import StaticFiles from fastapi import FastAPI, File, HTTPException, Request, UploadFile from fastapi.responses import RedirectResponse, StreamingResponse @@ -39,7 +37,7 @@ process_agent_scratch_pad as td_agent_scratch_pad, get_images_in_temp ) -from shared_code.status_log import State, StatusClassification, StatusLog, StatusQueryLevel +from shared_code.status_log import State, StatusClassification, StatusLog from azure.cosmos import CosmosClient @@ -701,7 +699,7 @@ async def posttd(csv: UploadFile = File(...)): # Process the DataFrame... save_df(df) except Exception as ex: - raise HTTPException(status_code=500, detail=str(ex)) from ex + raise HTTPException(status_code=500, detail=str(ex)) from ex #return {"filename": csv.filename} @@ -733,7 +731,7 @@ async def process_td_agent_response(retries=3, delay=1000, question: Optional[st async def getTdAnalysis(retries=3, delay=1, question: Optional[str] = None): global dffinal if question is None: - raise HTTPException(status_code=400, detail="Question is required") + raise HTTPException(status_code=400, detail="Question is required") for i in range(retries): try: @@ -825,7 +823,7 @@ async def stream_agent_response(question: str): results = process_agent_response(question) except Exception as e: print(f"Error processing agent response: {e}") - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e return results diff --git a/app/backend/approaches/mathassistant.py b/app/backend/approaches/mathassistant.py index 3816cdef0..f03e8e444 100644 --- a/app/backend/approaches/mathassistant.py +++ b/app/backend/approaches/mathassistant.py @@ -3,16 +3,15 @@ #Turn warnings off #from st_pages import Page, show_pages, add_page_title -import warnings -warnings.filterwarnings('ignore') import os -# import openai +import warnings from dotenv import load_dotenv from langchain_openai import AzureChatOpenAI from langchain.agents import initialize_agent, load_tools, AgentType from langchain.prompts import ChatPromptTemplate from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider +warnings.filterwarnings('ignore') load_dotenv() OPENAI_API_BASE = os.environ.get("AZURE_OPENAI_ENDPOINT") @@ -180,12 +179,7 @@ def process_agent_response( question): #Function to process clues -def generate_response(question): - model = AzureChatOpenAI( - api_key= OPENAI_API_KEY, - azure_endpoint=OPENAI_API_BASE, - openai_api_version=OPENAI_API_VERSION , - deployment_name=OPENAI_DEPLOYMENT_NAME) +def generate_response(question): prompt_template = ChatPromptTemplate.from_template(template=prompt) messages = prompt_template.format_messages( question=question diff --git a/app/backend/approaches/tabulardataassistant.py b/app/backend/approaches/tabulardataassistant.py index 4ef02af64..b88d9b55a 100644 --- a/app/backend/approaches/tabulardataassistant.py +++ b/app/backend/approaches/tabulardataassistant.py @@ -4,53 +4,38 @@ import base64 import os import glob -import re import warnings -from PIL import Image import io -import pandas as pd +import tempfile +from dotenv import load_dotenv +from PIL import Image from langchain_experimental.agents.agent_toolkits import create_pandas_dataframe_agent from langchain.agents.agent_types import AgentType from langchain_openai import AzureChatOpenAI -from langchain.agents import load_tools -import matplotlib.pyplot as plt -import tempfile -warnings.filterwarnings('ignore') -from dotenv import load_dotenv - - - -#-------------------------------------------------------------------------- -#variables needed for testing -OPENAI_API_TYPE = "azure" -OPENAI_API_VERSION = "2024-02-01" -OPENAI_API_BASE = " " -OPENAI_API_KEY = " " -OPENAI_DEPLOYMENT_NAME = " " -MODEL_NAME = " " -AZURE_OPENAI_ENDPOINT = ' ' -AZURE_OPENAI_SERVICE_KEY = ' ' - -os.environ["OPENAI_API_TYPE"] = OPENAI_API_TYPE -os.environ["OPENAI_API_VERSION"] = OPENAI_API_VERSION - +from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider +warnings.filterwarnings('ignore') load_dotenv() -#Environment variables when integrated into the app -#_________________________________________________________________________ - - - -azure_openai_chatgpt_deployment = os.getenv("AZURE_OPENAI_CHATGPT_DEPLOYMENT") -deployment_name = azure_openai_chatgpt_deployment -OPENAI_DEPLOYMENT_NAME = deployment_name OPENAI_API_BASE = os.environ.get("AZURE_OPENAI_ENDPOINT") -OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_SERVICE_KEY") +OPENAI_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_CHATGPT_DEPLOYMENT") +if os.environ.get("AZURE_OPENAI_AUTHORITY_HOST") == "AzureUSGovernment": + AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT +else: + AUTHORITY = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD -# Page title +if os.environ.get("LOCAL_DEBUG") == "true": + azure_credential = DefaultAzureCredential(authority=AUTHORITY) +else: + azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +token_provider = get_bearer_token_provider(azure_credential, f'https://{os.environ.get("AZURE_AI_CREDENTIAL_DOMAIN")}/.default') +model = AzureChatOpenAI( + azure_ad_token_provider=token_provider, + azure_endpoint=OPENAI_API_BASE, + openai_api_version="2024-02-01" , + deployment_name=OPENAI_DEPLOYMENT_NAME) dffinal = None pdagent = None @@ -97,11 +82,6 @@ def save_df(dff): # function to stream agent response def process_agent_scratch_pad(question, df): - chat = AzureChatOpenAI( - api_key= OPENAI_API_KEY, - azure_endpoint=OPENAI_API_BASE, - openai_api_version=OPENAI_API_VERSION , - deployment_name=OPENAI_DEPLOYMENT_NAME) question = save_chart(question) # This agent relies on access to a python repl tool which can execute arbitrary code. @@ -110,7 +90,7 @@ def process_agent_scratch_pad(question, df): # which can lead to data breaches, data loss, or other security incidents. You must opt in # to use this functionality by setting allow_dangerous_code=True. # https://api.python.langchain.com/en/latest/agents/langchain_experimental.agents.agent_toolkits.pandas.base.create_pandas_dataframe_agent.html - pdagent = create_pandas_dataframe_agent(chat, df, verbose=True,agent_type=AgentType.OPENAI_FUNCTIONS,allow_dangerous_code=True , agent_executor_kwargs={"handle_parsing_errors": True}) + pdagent = create_pandas_dataframe_agent(model, df, verbose=True,agent_type=AgentType.OPENAI_FUNCTIONS,allow_dangerous_code=True , agent_executor_kwargs={"handle_parsing_errors": True}) for chunk in pdagent.stream({"input": question}): if "actions" in chunk: for action in chunk["actions"]: @@ -130,15 +110,8 @@ def process_agent_scratch_pad(question, df): #Function to stream final output def process_agent_response(question): question = save_chart(question) - - chat = AzureChatOpenAI( - api_key= OPENAI_API_KEY, - azure_endpoint=OPENAI_API_BASE, - openai_api_version=OPENAI_API_VERSION , - deployment_name=OPENAI_DEPLOYMENT_NAME) - - - pdagent = create_pandas_dataframe_agent(chat, dffinal, verbose=True,agent_type=AgentType.OPENAI_FUNCTIONS, allow_dangerous_code=True, agent_executor_kwargs={"handle_parsing_errors": True}) + + pdagent = create_pandas_dataframe_agent(model, dffinal, verbose=True,agent_type=AgentType.OPENAI_FUNCTIONS, allow_dangerous_code=True, agent_executor_kwargs={"handle_parsing_errors": True}) for chunk in pdagent.stream({"input": question}): if "output" in chunk: output = f'Final Output: ```{chunk["output"]}```' From 802a6d1eb7e1753574cb8d4740c4730fd826de0c Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 11 Jul 2024 01:09:33 +0100 Subject: [PATCH 09/33] cleanup unused code in function --- functions/FileDeletion/__init__.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/functions/FileDeletion/__init__.py b/functions/FileDeletion/__init__.py index 8ec91c4bb..d3da95ec7 100644 --- a/functions/FileDeletion/__init__.py +++ b/functions/FileDeletion/__init__.py @@ -72,23 +72,6 @@ def get_deleted_blobs(blob_service_client: BlobServiceClient) -> list: deleted_blobs.append(blob.name) return deleted_blobs - -def purge_soft_deleted_blob(blob_service_client: BlobServiceClient) -> list: - '''Creates and returns a list of file paths that are soft-deleted.''' - # Create Uploaded Container Client and list all blobs, including deleted blobs - upload_container_client = blob_service_client.get_container_client( - blob_storage_account_upload_container_name) - temp_list = upload_container_client.list_blobs(include="deleted") - - deleted_blobs = [] - # Pull out the soft-deleted blob names - for blob in temp_list: - if blob.deleted: - logging.debug("\t Deleted Blob name: %s", blob.name) - deleted_blobs.append(blob.name) - return deleted_blobs - - def delete_content_blobs(blob_service_client: BlobServiceClient, deleted_blob: str) -> dict: '''Deletes blobs in the content container that correspond to a given soft-deleted blob from the upload container. Returns a list of deleted From ef509fe2fc6cb7e8ac67378cc2fcb3d53306be27 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Tue, 16 Jul 2024 01:33:49 +0100 Subject: [PATCH 10/33] Update FormRec to use RBAC instead of key auth --- app/backend/app.py | 27 +++++++---- .../approaches/chatreadretrieveread.py | 36 +++++++------- app/backend/requirements.txt | 2 +- app/enrichment/app.py | 3 +- app/frontend/src/api/api.ts | 10 ++-- app/frontend/src/api/models.ts | 4 +- .../components/FolderPicker/FolderPicker.tsx | 8 ++-- .../src/components/filepicker/file-picker.tsx | 36 +++++++------- functions/FileFormRecPollingPDF/__init__.py | 7 +-- .../FileFormRecSubmissionPDF/__init__.py | 5 +- functions/FileLayoutParsingOther/__init__.py | 3 +- functions/FileUploadedFunc/__init__.py | 13 +++-- functions/ImageEnrichment/__init__.py | 2 +- functions/TextEnrichment/__init__.py | 5 +- functions/requirements.txt | 6 +-- functions/shared_code/utilities.py | 5 +- functions/shared_code/utilities_helper.py | 2 +- .../ai/docintelligence/docintelligence.tf | 9 ++-- infra/core/ai/docintelligence/outputs.tf | 12 +++-- infra/core/host/functions/functions.tf | 13 +++-- infra/core/host/functions/outputs.tf | 25 ++++++++-- infra/main.tf | 48 +++++++++++-------- infra/outputs.tf | 28 +++++++++++ scripts/extract-content.py | 4 +- scripts/json-to-env.function.debug.sh | 36 +++++++++++--- scripts/json-to-env.sh | 4 -- scripts/json-to-env.webapp.debug.sh | 4 -- 27 files changed, 222 insertions(+), 135 deletions(-) diff --git a/app/backend/app.py b/app/backend/app.py index 23031552d..f8f3b9819 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -10,7 +10,7 @@ import urllib.parse import pandas as pd import pydantic -from datetime import datetime +from datetime import datetime, timedelta from fastapi.staticfiles import StaticFiles from fastapi import FastAPI, File, HTTPException, Request, UploadFile from fastapi.responses import RedirectResponse, StreamingResponse @@ -24,7 +24,7 @@ from azure.identity import ManagedIdentityCredential, AzureAuthorityHosts, DefaultAzureCredential, get_bearer_token_provider from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient from azure.search.documents import SearchClient -from azure.storage.blob import BlobServiceClient +from azure.storage.blob import BlobServiceClient, generate_container_sas, ContainerSasPermissions from approaches.mathassistant import( generate_response, process_agent_response, @@ -332,16 +332,27 @@ async def chat(request: Request): -@app.get("/getblobclient") -async def get_blob_client(): - """Get an authenticated blob client. +@app.get("/getblobclienturl") +async def get_blob_client_url(): + """Get a URL for a file in Blob Storage with SAS token. - This function generates a Blob Client for accessing the Blob Storage Account. + This function generates a Shared Access Signature (SAS) token for accessing a file in Blob Storage. + The generated URL includes the SAS token as a query parameter. Returns: - dict: A dictionary containing the Blob Client object. + dict: A dictionary containing the URL with the SAS token. """ - return {"client": blob_client} + # Obtain the user delegation key + user_delegation_key = blob_client.get_user_delegation_key(key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=2)) + + sas_token = generate_container_sas(account_name=ENV["AZURE_BLOB_STORAGE_ACCOUNT"], + container_name=ENV["AZURE_BLOB_STORAGE_UPLOAD_CONTAINER"], + permission=ContainerSasPermissions(read=True, write=True, delete=False, list=True, tag=True, create=True), + user_delegation_key=user_delegation_key, + expiry=datetime.utcnow() + timedelta(hours=2) + ) + + return {"url": f"{blob_client.url}upload?{sas_token}"} @app.post("/getalluploadstatus") async def get_all_upload_status(request: Request): diff --git a/app/backend/approaches/chatreadretrieveread.py b/app/backend/approaches/chatreadretrieveread.py index 9303c6ff1..3c09b8648 100644 --- a/app/backend/approaches/chatreadretrieveread.py +++ b/app/backend/approaches/chatreadretrieveread.py @@ -6,20 +6,18 @@ import logging import urllib.parse from datetime import datetime, timedelta -from typing import Any, AsyncGenerator, Coroutine, Sequence +from typing import Any, Sequence import openai -from openai import AzureOpenAI from openai import AsyncAzureOpenAI from approaches.approach import Approach from azure.search.documents import SearchClient from azure.search.documents.models import RawVectorQuery from azure.search.documents.models import QueryType from azure.storage.blob import ( - AccountSasPermissions, + BlobSasPermissions, BlobServiceClient, - ResourceTypes, - generate_account_sas, + generate_blob_sas, ) from text import nonewlines from core.modelhelper import get_token_limit @@ -479,20 +477,20 @@ def translate_response(self, response: str, target_language: str) -> str: def get_source_file_with_sas(self, source_file: str) -> str: """ Function to return the source file with a SAS token""" try: - sas_token = generate_account_sas( - self.blob_client.account_name, - self.blob_client.credential.account_key, - resource_types=ResourceTypes(object=True, service=True, container=True), - permission=AccountSasPermissions( - read=True, - write=True, - list=True, - delete=False, - add=True, - create=True, - update=True, - process=False, - ), + separator = "/" + file_path_w_name_no_cont = separator.join( + source_file.split(separator)[4:]) + container_name = separator.join( + source_file.split(separator)[3:4]) + # Obtain the user delegation key + user_delegation_key = self.blob_client.get_user_delegation_key(key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=2)) + + sas_token = generate_blob_sas( + account_name=self.blob_client.account_name, + container_name=container_name, + blob_name=file_path_w_name_no_cont, + user_delegation_key=user_delegation_key, + permission=BlobSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=1), ) return source_file + "?" + sas_token diff --git a/app/backend/requirements.txt b/app/backend/requirements.txt index 48ab12972..d03954ab4 100644 --- a/app/backend/requirements.txt +++ b/app/backend/requirements.txt @@ -6,7 +6,7 @@ azure-mgmt-cognitiveservices==13.5.0 openai==1.35.8 # azure-search-documents==11.4.0 azure-search-documents==11.4.0b11 -azure-storage-blob==12.16.0 +azure-storage-blob==12.20.0 azure-cosmos == 4.7.0 tiktoken == 0.7.0 fastapi == 0.109.1 diff --git a/app/enrichment/app.py b/app/enrichment/app.py index ce3b49fda..7412cb4b1 100644 --- a/app/enrichment/app.py +++ b/app/enrichment/app.py @@ -38,6 +38,7 @@ "AZURE_BLOB_STORAGE_ACCOUNT": None, "AZURE_BLOB_STORAGE_CONTAINER": None, "AZURE_BLOB_STORAGE_ENDPOINT": None, + "AZURE_QUEUE_STORAGE_ENDPOINT": None, "AZURE_BLOB_STORAGE_UPLOAD_CONTAINER": None, "COSMOSDB_URL": None, "COSMOSDB_KEY": None, @@ -445,7 +446,7 @@ def poll_queue() -> None: if requeue_count <= int(ENV["MAX_EMBEDDING_REQUEUE_COUNT"]): message_json['embeddings_queued_count'] = requeue_count # Requeue with a random backoff within limits - queue_client = QueueClient(account_url=ENV["AZURE_BLOB_STORAGE_ENDPOINT"], + queue_client = QueueClient(account_url=ENV["AZURE_QUEUE_STORAGE_ENDPOINT"], queue_name=ENV["EMBEDDINGS_QUEUE"], credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) diff --git a/app/frontend/src/api/api.ts b/app/frontend/src/api/api.ts index 80eb9518f..2a58f8282 100644 --- a/app/frontend/src/api/api.ts +++ b/app/frontend/src/api/api.ts @@ -3,7 +3,7 @@ import { ChatResponse, ChatRequest, - BlobClientResponse, + BlobClientUrlResponse, AllFilesUploadStatus, GetUploadStatusRequest, GetInfoResponse, @@ -64,20 +64,20 @@ export function getCitationFilePath(citation: string): string { return `${encodeURIComponent(citation)}`; } -export async function getBlobClient(): Promise { - const response = await fetch("/getblobclient", { +export async function getBlobClientUrl(): Promise { + const response = await fetch("/getblobclienturl", { method: "GET", headers: { "Content-Type": "application/json" } }); - const parsedResponse: BlobClientResponse = await response.json(); + const parsedResponse: BlobClientUrlResponse = await response.json(); if (response.status > 299 || !response.ok) { throw Error(parsedResponse.error || "Unknown error"); } - return parsedResponse.client; + return decodeURIComponent(parsedResponse.url); } export async function getAllUploadStatus(options: GetUploadStatusRequest): Promise { diff --git a/app/frontend/src/api/models.ts b/app/frontend/src/api/models.ts index 1b498b02f..34a13191e 100644 --- a/app/frontend/src/api/models.ts +++ b/app/frontend/src/api/models.ts @@ -67,8 +67,8 @@ export type ChatRequest = { thought_chain: { [key: string]: string }; }; -export type BlobClientResponse = { - client: any; +export type BlobClientUrlResponse = { + url: string; error?: string; }; diff --git a/app/frontend/src/components/FolderPicker/FolderPicker.tsx b/app/frontend/src/components/FolderPicker/FolderPicker.tsx index 7cc62531e..c392e609e 100644 --- a/app/frontend/src/components/FolderPicker/FolderPicker.tsx +++ b/app/frontend/src/components/FolderPicker/FolderPicker.tsx @@ -18,9 +18,9 @@ import { ITextFieldStyleProps, ITextFieldStyles, TextField } from '@fluentui/rea import { ILabelStyles, ILabelStyleProps } from '@fluentui/react/lib/Label'; import { IIconProps } from '@fluentui/react'; import { IButtonProps } from '@fluentui/react/lib/Button'; -import { BlobServiceClient } from "@azure/storage-blob"; +import { ContainerClient } from "@azure/storage-blob"; -import { getBlobClient } from "../../api"; +import { getBlobClientUrl } from "../../api"; import styles from "./FolderPicker.module.css"; var allowNewFolders = false; @@ -85,8 +85,8 @@ export const FolderPicker = ({allowFolderCreation, onSelectedKeyChange, preSelec async function fetchBlobFolderData() { try { - const blobServiceClient = await getBlobClient() as BlobServiceClient; - var containerClient = blobServiceClient.getContainerClient("upload"); + const blobClientUrl = await getBlobClientUrl(); + var containerClient = new ContainerClient(blobClientUrl); const delimiter = "/"; const prefix = ""; var newOptions: IComboBoxOption[] = allowNewFolders ? [] : [ diff --git a/app/frontend/src/components/filepicker/file-picker.tsx b/app/frontend/src/components/filepicker/file-picker.tsx index 30a625fd9..596377b55 100644 --- a/app/frontend/src/components/filepicker/file-picker.tsx +++ b/app/frontend/src/components/filepicker/file-picker.tsx @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { BlobServiceClient } from "@azure/storage-blob"; +import { ContainerClient } from "@azure/storage-blob"; import classNames from "classnames"; import { nanoid } from "nanoid"; import { useCallback, useEffect, useMemo, useState } from "react"; import { DropZone } from "./drop-zone" import styles from "./file-picker.module.css"; import { FilesList } from "./files-list"; -import { getBlobClient, logStatus, StatusLogClassification, StatusLogEntry, StatusLogState } from "../../api" +import { getBlobClientUrl, logStatus, StatusLogClassification, StatusLogEntry, StatusLogState } from "../../api" interface Props { folderPath: string; @@ -50,32 +50,34 @@ const FilePicker = ({folderPath, tags}: Props) => { setUploadStarted(true); // create an instance of the BlobServiceClient - const blobServiceClient = await getBlobClient() as BlobServiceClient; - - const containerClient = blobServiceClient.getContainerClient("upload"); + const blobClientUrl = await getBlobClientUrl(); + + const containerClient = new ContainerClient(blobClientUrl); var counter = 1; files.forEach(async (indexedFile: any) => { // add each file into Azure Blob Storage var file = indexedFile.file as File; var filePath = (folderPath == "") ? file.name : folderPath + "/" + file.name; - const blobClient = containerClient.getBlockBlobClient(filePath); // set mimetype as determined from browser with file upload control const options = { blobHTTPHeaders: { blobContentType: file.type }, metadata: { tags: tags.map(encodeURIComponent).join(",") } }; - - // upload file - blobClient.uploadData(file, options); - //write status to log - var logEntry: StatusLogEntry = { - path: "upload/"+filePath, - status: "File uploaded from browser to Azure Blob Storage", - status_classification: StatusLogClassification.Info, - state: StatusLogState.Uploaded + try { + // upload file + await containerClient.uploadBlockBlob(filePath, file, file.size, options) + //write status to log + var logEntry: StatusLogEntry = { + path: "upload/"+filePath, + status: "File uploaded from browser to Azure Blob Storage", + status_classification: StatusLogClassification.Info, + state: StatusLogState.Uploaded + } + await logStatus(logEntry); + } + catch (error) { + console.log("Unable to upload file"+filePath+" : Error: "+error); } - await logStatus(logEntry); - setProgress((counter/files.length) * 100); counter++; }); diff --git a/functions/FileFormRecPollingPDF/__init__.py b/functions/FileFormRecPollingPDF/__init__.py index ac0915ed7..4729ca0ed 100644 --- a/functions/FileFormRecPollingPDF/__init__.py +++ b/functions/FileFormRecPollingPDF/__init__.py @@ -21,6 +21,7 @@ def string_to_bool(s): azure_blob_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] +azure_queue_storage_endpoint = os.environ["AZURE_QUEUE_STORAGE_ENDPOINT"] azure_blob_drop_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_log_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME"] @@ -119,7 +120,7 @@ def main(msg: func.QueueMessage) -> None: statusLog.upsert_document(blob_name, f'{function_name} - Chunking complete, {chunk_count} chunks created.', StatusClassification.DEBUG) # submit message to the enrichment queue to continue processing - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=text_enrichment_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) @@ -136,7 +137,7 @@ def main(msg: func.QueueMessage) -> None: queued_count += 1 message_json['polling_queue_count'] = queued_count statusLog.upsert_document(blob_name, f"{function_name} - FR has not completed processing, requeuing. Polling back off of attempt {queued_count} of {max_polling_requeue_count} for {backoff} seconds", StatusClassification.DEBUG, State.QUEUED) - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=pdf_polling_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) @@ -148,7 +149,7 @@ def main(msg: func.QueueMessage) -> None: # unexpected status returned by FR, such as internal capacity overload, so requeue if submit_queued_count < max_submit_requeue_count: statusLog.upsert_document(blob_name, f'{function_name} - unhandled response from Form Recognizer- code: {response.status_code} status: {response_status} - text: {response.text}. Document will be resubmitted', StatusClassification.ERROR) - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=pdf_submit_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) diff --git a/functions/FileFormRecSubmissionPDF/__init__.py b/functions/FileFormRecSubmissionPDF/__init__.py index 957f34d40..ab6890586 100644 --- a/functions/FileFormRecSubmissionPDF/__init__.py +++ b/functions/FileFormRecSubmissionPDF/__init__.py @@ -14,6 +14,7 @@ azure_blob_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] +azure_queue_storage_endpoint = os.environ["AZURE_QUEUE_STORAGE_ENDPOINT"] azure_blob_drop_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME" ] @@ -125,7 +126,7 @@ def main(msg: func.QueueMessage) -> None: result_id = response.headers.get("apim-request-id") message_json["FR_resultId"] = result_id message_json["polling_queue_count"] = 1 - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=pdf_polling_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) @@ -155,7 +156,7 @@ def main(msg: func.QueueMessage) -> None: f"{FUNCTION_NAME} - Throttled on PDF submission to FR, requeuing. Back off of {backoff} seconds", StatusClassification.DEBUG, ) - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=pdf_submit_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) diff --git a/functions/FileLayoutParsingOther/__init__.py b/functions/FileLayoutParsingOther/__init__.py index 84c05aff0..4403e6596 100644 --- a/functions/FileLayoutParsingOther/__init__.py +++ b/functions/FileLayoutParsingOther/__init__.py @@ -17,6 +17,7 @@ azure_blob_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] +azure_queue_storage_endpoint = os.environ["AZURE_QUEUE_STORAGE_ENDPOINT"] azure_blob_drop_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_log_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME"] @@ -206,7 +207,7 @@ def main(msg: func.QueueMessage) -> None: statusLog.upsert_document(blob_name, f'{function_name} - chunking stored.', StatusClassification.DEBUG) # submit message to the text enrichment queue to continue processing - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=text_enrichment_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) diff --git a/functions/FileUploadedFunc/__init__.py b/functions/FileUploadedFunc/__init__.py index 744880a6b..dbfbaba81 100644 --- a/functions/FileUploadedFunc/__init__.py +++ b/functions/FileUploadedFunc/__init__.py @@ -28,6 +28,7 @@ max_seconds_hide_on_upload = int(os.environ["MAX_SECONDS_HIDE_ON_UPLOAD"]) azure_blob_content_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] +azure_queue_endpoint = os.environ["AZURE_QUEUE_STORAGE_ENDPOINT"] azure_blob_upload_container = os.environ["BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME"] azure_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_search_service_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] @@ -121,10 +122,8 @@ def main(myblob: func.InputStream): } message_string = json.dumps(message) - blob_client = BlobServiceClient( - account_url=azure_blob_endpoint, - credential=azure_credential, - ) + blob_client = BlobServiceClient(azure_blob_endpoint, + credential=azure_credential) myblob_filename = myblob.name.split("/", 1)[1] # Check if the blob has been marked as 'do not process' and abort if so @@ -169,12 +168,12 @@ def main(myblob: func.InputStream): logging.debug("No items to delete from AI Search index.") # write tags to cosmos db once per file/message - blob_service_client = BlobServiceClient(account_url=azure_blob_endpoint, credential=azure_credential) + blob_service_client = BlobServiceClient(azure_blob_endpoint, credential=azure_credential) upload_container_client = blob_service_client.get_container_client(azure_blob_upload_container) - tag_list = get_tags_and_upload_to_cosmos(upload_container_client, myblob.name) + get_tags_and_upload_to_cosmos(upload_container_client, myblob.name) # Queue message with a random backoff so as not to put the next function under unnecessary load - queue_client = QueueClient(account_url=azure_blob_endpoint, + queue_client = QueueClient(account_url=azure_queue_endpoint, queue_name=queue_name, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 3ebfe4038..f922c0024 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -28,7 +28,7 @@ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] # Authentication settings -azure_authority_host = os.environ["AZURE_AUTHORITY_HOST"] +azure_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] local_debug = os.environ.get("LOCAL_DEBUG", False) # Cosmos DB diff --git a/functions/TextEnrichment/__init__.py b/functions/TextEnrichment/__init__.py index d11d0d157..046bca3be 100644 --- a/functions/TextEnrichment/__init__.py +++ b/functions/TextEnrichment/__init__.py @@ -15,6 +15,7 @@ azure_blob_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] +azure_queue_storage_endpoint = os.environ["AZURE_QUEUE_STORAGE_ENDPOINT"] azure_blob_drop_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME" ] @@ -225,7 +226,7 @@ def main(msg: func.QueueMessage) -> None: block_blob_client.upload_blob(json_str, overwrite=True) # Queue message to embeddings queue for downstream processing - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=queueName, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) @@ -298,7 +299,7 @@ def requeue(response, message_json): ) queued_count += 1 message_json["text_enrichment_queued_count"] = queued_count - queue_client = QueueClient(account_url=azure_blob_storage_endpoint, + queue_client = QueueClient(account_url=azure_queue_storage_endpoint, queue_name=text_enrichment_queue, credential=azure_credential, message_encode_policy=TextBase64EncodePolicy()) diff --git a/functions/requirements.txt b/functions/requirements.txt index 973046950..e0de17354 100644 --- a/functions/requirements.txt +++ b/functions/requirements.txt @@ -5,9 +5,9 @@ #### Any version change made here should also be made and tested for the web apps in /app/backend and /app/enrichment azure-functions == 1.17.0 -tiktoken==0.4.0 +tiktoken==0.7.0 azure.ai.formrecognizer==3.2.1 -azure-storage-blob==12.16.0 +azure-storage-blob==12.20.0 azure-core == 1.30.2 lxml == 4.9.2 azure-cosmos == 4.7.0 @@ -19,4 +19,4 @@ unstructured[csv,doc,docx,email,html,md,msg,ppt,pptx,text,xlsx,xml] == 0.12.5 pyoo == 1.4 azure-search-documents == 11.4.0b11 beautifulsoup4 == 4.12.2 -azure-identity==1.16.1 \ No newline at end of file +azure-identity==1.17.1 \ No newline at end of file diff --git a/functions/shared_code/utilities.py b/functions/shared_code/utilities.py index 020c427c0..d98eeed25 100644 --- a/functions/shared_code/utilities.py +++ b/functions/shared_code/utilities.py @@ -76,6 +76,7 @@ def __init__(self, self.azure_blob_storage_endpoint = azure_blob_storage_endpoint self.azure_blob_drop_storage_container = azure_blob_drop_storage_container self.azure_blob_content_storage_container = azure_blob_content_storage_container + self.azure_credential = azure_credential self.utilities_helper = UtilitiesHelper(azure_blob_storage_account, azure_blob_storage_endpoint, azure_credential) @@ -86,7 +87,7 @@ def write_blob(self, output_container, content, output_filename, folder_set=""): # Get path and file name minus the root container blob_service_client = BlobServiceClient( self.azure_blob_storage_endpoint, - credential=azure_credential + credential=self.azure_credential ) block_blob_client = blob_service_client.get_blob_client( container=output_container, blob=f'{folder_set}{output_filename}') @@ -326,7 +327,7 @@ def write_chunk(self, myblob_name, myblob_uri, file_number, chunk_size, chunk_te file_name, file_extension, file_directory = self.get_filename_and_extension(myblob_name) blob_service_client = BlobServiceClient( self.azure_blob_storage_endpoint, - credential=azure_credential) + credential=self.azure_credential) json_str = json.dumps(chunk_output, indent=2, ensure_ascii=False) block_blob_client = blob_service_client.get_blob_client( container=self.azure_blob_content_storage_container, diff --git a/functions/shared_code/utilities_helper.py b/functions/shared_code/utilities_helper.py index ce04fd46d..e3e6551f4 100644 --- a/functions/shared_code/utilities_helper.py +++ b/functions/shared_code/utilities_helper.py @@ -41,7 +41,7 @@ def get_blob_and_sas(self, blob_path): blob_path.split(separator)[0:1]) # Obtain the user delegation key - user_delegation_key = self.blob_service_client.get_user_delegation_key(key_start_time=datetime.now(datetime.UTC), key_expiry_time=datetime.now(datetime.UTC) + timedelta(hours=12)) + user_delegation_key = self.blob_service_client.get_user_delegation_key(key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=12)) # Gen SAS token sas_token = generate_blob_sas( diff --git a/infra/core/ai/docintelligence/docintelligence.tf b/infra/core/ai/docintelligence/docintelligence.tf index a28877e18..a75bbc7f4 100644 --- a/infra/core/ai/docintelligence/docintelligence.tf +++ b/infra/core/ai/docintelligence/docintelligence.tf @@ -1,4 +1,4 @@ -resource "azurerm_cognitive_account" "formRecognizerAccount" { +resource "azurerm_cognitive_account" "docIntelligenceAccount" { name = var.name location = var.location resource_group_name = var.resourceGroupName @@ -8,6 +8,9 @@ resource "azurerm_cognitive_account" "formRecognizerAccount" { public_network_access_enabled = var.is_secure_mode ? false : true local_auth_enabled = var.is_secure_mode ? false : true tags = var.tags + identity { + type = "SystemAssigned" + } } data "azurerm_subnet" "subnet" { @@ -17,7 +20,7 @@ data "azurerm_subnet" "subnet" { resource_group_name = var.resourceGroupName } -resource "azurerm_private_endpoint" "formPrivateEndpoint" { +resource "azurerm_private_endpoint" "docintPrivateEndpoint" { count = var.is_secure_mode ? 1 : 0 name = "${var.name}-private-endpoint" location = var.location @@ -28,7 +31,7 @@ resource "azurerm_private_endpoint" "formPrivateEndpoint" { private_service_connection { name = "cognitiveAccount" is_manual_connection = false - private_connection_resource_id = azurerm_cognitive_account.formRecognizerAccount.id + private_connection_resource_id = azurerm_cognitive_account.docIntelligenceAccount.id subresource_names = ["account"] } diff --git a/infra/core/ai/docintelligence/outputs.tf b/infra/core/ai/docintelligence/outputs.tf index f7994f894..17b327e80 100644 --- a/infra/core/ai/docintelligence/outputs.tf +++ b/infra/core/ai/docintelligence/outputs.tf @@ -1,15 +1,19 @@ output "formRecognizerAccountName" { - value = azurerm_cognitive_account.formRecognizerAccount.name + value = azurerm_cognitive_account.docIntelligenceAccount.name } output "formRecognizerAccountEndpoint" { - value = azurerm_cognitive_account.formRecognizerAccount.endpoint + value = azurerm_cognitive_account.docIntelligenceAccount.endpoint } output "formRecognizerAccount" { - value = azurerm_cognitive_account.formRecognizerAccount.id + value = azurerm_cognitive_account.docIntelligenceAccount.id } output "formPrivateEndpoint" { - value = var.is_secure_mode ? azurerm_private_endpoint.formPrivateEndpoint[0].id : null + value = var.is_secure_mode ? azurerm_private_endpoint.docintPrivateEndpoint[0].id : null +} + +output "docIntelligenceIdentity" { + value = azurerm_cognitive_account.docIntelligenceAccount.identity } \ No newline at end of file diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 7f9e0e807..2ef68bbf8 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -118,19 +118,15 @@ resource "azurerm_linux_function_app" "function_app" { type = "SystemAssigned" } - connection_string { - name = "STORAGE_CONNECTION_STRING" - type = "Custom" - value = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BLOB-CONNECTION-STRING)" - } - app_settings = { WEBSITE_VNET_ROUTE_ALL = "1" WEBSITE_CONTENTOVERVNET = var.is_secure_mode ? "1" : "0" SCM_DO_BUILD_DURING_DEPLOYMENT = "false" ENABLE_ORYX_BUILD = "false" - AzureWebJobsStorage = "DefaultEndpointsProtocol=https;AccountName=${var.blobStorageAccountName};EndpointSuffix=${var.endpointSuffix};AccountKey=${data.azurerm_storage_account.existing_sa.primary_access_key}" + AzureWebJobsStorage = "" AzureWebJobsStorage__accountName = var.blobStorageAccountName + AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" + AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" WEBSITE_CONTENTAZUREFILECONNECTIONSTRING = "DefaultEndpointsProtocol=https;AccountName=${var.blobStorageAccountName};EndpointSuffix=${var.endpointSuffix};AccountKey=${data.azurerm_storage_account.existing_sa.primary_access_key}" WEBSITE_CONTENTSHARE = "funcfileshare" FUNCTIONS_WORKER_RUNTIME = var.runtime @@ -178,6 +174,9 @@ resource "azurerm_linux_function_app" "function_app" { AZURE_SEARCH_SERVICE_ENDPOINT = var.azureSearchServiceEndpoint AZURE_SEARCH_INDEX = var.azureSearchIndex WEBSITE_PULL_IMAGE_OVER_VNET = var.is_secure_mode ? "true" : "false" + STORAGE_CONNECTION_STRING = "" + STORAGE_CONNECTION_STRING__accountName = var.blobStorageAccountName + STORAGE_CONNECTION_STRING__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" STORAGE_CONNECTION_STRING__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_credential_domain } diff --git a/infra/core/host/functions/outputs.tf b/infra/core/host/functions/outputs.tf index 485f36280..077218d54 100644 --- a/infra/core/host/functions/outputs.tf +++ b/infra/core/host/functions/outputs.tf @@ -6,11 +6,6 @@ output "function_app_identity_principal_id" { value = azurerm_linux_function_app.function_app.identity.0.principal_id } - -# output "id" { -# value = azurerm_service_plan.funcServicePlan.id -# } - output "name" { value = azurerm_service_plan.funcServicePlan.name } @@ -21,4 +16,24 @@ output "subnet_integration_id" { output "identityPrincipalId" { value = azurerm_linux_function_app.function_app.identity.0.principal_id +} + +output "AzureWebJobsStorage__accountName" { + value = var.blobStorageAccountName +} + +output "AzureWebJobsStorage__blobServiceUri" { + value = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" +} + +output "STORAGE_CONNECTION_STRING__accountName" { + value = var.blobStorageAccountName +} + +output "STORAGE_CONNECTION_STRING__queueServiceUri" { + value = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" +} + +output "STORAGE_CONNECTION_STRING__blobServiceUri" { + value = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" } \ No newline at end of file diff --git a/infra/main.tf b/infra/main.tf index 3a1d8cb2f..46eedc70b 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -2,11 +2,9 @@ locals { tags = { ProjectName = "Information Assistant", BuildNumber = var.buildNumber } azure_roles = jsondecode(file("${path.module}/azure_roles.json")) selected_roles = ["CognitiveServicesOpenAIUser", - "CognitiveServicesUser", - "StorageBlobDataReader", - "StorageBlobDataContributor", - "StorageQueueDataMessageProcessor", - "SearchIndexDataReader", + "CognitiveServicesUser", + "StorageBlobDataOwner", + "StorageQueueDataContributor", "SearchIndexDataContributor"] } @@ -689,7 +687,7 @@ data "azurerm_resource_group" "existing" { } # # // SYSTEM IDENTITY ROLES -module "openAiRoleBackend" { +module "webApp_OpenAiRole" { source = "./core/security/role" scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id @@ -700,7 +698,7 @@ module "openAiRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "enrichmentOpenAiRoleBackend" { +module "enrichmentApp_OpenAiRole" { source = "./core/security/role" scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id @@ -711,7 +709,7 @@ module "enrichmentOpenAiRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "webAppCognitiveServicesUserBackend" { +module "webApp_CognitiveServicesUser" { source = "./core/security/role" scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id @@ -722,7 +720,7 @@ module "webAppCognitiveServicesUserBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "functionAppCognitiveServicesUserBackend" { +module "functionApp_CognitiveServicesUser" { source = "./core/security/role" scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id @@ -733,29 +731,29 @@ module "functionAppCognitiveServicesUserBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "webAppStorageQueueDataMessageProcessorBackend" { +module "enrichmentApp_StorageQueueDataContributor" { source = "./core/security/role" scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id - principalId = module.webapp.identityPrincipalId - roleDefinitionId = local.azure_roles.StorageQueueDataMessageProcessor + principalId = module.enrichmentApp.identityPrincipalId + roleDefinitionId = local.azure_roles.StorageQueueDataContributor principalType = "ServicePrincipal" subscriptionId = data.azurerm_client_config.current.subscription_id resourceGroupId = azurerm_resource_group.rg.id } -module "functionAppStorageQueueDataMessageProcessorBackend" { +module "functionApp_StorageQueueDataContributor" { source = "./core/security/role" scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id principalId = module.functions.identityPrincipalId - roleDefinitionId = local.azure_roles.StorageQueueDataMessageProcessor + roleDefinitionId = local.azure_roles.StorageQueueDataContributor principalType = "ServicePrincipal" subscriptionId = data.azurerm_client_config.current.subscription_id resourceGroupId = azurerm_resource_group.rg.id } -module "storageRoleBackend" { +module "webApp_StorageBlobDataReader" { source = "./core/security/role" scope = azurerm_resource_group.rg.id @@ -766,7 +764,7 @@ module "storageRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "searchRoleBackend" { +module "webApp_SearchIndexDataReader" { source = "./core/security/role" scope = azurerm_resource_group.rg.id @@ -777,7 +775,7 @@ module "searchRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "functionSearchRoleBackend" { +module "functionApp_SearchIndexDataReader" { source = "./core/security/role" scope = azurerm_resource_group.rg.id @@ -788,7 +786,7 @@ module "functionSearchRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "encrichmentSearchRoleBackend" { +module "encrichmentApp_SearchIndexDataReader" { source = "./core/security/role" scope = azurerm_resource_group.rg.id @@ -799,17 +797,27 @@ module "encrichmentSearchRoleBackend" { resourceGroupId = azurerm_resource_group.rg.id } -module "storageRoleFunc" { +module "fuctionApp_StorageBlobDataOwner" { source = "./core/security/role" scope = azurerm_resource_group.rg.id principalId = module.functions.function_app_identity_principal_id - roleDefinitionId = local.azure_roles.StorageBlobDataReader + roleDefinitionId = local.azure_roles.StorageBlobDataOwner principalType = "ServicePrincipal" subscriptionId = data.azurerm_client_config.current.subscription_id resourceGroupId = azurerm_resource_group.rg.id } +module "docIntel_StorageBlobDataReader" { + source = "./core/security/role" + scope = azurerm_resource_group.rg.id + principalId = module.aiDocIntelligence.docIntelligenceIdentity + roleDefinitionId = local.azure_roles.StorageBlobDataReader + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + module "aviRoleBackend" { source = "./core/security/role" count = var.enableMultimedia ? 1 : 0 diff --git a/infra/outputs.tf b/infra/outputs.tf index 28eeb3e17..a4d4e7353 100644 --- a/infra/outputs.tf +++ b/infra/outputs.tf @@ -206,4 +206,32 @@ output "DNS_PRIVATE_RESOLVER_IP" { output "AZURE_AI_CREDENTIAL_DOMAIN" { value = var.azure_ai_private_link_domain +} + +output "FUNC_AzureWebJobsStorage__accountName" { + value = module.functions.AzureWebJobsStorage__accountName +} + +output "FUNC_AzureWebJobsStorage__blobServiceUri" { + value = module.functions.AzureWebJobsStorage__blobServiceUri +} + +output "FUNC_STORAGE_CONNECTION_STRING__accountName" { + value = module.functions.STORAGE_CONNECTION_STRING__accountName +} + +output "FUNC_STORAGE_CONNECTION_STRING__queueServiceUri" { + value = module.functions.STORAGE_CONNECTION_STRING__queueServiceUri +} + +output "FUNC_STORAGE_CONNECTION_STRING__blobServiceUri" { + value = module.functions.STORAGE_CONNECTION_STRING__blobServiceUri +} + +output "AZURE_OPENAI_AUTHORITY_HOST" { + value = var.azure_openai_authority_host +} + +output "AZURE_QUEUE_STORAGE_ENDPOINT" { + value = module.storage.primary_queue_endpoint } \ No newline at end of file diff --git a/scripts/extract-content.py b/scripts/extract-content.py index 10143314a..f053c2659 100755 --- a/scripts/extract-content.py +++ b/scripts/extract-content.py @@ -381,7 +381,7 @@ def get_storage_account_endpoint(storage_account_name): for blob in blob_list: # Obtain the user delegation key - old_user_delegation_key = old_blob_service_client.get_user_delegation_key(key_start_time=datetime.now(datetime.UTC), key_expiry_time=datetime.now(datetime.UTC) + timedelta(hours=12)) + old_user_delegation_key = old_blob_service_client.get_user_delegation_key(key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=12)) # Generate SAS token for old blob old_sas_token = generate_container_sas( @@ -394,7 +394,7 @@ def get_storage_account_endpoint(storage_account_name): source_url = f"https://{old_azure_blob_storage_account}.blob.core.windows.net/{container_name}/{urllib.parse.quote(blob.name)}?{old_sas_token}" # Obtain the user delegation key - new_user_delegation_key = new_blob_service_client.get_user_delegation_key(key_start_time=datetime.now(datetime.UTC), key_expiry_time=datetime.now(datetime.UTC) + timedelta(hours=12)) + new_user_delegation_key = new_blob_service_client.get_user_delegation_key(key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=12)) # Generate SAS token for new blob new_sas_token = generate_container_sas( diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index a2f9a18a6..084386356 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -79,8 +79,24 @@ jq -r --arg secrets "$secrets" ' "env_var": "COSMOSDB_LOG_CONTAINER_NAME" }, { - "path": "AzureWebJobsStorage", - "env_var": "AzureWebJobsStorage" + "path": "FUNC_AzureWebJobsStorage__accountName", + "env_var": "AzureWebJobsStorage__accountName" + }, + { + "path": "FUNC_AzureWebJobsStorage__blobServiceUri", + "env_var": "AzureWebJobsStorage__blobServiceUri" + }, + { + "path": "FUNC_STORAGE_CONNECTION_STRING__accountName", + "env_var": "STORAGE_CONNECTION_STRING__accountName" + }, + { + "path": "FUNC_STORAGE_CONNECTION_STRING__queueServiceUri", + "env_var": "STORAGE_CONNECTION_STRING__queueServiceUri" + }, + { + "path": "FUNC_STORAGE_CONNECTION_STRING__blobServiceUri", + "env_var": "STORAGE_CONNECTION_STRING__blobServiceUri" }, { "path": "AZURE_AI_ENDPOINT", @@ -102,6 +118,10 @@ jq -r --arg secrets "$secrets" ' "path": "BLOB_STORAGE_ACCOUNT_ENDPOINT", "env_var": "BLOB_STORAGE_ACCOUNT_ENDPOINT" }, + { + "path": "AZURE_QUEUE_STORAGE_ENDPOINT", + "env_var": "AZURE_QUEUE_STORAGE_ENDPOINT" + }, { "path": "AZURE_LOCATION", "env_var": "AZURE_AI_LOCATION" @@ -114,10 +134,6 @@ jq -r --arg secrets "$secrets" ' "path": "AZURE_SEARCH_SERVICE_ENDPOINT", "env_var": "AZURE_SEARCH_SERVICE_ENDPOINT" }, - { - "path": "AZURE_KEYVAULT_NAME", - "env_var": "AZURE_KEYVAULT_NAME" - }, { "path": "AZURE_AI_LOCATION", "env_var": "AZURE_AI_LOCATION" @@ -125,6 +141,10 @@ jq -r --arg secrets "$secrets" ' { "path": "AZURE_AI_CREDENTIAL_DOMAIN", "env_var": "AZURE_AI_CREDENTIAL_DOMAIN" + }, + { + "path": "AZURE_OPENAI_AUTHORITY_HOST", + "env_var": "AZURE_OPENAI_AUTHORITY_HOST" } ] as $env_vars_to_extract @@ -145,7 +165,7 @@ jq -r --arg secrets "$secrets" ' | reduce .[] as $item ({}; .[$item.key] = $item.value) | - {"IsEncrypted": false, "Values": (. + {"FUNCTIONS_WORKER_RUNTIME": "python", + {"IsEncrypted": false, "Values": (. + {"FUNCTIONS_WORKER_RUNTIME": "python", "AzureWebJobs.parse_html_w_form_rec.Disabled": "true", "MAX_SECONDS_HIDE_ON_UPLOAD": "30", "MAX_SUBMIT_REQUEUE_COUNT": "10", @@ -166,6 +186,8 @@ jq -r --arg secrets "$secrets" ' "TEXT_ENRICHMENT_QUEUE": "text-enrichment-queue", "IMAGE_ENRICHMENT_QUEUE": "image-enrichment-queue", "LOCAL_DEBUG": "true", + "AzureWebJobsStorage": "", + "STORAGE_CONNECTION_STRING": "" } + ($secrets | fromjson) )} diff --git a/scripts/json-to-env.sh b/scripts/json-to-env.sh index 8e3a6186a..bba4972ea 100755 --- a/scripts/json-to-env.sh +++ b/scripts/json-to-env.sh @@ -102,10 +102,6 @@ jq -r ' "path": "ENRICHMENT_APPSERVICE_NAME", "env_var": "ENRICHMENT_APPSERVICE_NAME" }, - { - "path": "AZURE_KEYVAULT_NAME", - "env_var": "AZURE_KEYVAULT_NAME" - }, { "path": "MAX_CSV_FILE_SIZE", "env_var": "MAX_CSV_FILE_SIZE" diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index 32d150f84..37fc4cd0b 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -116,10 +116,6 @@ jq -r ' "path": "ENRICHMENT_APPSERVICE_URL", "env_var": "ENRICHMENT_APPSERVICE_URL" }, - { - "path": "AZURE_KEYVAULT_NAME", - "env_var": "AZURE_KEYVAULT_NAME" - }, { "path": "AZURE_OPENAI_CHATGPT_MODEL_NAME", "env_var": "AZURE_OPENAI_CHATGPT_MODEL_NAME" From 25e33fc46c40666be0eb30121757702a59836584 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:42:10 +0100 Subject: [PATCH 11/33] cleanup TF output variables --- app/backend/app.py | 6 +- app/frontend/src/api/api.ts | 2 +- functions/ImageEnrichment/__init__.py | 138 ++++++++++++----------- functions/requirements.txt | 2 +- infra/core/ai/docintelligence/outputs.tf | 2 +- infra/main.tf | 4 +- infra/outputs.tf | 4 - 7 files changed, 78 insertions(+), 80 deletions(-) diff --git a/app/backend/app.py b/app/backend/app.py index f8f3b9819..58a09626b 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -345,14 +345,14 @@ async def get_blob_client_url(): # Obtain the user delegation key user_delegation_key = blob_client.get_user_delegation_key(key_start_time=datetime.utcnow(), key_expiry_time=datetime.utcnow() + timedelta(hours=2)) - sas_token = generate_container_sas(account_name=ENV["AZURE_BLOB_STORAGE_ACCOUNT"], + sas_token = generate_container_sas(account_name=blob_client.account_name, container_name=ENV["AZURE_BLOB_STORAGE_UPLOAD_CONTAINER"], - permission=ContainerSasPermissions(read=True, write=True, delete=False, list=True, tag=True, create=True), + permission=ContainerSasPermissions(read=True, write=True, delete=False, list=True, tag=True), user_delegation_key=user_delegation_key, expiry=datetime.utcnow() + timedelta(hours=2) ) - return {"url": f"{blob_client.url}upload?{sas_token}"} + return {"url": f"{blob_client.url}upload?{sas_token}&restype=container"} @app.post("/getalluploadstatus") async def get_all_upload_status(request: Request): diff --git a/app/frontend/src/api/api.ts b/app/frontend/src/api/api.ts index 2a58f8282..91b072f15 100644 --- a/app/frontend/src/api/api.ts +++ b/app/frontend/src/api/api.ts @@ -77,7 +77,7 @@ export async function getBlobClientUrl(): Promise { throw Error(parsedResponse.error || "Unknown error"); } - return decodeURIComponent(parsedResponse.url); + return parsedResponse.url; } export async function getAllUploadStatus(options: GetUploadStatusRequest): Promise { diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index f922c0024..883857640 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -2,7 +2,6 @@ import logging import os -import azure.ai.vision as visionsdk import azure.functions as func import requests from azure.storage.blob import BlobServiceClient @@ -38,7 +37,6 @@ cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] # Cognitive Services -azure_ai_key = os.environ["AZURE_AI_KEY"] azure_ai_endpoint = os.environ["AZURE_AI_ENDPOINT"] azure_ai_location = os.environ["AZURE_AI_LOCATION"] azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] @@ -62,11 +60,11 @@ targetTranslationLanguage = os.environ["TARGET_TRANSLATION_LANGUAGE"] API_DETECT_ENDPOINT = ( - f"{azure_ai_endpoint}translator/text/v3.0/detect" - ) + f"{azure_ai_endpoint}translator/text/v3.0/detect" +) API_TRANSLATE_ENDPOINT = ( - f"{azure_ai_endpoint}translator/text/v3.0/translate" - ) + f"{azure_ai_endpoint}translator/text/v3.0/translate" +) MAX_CHARS_FOR_DETECTION = 1000 translator_api_headers = { @@ -75,15 +73,13 @@ "Ocp-Apim-Subscription-Region": azure_ai_location, } -# Vision SDK -vision_service_options = visionsdk.VisionServiceOptions( - endpoint=azure_ai_endpoint, key=azure_ai_key -) - -analysis_options = visionsdk.ImageAnalysisOptions() +vison_api_headers = { + "Auhorization": f"Bearer {token_provider()}", + "Content-type": "application/json", +} -# Note that "CAPTION" and "DENSE_CAPTIONS" are only supported in Azure GPU regions (East US, France Central, -# Korea Central, North Europe, Southeast Asia, West Europe, West US). Remove "CAPTION" and "DENSE_CAPTIONS" +# Note that "cation" and "denseCaptions" are only supported in Azure GPU regions (East US, France Central, +# Korea Central, North Europe, Southeast Asia, West Europe, West US). Remove "caption" and "denseCaptions" # from the list below if your Computer Vision key is not from one of those regions. if azure_ai_location in [ @@ -96,23 +92,22 @@ "westus", ]: GPU_REGION = True - analysis_options.features = ( - visionsdk.ImageAnalysisFeature.CAPTION - | visionsdk.ImageAnalysisFeature.DENSE_CAPTIONS - | visionsdk.ImageAnalysisFeature.OBJECTS - | visionsdk.ImageAnalysisFeature.TEXT - | visionsdk.ImageAnalysisFeature.TAGS + visual_features = ["caption", + "denseCaptions", + "objects", + "tags", + "read"] + API_VISION_ENDPOINT = ( + f"https://{azure_ai_location}.{azure_ai_endpoint[8:]}vision/v4.0/analyze?visualFeatures={','.join(visual_features)}" ) else: GPU_REGION = False - analysis_options.features = ( - visionsdk.ImageAnalysisFeature.OBJECTS - | visionsdk.ImageAnalysisFeature.TEXT - | visionsdk.ImageAnalysisFeature.TAGS - ) - -analysis_options.model_version = "latest" - + visual_features = ["objects", + "tags", + "read"] + API_VISION_ENDPOINT = ( + f"https://{azure_ai_location}.{azure_ai_endpoint[8:]}vision/v3.2/analyze?visualFeatures={','.join(visual_features)}" +) FUNCTION_NAME = "ImageEnrichment" @@ -178,67 +173,69 @@ def main(msg: func.QueueMessage) -> None: ) # Run the image through the Computer Vision service - file_name, file_extension, file_directory = utilities.get_filename_and_extension(blob_path) + file_name, file_extension, file_directory = utilities.get_filename_and_extension( + blob_path) blob_path_plus_sas = utilities.get_blob_and_sas(blob_path) - vision_source = visionsdk.VisionSource(url=blob_path_plus_sas) - image_analyzer = visionsdk.ImageAnalyzer( - vision_service_options, vision_source, analysis_options - ) - result = image_analyzer.analyze() + data = {"url": blob_path_plus_sas} - text_image_summary = "" - index_content = "" - complete_ocr_text = None + response = requests.post(API_VISION_ENDPOINT, + headers=translator_api_headers, + json=data) + if response.status_code == 200: + print(response.json()) + + text_image_summary = "" + index_content = "" + complete_ocr_text = None - if result.reason == visionsdk.ImageAnalysisResultReason.ANALYZED: if GPU_REGION: - if result.caption is not None: + if response.json()["captionResult"] is not None: text_image_summary += "Caption:\n" text_image_summary += "\t'{}', Confidence {:.4f}\n".format( - result.caption.content, result.caption.confidence + response.json()["captionResult"]["text"], response.json()["captionResult"]["confidence"] ) - index_content += "Caption: {}\n ".format(result.caption.content) + index_content += "Caption: {}\n ".format( + response.json()["captionResult"]["text"]) - if result.dense_captions is not None: + if response.json()["denseCaptionsResult"] is not None: text_image_summary += "Dense Captions:\n" index_content += "DeepCaptions: " - for caption in result.dense_captions: + for caption in response.json()["denseCaptionsResult"]["values"]: text_image_summary += "\t'{}', Confidence: {:.4f}\n".format( - caption.content, caption.confidence + caption["text"], caption["confidence"] ) - index_content += "{}\n ".format(caption.content) + index_content += "{}\n ".format(caption.text) - if result.objects is not None: + if response.json()["objectsResult"] is not None: text_image_summary += "Objects:\n" index_content += "Descriptions: " - for object_detection in result.objects: + for object_detection in response.json()["objectsResult"]["values"]: text_image_summary += "\t'{}', Confidence: {:.4f}\n".format( - object_detection.name, object_detection.confidence + object_detection["tags"][0]["name"], object_detection["tags"][0]["confidence"] ) - index_content += "{}\n ".format(object_detection.name) + index_content += "{}\n ".format( + object_detection["tags"][0]["name"]) - if result.tags is not None: + if response.json()["tagsResult"] is not None: text_image_summary += "Tags:\n" - for tag in result.tags: + for tag in response.json()["tagsResult"]["values"]: text_image_summary += "\t'{}', Confidence {:.4f}\n".format( - tag.name, tag.confidence + tag["name"], tag["confidence"] ) index_content += "{}\n ".format(tag.name) - if result.text is not None: + if response.json()["readResult"] is not None: text_image_summary += "Raw OCR Text:\n" complete_ocr_text = "" - for line in result.text.lines: - complete_ocr_text += "{}\n".format(line.content) + for line in response.json()["readResult"]["blocks"][0]["lines"]: + complete_ocr_text += "{}\n".format(line["text"]) text_image_summary += complete_ocr_text else: - error_details = visionsdk.ImageAnalysisErrorDetails.from_result(result) - statusLog.upsert_document( blob_path, - f"{FUNCTION_NAME} - Image analysis failed: {error_details.error_code} {error_details.error_code} {error_details.message}", + f"{FUNCTION_NAME} - Image analysis failed: {response.status_code} - {response.text}", StatusClassification.ERROR, State.ERROR, ) @@ -247,7 +244,8 @@ def main(msg: func.QueueMessage) -> None: # Detect language output_text = "" - detected_language, detection_confidence = detect_language(complete_ocr_text) + detected_language, detection_confidence = detect_language( + complete_ocr_text) text_image_summary += f"Raw OCR Text - Detected language: {detected_language}, Confidence: {detection_confidence}\n" if detected_language != targetTranslationLanguage: @@ -302,12 +300,15 @@ def main(msg: func.QueueMessage) -> None: ) try: - file_name, file_extension, file_directory = utilities.get_filename_and_extension(blob_path) - + file_name, file_extension, file_directory = utilities.get_filename_and_extension( + blob_path) + # Get the tags from metadata on the blob path = file_directory + file_name + file_extension - blob_service_client = BlobServiceClient(account_url=azure_blob_storage_endpoint, credential=azure_credential) - blob_client = blob_service_client.get_blob_client(container=azure_blob_drop_storage_container, blob=path) + blob_service_client = BlobServiceClient( + account_url=azure_blob_storage_endpoint, credential=azure_credential) + blob_client = blob_service_client.get_blob_client( + container=azure_blob_drop_storage_container, blob=path) blob_properties = blob_client.get_blob_properties() tags = blob_properties.metadata.get("tags") if tags is not None: @@ -321,9 +322,11 @@ def main(msg: func.QueueMessage) -> None: statusLog.update_document_tags(blob_path, tags_list) # Only one chunk per image currently. - chunk_file=utilities.build_chunk_filepath(file_directory, file_name, file_extension, '0') + chunk_file = utilities.build_chunk_filepath( + file_directory, file_name, file_extension, '0') - index_section(index_content, file_name, file_directory[:-1], statusLog.encode_document_id(chunk_file), chunk_file, blob_path, blob_uri, tags_list) + index_section(index_content, file_name, file_directory[:-1], statusLog.encode_document_id( + chunk_file), chunk_file, blob_path, blob_uri, tags_list) statusLog.upsert_document( blob_path, @@ -339,7 +342,6 @@ def main(msg: func.QueueMessage) -> None: State.ERROR, ) - statusLog.save_document(blob_path) @@ -364,7 +366,7 @@ def index_section(index_content, file_name, file_directory, chunk_id, chunk_file batch.append(index_chunk) search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE_ENDPOINT, - index_name=AZURE_SEARCH_INDEX, - credential=azure_credential) + index_name=AZURE_SEARCH_INDEX, + credential=azure_credential) search_client.upload_documents(documents=batch) diff --git a/functions/requirements.txt b/functions/requirements.txt index e0de17354..149ae81e9 100644 --- a/functions/requirements.txt +++ b/functions/requirements.txt @@ -14,7 +14,7 @@ azure-cosmos == 4.7.0 azure-storage-queue == 12.6.0 nltk == 3.8.1 tenacity == 8.2.3 -azure-ai-vision == 0.15.1b1 +azure-ai-vision-imageanalysis == 1.0.0b2 unstructured[csv,doc,docx,email,html,md,msg,ppt,pptx,text,xlsx,xml] == 0.12.5 pyoo == 1.4 azure-search-documents == 11.4.0b11 diff --git a/infra/core/ai/docintelligence/outputs.tf b/infra/core/ai/docintelligence/outputs.tf index 17b327e80..8244d24d5 100644 --- a/infra/core/ai/docintelligence/outputs.tf +++ b/infra/core/ai/docintelligence/outputs.tf @@ -15,5 +15,5 @@ output "formPrivateEndpoint" { } output "docIntelligenceIdentity" { - value = azurerm_cognitive_account.docIntelligenceAccount.identity + value = azurerm_cognitive_account.docIntelligenceAccount.identity[0].principal_id } \ No newline at end of file diff --git a/infra/main.tf b/infra/main.tf index 46eedc70b..e8e6b4e2a 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -753,12 +753,12 @@ module "functionApp_StorageQueueDataContributor" { resourceGroupId = azurerm_resource_group.rg.id } -module "webApp_StorageBlobDataReader" { +module "webApp_StorageBlobDataContributor" { source = "./core/security/role" scope = azurerm_resource_group.rg.id principalId = module.webapp.identityPrincipalId - roleDefinitionId = local.azure_roles.StorageBlobDataReader + roleDefinitionId = local.azure_roles.StorageBlobDataContributor principalType = "ServicePrincipal" subscriptionId = data.azurerm_client_config.current.subscription_id resourceGroupId = azurerm_resource_group.rg.id diff --git a/infra/outputs.tf b/infra/outputs.tf index a4d4e7353..72b29a3b5 100644 --- a/infra/outputs.tf +++ b/infra/outputs.tf @@ -230,8 +230,4 @@ output "FUNC_STORAGE_CONNECTION_STRING__blobServiceUri" { output "AZURE_OPENAI_AUTHORITY_HOST" { value = var.azure_openai_authority_host -} - -output "AZURE_QUEUE_STORAGE_ENDPOINT" { - value = module.storage.primary_queue_endpoint } \ No newline at end of file From f1ec3fa840a2e09c6437328d4e4d7b998df302c3 Mon Sep 17 00:00:00 2001 From: bjakems <165402330+bjakems@users.noreply.github.com> Date: Wed, 17 Jul 2024 18:57:52 +0100 Subject: [PATCH 12/33] resolve issue with sas token scope --- app/backend/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/backend/app.py b/app/backend/app.py index 58a09626b..ca3792ca4 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -352,7 +352,7 @@ async def get_blob_client_url(): expiry=datetime.utcnow() + timedelta(hours=2) ) - return {"url": f"{blob_client.url}upload?{sas_token}&restype=container"} + return {"url": f"{blob_client.url}upload?{sas_token}"} @app.post("/getalluploadstatus") async def get_all_upload_status(request: Request): From 63c9b4504b7e6e4377d49626881d2214529be967 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:30:54 +0100 Subject: [PATCH 13/33] Updated to prepare for new Vision SDK an remove AZURE-AI-KEY kv secret --- functions/ImageEnrichment/__init__.py | 95 ++++++++++++------------ infra/core/ai/cogServices/cogServices.tf | 12 --- infra/core/host/functions/functions.tf | 24 +++--- scripts/inf-import-state.sh | 4 - scripts/json-to-env.function.debug.sh | 2 +- scripts/json-to-env.webapp.debug.sh | 4 +- 6 files changed, 65 insertions(+), 76 deletions(-) diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 883857640..15eda4b8e 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -4,6 +4,8 @@ import azure.functions as func import requests +from azure.ai.vision.imageanalysis import ImageAnalysisClient +from azure.ai.vision.imageanalysis.models import VisualFeatures from azure.storage.blob import BlobServiceClient from azure.identity import ManagedIdentityCredential, DefaultAzureCredential, get_bearer_token_provider, AzureAuthorityHosts from shared_code.status_log import State, StatusClassification, StatusLog @@ -11,7 +13,6 @@ from azure.search.documents import SearchClient from datetime import datetime - azure_blob_storage_account = os.environ["BLOB_STORAGE_ACCOUNT"] azure_blob_drop_storage_container = os.environ[ "BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME" @@ -73,11 +74,6 @@ "Ocp-Apim-Subscription-Region": azure_ai_location, } -vison_api_headers = { - "Auhorization": f"Bearer {token_provider()}", - "Content-type": "application/json", -} - # Note that "cation" and "denseCaptions" are only supported in Azure GPU regions (East US, France Central, # Korea Central, North Europe, Southeast Asia, West Europe, West US). Remove "caption" and "denseCaptions" # from the list below if your Computer Vision key is not from one of those regions. @@ -92,22 +88,21 @@ "westus", ]: GPU_REGION = True - visual_features = ["caption", - "denseCaptions", - "objects", - "tags", - "read"] - API_VISION_ENDPOINT = ( - f"https://{azure_ai_location}.{azure_ai_endpoint[8:]}vision/v4.0/analyze?visualFeatures={','.join(visual_features)}" - ) + visual_features = [VisualFeatures.CAPTION, + VisualFeatures.DENSE_CAPTIONS, + VisualFeatures.OBJECTS, + VisualFeatures.TAGS, + VisualFeatures.READ] else: GPU_REGION = False - visual_features = ["objects", - "tags", - "read"] - API_VISION_ENDPOINT = ( - f"https://{azure_ai_location}.{azure_ai_endpoint[8:]}vision/v3.2/analyze?visualFeatures={','.join(visual_features)}" -) + visual_features = [VisualFeatures.OBJECTS, + VisualFeatures.TAGS, + VisualFeatures.READ] + +vision_client = ImageAnalysisClient( + endpoint=azure_ai_endpoint, + credential=azure_credential + ) FUNCTION_NAME = "ImageEnrichment" @@ -175,70 +170,76 @@ def main(msg: func.QueueMessage) -> None: # Run the image through the Computer Vision service file_name, file_extension, file_directory = utilities.get_filename_and_extension( blob_path) - blob_path_plus_sas = utilities.get_blob_and_sas(blob_path) - - data = {"url": blob_path_plus_sas} - - response = requests.post(API_VISION_ENDPOINT, - headers=translator_api_headers, - json=data) - if response.status_code == 200: - print(response.json()) + path = blob_path.split("/", 1)[1] + + try: + blob_service_client = BlobServiceClient(account_url=azure_blob_storage_endpoint, + credential=azure_credential) + blob_client = blob_service_client.get_blob_client(container=azure_blob_drop_storage_container, + blob=path) + image_data = blob_client.download_blob().readall() + response = vision_client.analyze(image_data=image_data, + visual_features=visual_features, + gender_neutral_caption=True) + + print(response) text_image_summary = "" index_content = "" complete_ocr_text = None if GPU_REGION: - if response.json()["captionResult"] is not None: + if response.caption is not None: text_image_summary += "Caption:\n" text_image_summary += "\t'{}', Confidence {:.4f}\n".format( - response.json()["captionResult"]["text"], response.json()["captionResult"]["confidence"] + response.caption.text, response.caption.confidence ) index_content += "Caption: {}\n ".format( - response.json()["captionResult"]["text"]) + response.caption.text) - if response.json()["denseCaptionsResult"] is not None: + if response.dense_captions is not None: text_image_summary += "Dense Captions:\n" index_content += "DeepCaptions: " - for caption in response.json()["denseCaptionsResult"]["values"]: + for caption in response.dense_captions.values: text_image_summary += "\t'{}', Confidence: {:.4f}\n".format( - caption["text"], caption["confidence"] + caption.text, caption.confidence ) index_content += "{}\n ".format(caption.text) - if response.json()["objectsResult"] is not None: + if response.objects is not None: text_image_summary += "Objects:\n" index_content += "Descriptions: " - for object_detection in response.json()["objectsResult"]["values"]: + for object_detection in response.objects.values: text_image_summary += "\t'{}', Confidence: {:.4f}\n".format( - object_detection["tags"][0]["name"], object_detection["tags"][0]["confidence"] + object_detection.tags[0].name, object_detection.tags[0].confidence ) index_content += "{}\n ".format( - object_detection["tags"][0]["name"]) + object_detection.tags[0].name) - if response.json()["tagsResult"] is not None: + if response.tags is not None: text_image_summary += "Tags:\n" - for tag in response.json()["tagsResult"]["values"]: + for tag in response.tags.values: text_image_summary += "\t'{}', Confidence {:.4f}\n".format( - tag["name"], tag["confidence"] + tag.name, tag.confidence ) index_content += "{}\n ".format(tag.name) - if response.json()["readResult"] is not None: + if response.read is not None: text_image_summary += "Raw OCR Text:\n" complete_ocr_text = "" - for line in response.json()["readResult"]["blocks"][0]["lines"]: - complete_ocr_text += "{}\n".format(line["text"]) + for line in response.read.blocks[0].lines: + complete_ocr_text += "{}\n".format(line.text) text_image_summary += complete_ocr_text - else: + except Exception as ex: + logging.error(f"{FUNCTION_NAME} - Image analysis failed for {blob_path}: {str(ex)}") statusLog.upsert_document( blob_path, - f"{FUNCTION_NAME} - Image analysis failed: {response.status_code} - {response.text}", + f"{FUNCTION_NAME} - Image analysis failed: {str(ex)}", StatusClassification.ERROR, State.ERROR, ) + raise ex if complete_ocr_text not in [None, ""]: # Detect language diff --git a/infra/core/ai/cogServices/cogServices.tf b/infra/core/ai/cogServices/cogServices.tf index 413918750..083913caf 100644 --- a/infra/core/ai/cogServices/cogServices.tf +++ b/infra/core/ai/cogServices/cogServices.tf @@ -10,18 +10,6 @@ resource "azurerm_cognitive_account" "cognitiveService" { local_auth_enabled = var.is_secure_mode ? false : true } -module "search_service_key" { - source = "../../security/keyvaultSecret" - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - key_vault_name = var.key_vault_name - resourceGroupName = var.resourceGroupName - secret_name = "AZURE-AI-KEY" - secret_value = azurerm_cognitive_account.cognitiveService.primary_access_key - alias = "aisvckey" - tags = var.tags - kv_secret_expiration = var.kv_secret_expiration -} - data "azurerm_subnet" "subnet" { count = var.is_secure_mode ? 1 : 0 name = var.subnet_name diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 2ef68bbf8..1a1c21181 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -93,7 +93,7 @@ resource "azurerm_linux_function_app" "function_app" { tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true virtual_network_subnet_id = var.is_secure_mode ? var.subnetIntegration_id : null - + content_share_force_disabled = true site_config { application_stack { @@ -112,6 +112,7 @@ resource "azurerm_linux_function_app" "function_app" { cors { allowed_origins = concat([var.azure_portal_domain, "https://ms.portal.azure.com"], var.allowedOrigins) } + vnet_route_all_enabled = var.is_secure_mode ? true : false } identity { @@ -119,21 +120,30 @@ resource "azurerm_linux_function_app" "function_app" { } app_settings = { - WEBSITE_VNET_ROUTE_ALL = "1" + # Network realated settings for secure mode + WEBSITE_VNET_ROUTE_ALL = var.is_secure_mode ? "1" : "0" WEBSITE_CONTENTOVERVNET = var.is_secure_mode ? "1" : "0" + WEBSITE_PULL_IMAGE_OVER_VNET = var.is_secure_mode ? "true" : "false" + WEBSITE_SKIP_CONTENTSHARE_VALIDATION = var.is_secure_mode ? "1" : "0" + SCM_DO_BUILD_DURING_DEPLOYMENT = "false" ENABLE_ORYX_BUILD = "false" + #Set all connections to use Managed Identity instead of connection strings AzureWebJobsStorage = "" AzureWebJobsStorage__accountName = var.blobStorageAccountName AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" - WEBSITE_CONTENTAZUREFILECONNECTIONSTRING = "DefaultEndpointsProtocol=https;AccountName=${var.blobStorageAccountName};EndpointSuffix=${var.endpointSuffix};AccountKey=${data.azurerm_storage_account.existing_sa.primary_access_key}" - WEBSITE_CONTENTSHARE = "funcfileshare" + STORAGE_CONNECTION_STRING = "" + STORAGE_CONNECTION_STRING__accountName = var.blobStorageAccountName + STORAGE_CONNECTION_STRING__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" + STORAGE_CONNECTION_STRING__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" + FUNCTIONS_WORKER_RUNTIME = var.runtime FUNCTIONS_EXTENSION_VERSION = "~4" WEBSITE_NODE_DEFAULT_VERSION = "~14" APPLICATIONINSIGHTS_CONNECTION_STRING = var.appInsightsConnectionString APPINSIGHTS_INSTRUMENTATIONKEY = var.appInsightsInstrumentationKey + # Environment variables used by custom Python code BLOB_STORAGE_ACCOUNT = var.blobStorageAccountName BLOB_STORAGE_ACCOUNT_ENDPOINT = var.blobStorageAccountEndpoint BLOB_STORAGE_ACCOUNT_UPLOAD_CONTAINER_NAME = var.blobStorageAccountUploadContainerName @@ -161,7 +171,6 @@ resource "azurerm_linux_function_app" "function_app" { SUBMIT_REQUEUE_HIDE_SECONDS = var.submitRequeueHideSeconds POLLING_BACKOFF = var.pollingBackoff MAX_READ_ATTEMPTS = var.maxReadAttempts - AZURE_AI_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-AI-KEY)" AZURE_AI_ENDPOINT = var.enrichmentEndpoint ENRICHMENT_NAME = var.enrichmentName AZURE_AI_LOCATION = var.enrichmentLocation @@ -173,11 +182,6 @@ resource "azurerm_linux_function_app" "function_app" { COSMOSDB_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" AZURE_SEARCH_SERVICE_ENDPOINT = var.azureSearchServiceEndpoint AZURE_SEARCH_INDEX = var.azureSearchIndex - WEBSITE_PULL_IMAGE_OVER_VNET = var.is_secure_mode ? "true" : "false" - STORAGE_CONNECTION_STRING = "" - STORAGE_CONNECTION_STRING__accountName = var.blobStorageAccountName - STORAGE_CONNECTION_STRING__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" - STORAGE_CONNECTION_STRING__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_credential_domain } } diff --git a/scripts/inf-import-state.sh b/scripts/inf-import-state.sh index 80368c877..140e8f937 100755 --- a/scripts/inf-import-state.sh +++ b/scripts/inf-import-state.sh @@ -365,10 +365,6 @@ name="infoasst-enrichment-cog-$random_text" providers="/providers/Microsoft.CognitiveServices/accounts/$name" module_path="module.cognitiveServices.azurerm_cognitive_account.cognitiveService" import_resource_if_needed "$module_path" "$resourceId$providers" -secret_id=$(get_secret "AZURE-AI-KEY") -module_path="module.cognitiveServices.azurerm_key_vault_secret.search_service_key" -import_resource_if_needed "$module_path" "$secret_id" - # Key Vault echo diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index 084386356..980e23ab7 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -20,7 +20,7 @@ secrets="{" keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("COSMOSDB-KEY" "AZURE-AI-KEY") +secretNames=("COSMOSDB-KEY") # Retrieve and export each secret for secretName in "${secretNames[@]}"; do diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index 37fc4cd0b..f70457eb2 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -207,9 +207,9 @@ keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets if [ -n "${SECURE_MODE}" ]; then - secretNames=("COSMOSDB-KEY" "AZURE-AI-KEY") + secretNames=("COSMOSDB-KEY") else - secretNames=("COSMOSDB-KEY" "BINGSEARCH-KEY" "AZURE-AI-KEY") + secretNames=("COSMOSDB-KEY" "BINGSEARCH-KEY") fi From 0a50a9ec0317802c80c6d977fe5cd3014f619af0 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:47:56 +0100 Subject: [PATCH 14/33] Change CosmosDB to use Entra ID Auth instead of shared keys --- app/backend/app.py | 7 ++-- app/enrichment/app.py | 3 +- docs/function_debug.md | 1 - docs/status_log.md | 2 +- functions/FileDeletion/__init__.py | 11 +++---- functions/FileFormRecPollingPDF/__init__.py | 3 +- .../FileFormRecSubmissionPDF/__init__.py | 3 +- functions/FileLayoutParsingOther/__init__.py | 3 +- functions/FileUploadedFunc/__init__.py | 3 +- functions/ImageEnrichment/__init__.py | 3 +- functions/TextEnrichment/__init__.py | 10 +++--- functions/shared_code/status_log.py | 6 ++-- infra/core/db/cosmosdb.tf | 12 ------- infra/core/db/outputs.tf | 8 +++++ .../core/host/enrichmentapp/enrichmentapp.tf | 1 - infra/core/host/functions/functions.tf | 1 - infra/core/host/webapp/webapp.tf | 1 - infra/main.tf | 32 +++++++++++++++++++ scripts/build.sh | 8 ++--- scripts/extract-content.py | 3 +- scripts/inf-import-state.sh | 3 -- scripts/json-to-env.function.debug.sh | 23 ++----------- scripts/json-to-env.sh | 21 ------------ scripts/json-to-env.webapp.debug.sh | 4 +-- scripts/merge-databases.py | 4 +-- 25 files changed, 75 insertions(+), 101 deletions(-) diff --git a/app/backend/app.py b/app/backend/app.py index ca3792ca4..168033ae8 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -72,7 +72,6 @@ "KB_FIELDS_SOURCEFILE": "file_uri", "KB_FIELDS_CHUNKFILE": "chunk_file", "COSMOSDB_URL": None, - "COSMOSDB_KEY": None, "COSMOSDB_LOG_DATABASE_NAME": "statusdb", "COSMOSDB_LOG_CONTAINER_NAME": "statuscontainer", "QUERY_TERM_LANGUAGE": "English", @@ -143,7 +142,7 @@ class StatusResponse(pydantic.BaseModel): # Setup StatusLog to allow access to CosmosDB for logging statusLog = StatusLog( ENV["COSMOSDB_URL"], - ENV["COSMOSDB_KEY"], + azure_credential, ENV["COSMOSDB_LOG_DATABASE_NAME"], ENV["COSMOSDB_LOG_CONTAINER_NAME"] ) @@ -380,7 +379,7 @@ async def get_all_upload_status(request: Request): # retrieve tags for each file # Initialize an empty list to hold the tags items = [] - cosmos_client = CosmosClient(url=statusLog._url, credential=statusLog._key, consistency_level='Session') + cosmos_client = CosmosClient(url=statusLog._url, credential=azure_credential, consistency_level='Session') database = cosmos_client.get_database_client(statusLog._database_name) container = database.get_container_client(statusLog._container_name) query_string = "SELECT DISTINCT VALUE t FROM c JOIN t IN c.tags" @@ -518,7 +517,7 @@ async def get_tags(request: Request): try: # Initialize an empty list to hold the tags items = [] - cosmos_client = CosmosClient(url=statusLog._url, credential=statusLog._key, consistency_level='Session') + cosmos_client = CosmosClient(url=statusLog._url, credential=azure_credential, consistency_level='Session') database = cosmos_client.get_database_client(statusLog._database_name) container = database.get_container_client(statusLog._container_name) query_string = "SELECT DISTINCT VALUE t FROM c JOIN t IN c.tags" diff --git a/app/enrichment/app.py b/app/enrichment/app.py index 7412cb4b1..70f1706aa 100644 --- a/app/enrichment/app.py +++ b/app/enrichment/app.py @@ -41,7 +41,6 @@ "AZURE_QUEUE_STORAGE_ENDPOINT": None, "AZURE_BLOB_STORAGE_UPLOAD_CONTAINER": None, "COSMOSDB_URL": None, - "COSMOSDB_KEY": None, "COSMOSDB_LOG_DATABASE_NAME": None, "COSMOSDB_LOG_CONTAINER_NAME": None, "MAX_EMBEDDING_REQUEUE_COUNT": 5, @@ -137,7 +136,7 @@ def encode(self, texts) -> None: credential=azure_credential ) -statusLog = StatusLog(ENV["COSMOSDB_URL"], ENV["COSMOSDB_KEY"], ENV["COSMOSDB_LOG_DATABASE_NAME"], ENV["COSMOSDB_LOG_CONTAINER_NAME"]) +statusLog = StatusLog(ENV["COSMOSDB_URL"], azure_credential, ENV["COSMOSDB_LOG_DATABASE_NAME"], ENV["COSMOSDB_LOG_CONTAINER_NAME"]) # === API Setup === start_time = datetime.now() diff --git a/docs/function_debug.md b/docs/function_debug.md index 253cbed68..df8336b80 100644 --- a/docs/function_debug.md +++ b/docs/function_debug.md @@ -20,7 +20,6 @@ Next you will need to create local configuration values that are used by the fun "BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME": "logs", "COSMOSDB_LOG_CONTAINER_NAME": "statuscontainer", "COSMOSDB_LOG_DATABASE_NAME": "statusdb", - "COSMOSDB_KEY": "", "COSMOSDB_URL": "", "AZURE_FORM_RECOGNIZER_ENDPOINT": "", "BLOB_STORAGE_ACCOUNT": "", diff --git a/docs/status_log.md b/docs/status_log.md index 4b43924dd..d384c0ff7 100644 --- a/docs/status_log.md +++ b/docs/status_log.md @@ -10,7 +10,7 @@ Currently the status logger provides a class, StatusLog, with the following func - **encode_document_id** - this function is used to generate the id from the file name by the upsert_document function initially. It can also be called to retrieve the encoded id of a file if you pass in the file name. The id is used as the partition key. - **read_documents** - This function returns status documents from Cosmos DB for you to use. You can specify optional query parameters, such as document id (the document path) or an integer representing how many minutes from now the processing should have started, or if you wish to receive verbose or concise details. -Finally you will need to supply 4 properties to the class before you can call the above functions. These are COSMOSDB_URL, COSMOSDB_KEY, COSMOSDB_LOG_DATABASE_NAME and COSMOSDB_LOG_CONTAINER_NAME. The resulting json includes verbose status updates but also a snapshot status for the end user UI, specifically the state, state_description and state_timestamp. These values are just select high level state snapshots, including 'Processing', 'Error' and 'Complete'. +Finally you will need to supply 4 properties to the class before you can call the above functions. These are COSMOSDB_URL, azure_credential, COSMOSDB_LOG_DATABASE_NAME and COSMOSDB_LOG_CONTAINER_NAME. The resulting json includes verbose status updates but also a snapshot status for the end user UI, specifically the state, state_description and state_timestamp. These values are just select high level state snapshots, including 'Processing', 'Error' and 'Complete'. ````json { diff --git a/functions/FileDeletion/__init__.py b/functions/FileDeletion/__init__.py index d3da95ec7..6c654cea5 100644 --- a/functions/FileDeletion/__init__.py +++ b/functions/FileDeletion/__init__.py @@ -19,18 +19,12 @@ azure_search_service_endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] azure_search_index = os.environ["AZURE_SEARCH_INDEX"] cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] local_debug = os.environ["LOCAL_DEBUG"] or "false" azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] azure_openai_authority_host = os.environ["AZURE_OPENAI_AUTHORITY_HOST"] -status_log = StatusLog(cosmosdb_url, - cosmosdb_key, - cosmosdb_log_database_name, - cosmosdb_log_container_name) - if azure_openai_authority_host == "AzureUSGovernment": AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT else: @@ -46,6 +40,11 @@ else: azure_credential = ManagedIdentityCredential(authority=AUTHORITY) +status_log = StatusLog(cosmosdb_url, + azure_credential, + cosmosdb_log_database_name, + cosmosdb_log_container_name) + def chunks(data, size): '''max number of blobs to delete in one request is 256, so this breaks chunks the dictionary''' diff --git a/functions/FileFormRecPollingPDF/__init__.py b/functions/FileFormRecPollingPDF/__init__.py index 4729ca0ed..43b30ff91 100644 --- a/functions/FileFormRecPollingPDF/__init__.py +++ b/functions/FileFormRecPollingPDF/__init__.py @@ -31,7 +31,6 @@ def string_to_bool(s): # ranges of pages you want to get in the result. For a range of pages, use a hyphen, like pages="1-3, 5-6". # Separate each page number or range with a comma. cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] non_pdf_submit_queue = os.environ["NON_PDF_SUBMIT_QUEUE"] @@ -74,7 +73,7 @@ def string_to_bool(s): def main(msg: func.QueueMessage) -> None: try: - statusLog = StatusLog(cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name) + statusLog = StatusLog(cosmosdb_url, azure_credential, cosmosdb_log_database_name, cosmosdb_log_container_name) # Receive message from the queue message_body = msg.get_body().decode('utf-8') message_json = json.loads(message_body) diff --git a/functions/FileFormRecSubmissionPDF/__init__.py b/functions/FileFormRecSubmissionPDF/__init__.py index ab6890586..f776ec731 100644 --- a/functions/FileFormRecSubmissionPDF/__init__.py +++ b/functions/FileFormRecSubmissionPDF/__init__.py @@ -22,7 +22,6 @@ "BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME" ] cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] pdf_polling_queue = os.environ["PDF_POLLING_QUEUE"] @@ -74,7 +73,7 @@ def main(msg: func.QueueMessage) -> None: blob_path = message_json["blob_name"] try: statusLog = StatusLog( - cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name + cosmosdb_url, azure_credential, cosmosdb_log_database_name, cosmosdb_log_container_name ) # Receive message from the queue diff --git a/functions/FileLayoutParsingOther/__init__.py b/functions/FileLayoutParsingOther/__init__.py index 4403e6596..8e35dfd99 100644 --- a/functions/FileLayoutParsingOther/__init__.py +++ b/functions/FileLayoutParsingOther/__init__.py @@ -22,7 +22,6 @@ azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_log_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_LOG_CONTAINER_NAME"] cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] non_pdf_submit_queue = os.environ["NON_PDF_SUBMIT_QUEUE"] @@ -132,7 +131,7 @@ def PartitionFile(file_extension: str, file_url: str): def main(msg: func.QueueMessage) -> None: try: - statusLog = StatusLog(cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name) + statusLog = StatusLog(cosmosdb_url, azure_credential, cosmosdb_log_database_name, cosmosdb_log_container_name) logging.info('Python queue trigger function processed a queue item: %s', msg.get_body().decode('utf-8')) diff --git a/functions/FileUploadedFunc/__init__.py b/functions/FileUploadedFunc/__init__.py index dbfbaba81..fdb3d9f34 100644 --- a/functions/FileUploadedFunc/__init__.py +++ b/functions/FileUploadedFunc/__init__.py @@ -17,7 +17,6 @@ cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] non_pdf_submit_queue = os.environ["NON_PDF_SUBMIT_QUEUE"] @@ -59,7 +58,7 @@ azure_blob_storage_endpoint=azure_blob_endpoint, credential=azure_credential ) -statusLog = StatusLog(cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name) +statusLog = StatusLog(cosmosdb_url, azure_credential, cosmosdb_log_database_name, cosmosdb_log_container_name) def get_tags_and_upload_to_cosmos(blob_service_client, blob_path): """ Gets the tags from the blob metadata and uploads them to cosmos db""" diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 15eda4b8e..024783909 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -33,7 +33,6 @@ # Cosmos DB cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] @@ -153,7 +152,7 @@ def main(msg: func.QueueMessage) -> None: blob_uri = message_json["blob_uri"] try: statusLog = StatusLog( - cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name + cosmosdb_url, azure_credential, cosmosdb_log_database_name, cosmosdb_log_container_name ) logging.info( "Python queue trigger function processed a queue item: %s", diff --git a/functions/TextEnrichment/__init__.py b/functions/TextEnrichment/__init__.py index 046bca3be..7bacaece0 100644 --- a/functions/TextEnrichment/__init__.py +++ b/functions/TextEnrichment/__init__.py @@ -25,7 +25,6 @@ azure_blob_content_storage_container = os.environ["BLOB_STORAGE_ACCOUNT_OUTPUT_CONTAINER_NAME"] azure_blob_storage_endpoint = os.environ["BLOB_STORAGE_ACCOUNT_ENDPOINT"] cosmosdb_url = os.environ["COSMOSDB_URL"] -cosmosdb_key = os.environ["COSMOSDB_KEY"] cosmosdb_log_database_name = os.environ["COSMOSDB_LOG_DATABASE_NAME"] cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] text_enrichment_queue = os.environ["TEXT_ENRICHMENT_QUEUE"] @@ -44,10 +43,6 @@ MAX_CHARS_FOR_DETECTION = 1000 -statusLog = StatusLog( - cosmosdb_url, cosmosdb_key, cosmosdb_log_database_name, cosmosdb_log_container_name -) - if azure_openai_authority_host == "AzureUSGovernment": AUTHORITY = AzureAuthorityHosts.AZURE_GOVERNMENT else: @@ -71,6 +66,11 @@ azure_blob_content_storage_container, azure_credential, ) + +statusLog = StatusLog( + cosmosdb_url, azure_credential, cosmosdb_log_database_name, cosmosdb_log_container_name +) + def main(msg: func.QueueMessage) -> None: '''This function is triggered by a message in the text-enrichment-queue. It will first determine the language, and if this differs from diff --git a/functions/shared_code/status_log.py b/functions/shared_code/status_log.py index bb2d37423..f83aa5466 100644 --- a/functions/shared_code/status_log.py +++ b/functions/shared_code/status_log.py @@ -38,13 +38,13 @@ class StatusQueryLevel(Enum): class StatusLog: """ Class for logging status of various processes to Cosmos DB""" - def __init__(self, url, key, database_name, container_name): + def __init__(self, url, azure_credential, database_name, container_name): """ Constructor function """ self._url = url - self._key = key + self.azure_credential = azure_credential self._database_name = database_name self._container_name = container_name - self.cosmos_client = CosmosClient(url=self._url, credential=self._key, consistency_level='Session') + self.cosmos_client = CosmosClient(url=self._url, credential=self.azure_credential, consistency_level='Session') self._log_document = {} # Select a database (will create it if it doesn't exist) diff --git a/infra/core/db/cosmosdb.tf b/infra/core/db/cosmosdb.tf index 6d1f66f48..ed85cda4b 100644 --- a/infra/core/db/cosmosdb.tf +++ b/infra/core/db/cosmosdb.tf @@ -68,18 +68,6 @@ resource "azurerm_cosmosdb_sql_container" "log_container" { partition_key_path = "/file_name" } -module "cosmos_db_key" { - source = "../security/keyvaultSecret" - resourceGroupName = var.resourceGroupName - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - key_vault_name = var.key_vault_name - secret_name = "COSMOSDB-KEY" - secret_value = azurerm_cosmosdb_account.cosmosdb_account.primary_key - alias = "cosmoskey" - tags = var.tags - kv_secret_expiration = var.kv_secret_expiration -} - data "azurerm_subnet" "subnet" { count = var.is_secure_mode ? 1 : 0 name = var.subnet_name diff --git a/infra/core/db/outputs.tf b/infra/core/db/outputs.tf index 08c26613b..4a421f562 100644 --- a/infra/core/db/outputs.tf +++ b/infra/core/db/outputs.tf @@ -12,4 +12,12 @@ output "CosmosDBLogContainerName" { output "privateEndpointId" { value = var.is_secure_mode ? azurerm_private_endpoint.cosmosPrivateEndpoint[0].id : null +} + +output "id" { + value = azurerm_cosmosdb_account.cosmosdb_account.id +} + +output "name" { + value = azurerm_cosmosdb_account.cosmosdb_account.name } \ No newline at end of file diff --git a/infra/core/host/enrichmentapp/enrichmentapp.tf b/infra/core/host/enrichmentapp/enrichmentapp.tf index 8713fc084..3a68ee418 100644 --- a/infra/core/host/enrichmentapp/enrichmentapp.tf +++ b/infra/core/host/enrichmentapp/enrichmentapp.tf @@ -105,7 +105,6 @@ resource "azurerm_linux_web_app" "enrichmentapp" { "SCM_DO_BUILD_DURING_DEPLOYMENT" = lower(tostring(var.scmDoBuildDuringDeployment)) "ENABLE_ORYX_BUILD" = tostring(var.enableOryxBuild) "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.applicationInsightsConnectionString - "COSMOSDB_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" "KEY_EXPIRATION_DATE" = timeadd(timestamp(), "4320h") # Added expiration date setting for keys "WEBSITE_PULL_IMAGE_OVER_VNET" = var.is_secure_mode ? "true" : "false" "WEBSITES_PORT" = "6000" diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 1a1c21181..6214592b5 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -179,7 +179,6 @@ resource "azurerm_linux_function_app" "function_app" { ENRICHMENT_BACKOFF = var.enrichmentBackoff ENABLE_DEV_CODE = tostring(var.enableDevCode) EMBEDDINGS_QUEUE = var.EMBEDDINGS_QUEUE - COSMOSDB_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" AZURE_SEARCH_SERVICE_ENDPOINT = var.azureSearchServiceEndpoint AZURE_SEARCH_INDEX = var.azureSearchIndex AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_credential_domain diff --git a/infra/core/host/webapp/webapp.tf b/infra/core/host/webapp/webapp.tf index 11fd005a4..96b2cafa7 100644 --- a/infra/core/host/webapp/webapp.tf +++ b/infra/core/host/webapp/webapp.tf @@ -114,7 +114,6 @@ resource "azurerm_linux_web_app" "app_service" { "SCM_DO_BUILD_DURING_DEPLOYMENT" = lower(tostring(var.scmDoBuildDuringDeployment)) "ENABLE_ORYX_BUILD" = lower(tostring(var.enableOryxBuild)) "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.applicationInsightsConnectionString - "COSMOSDB_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/COSMOSDB-KEY)" "BING_SEARCH_KEY" = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/BINGSEARCH-KEY)" "WEBSITE_PULL_IMAGE_OVER_VNET" = var.is_secure_mode ? "true" : "false" "WEBSITES_PORT" = "6000" diff --git a/infra/main.tf b/infra/main.tf index e8e6b4e2a..1b6030aff 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -681,6 +681,14 @@ module "userRoles" { resourceGroupId = azurerm_resource_group.rg.id } +resource "azurerm_cosmosdb_sql_role_assignment" "user_cosmosdb_data_contributor" { + resource_group_name = azurerm_resource_group.rg.name + account_name = module.cosmosdb.name + role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.DocumentDB/databaseAccounts/${module.cosmosdb.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002" #Cosmos DB Built-in Data Contributor + principal_id = data.azurerm_client_config.current.object_id + scope = module.cosmosdb.id +} + data "azurerm_resource_group" "existing" { count = var.useExistingAOAIService ? 1 : 0 name = var.azureOpenAIResourceGroup @@ -808,6 +816,30 @@ module "fuctionApp_StorageBlobDataOwner" { resourceGroupId = azurerm_resource_group.rg.id } +resource "azurerm_cosmosdb_sql_role_assignment" "webApp_cosmosdb_data_contributor" { + resource_group_name = azurerm_resource_group.rg.name + account_name = module.cosmosdb.name + role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.DocumentDB/databaseAccounts/${module.cosmosdb.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002" #Cosmos DB Built-in Data Contributor + principal_id = module.webapp.identityPrincipalId + scope = module.cosmosdb.id +} + +resource "azurerm_cosmosdb_sql_role_assignment" "functionApp_cosmosdb_data_contributor" { + resource_group_name = azurerm_resource_group.rg.name + account_name = module.cosmosdb.name + role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.DocumentDB/databaseAccounts/${module.cosmosdb.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002" #Cosmos DB Built-in Data Contributor + principal_id = module.functions.function_app_identity_principal_id + scope = module.cosmosdb.id +} + +resource "azurerm_cosmosdb_sql_role_assignment" "enrichmentApp_cosmosdb_data_contributor" { + resource_group_name = azurerm_resource_group.rg.name + account_name = module.cosmosdb.name + role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.DocumentDB/databaseAccounts/${module.cosmosdb.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002" #Cosmos DB Built-in Data Contributor + principal_id = module.enrichmentApp.identityPrincipalId + scope = module.cosmosdb.id +} + module "docIntel_StorageBlobDataReader" { source = "./core/security/role" scope = azurerm_resource_group.rg.id diff --git a/scripts/build.sh b/scripts/build.sh index ad4de017e..f1c62d6e0 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -31,14 +31,14 @@ npm run build # copy the shared_code files from functions to the webapp cd ../backend mkdir -p ./shared_code -cp ../../functions/shared_code/status_log.py ./shared_code -cp ../../functions/shared_code/__init__.py ./shared_code +cp -u ../../functions/shared_code/status_log.py ./shared_code +cp -u ../../functions/shared_code/__init__.py ./shared_code cd $DIR # zip the enrichment app content from app/enrichments to the .artifacts folders cd ${ENRICHMENT_ROOT_PATH} mkdir -p ./shared_code -cp ../../functions/shared_code/status_log.py ./shared_code -cp ../../functions/shared_code/utilities_helper.py ./shared_code +cp -u ../../functions/shared_code/status_log.py ./shared_code +cp -u ../../functions/shared_code/utilities_helper.py ./shared_code echo "Successfully prepared enrichment app code" echo -e "\n" \ No newline at end of file diff --git a/scripts/extract-content.py b/scripts/extract-content.py index f053c2659..6ad399f06 100755 --- a/scripts/extract-content.py +++ b/scripts/extract-content.py @@ -164,7 +164,6 @@ def get_storage_account_endpoint(storage_account_name): new_search_endpoint = f'https://infoasst-search-{new_random_text}.search.windows.net' new_cosmosdb_url = f'https://infoasst-cosmos-{new_random_text}.documents.azure.com:443/' -new_cosmosdb_key = new_secret_client.get_secret('COSMOSDB-KEY').value new_azure_blob_storage_account = f"infoasststore{new_random_text}" new_azure_blob_storage_endpoint = get_storage_account_endpoint(new_azure_blob_storage_account) @@ -290,7 +289,7 @@ def get_storage_account_endpoint(storage_account_name): old_status_container = old_status_database.get_container_client('statuscontainer') old_tags_database = old_cosmos_client.get_database_client('tagdb') old_tags_container = old_tags_database.get_container_client('tagcontainer') - new_cosmos_client = CosmosClient(new_cosmosdb_url, new_cosmosdb_key, consistency_level='Session') + new_cosmos_client = CosmosClient(new_cosmosdb_url, DefaultAzureCredential(), consistency_level='Session') new_status_database = new_cosmos_client.get_database_client('statusdb') new_status_container = new_status_database.get_container_client('statuscontainer') diff --git a/scripts/inf-import-state.sh b/scripts/inf-import-state.sh index 140e8f937..7521cb119 100755 --- a/scripts/inf-import-state.sh +++ b/scripts/inf-import-state.sh @@ -494,9 +494,6 @@ import_resource_if_needed "$module_path" "$resourceId$providers" providers="/providers/Microsoft.DocumentDB/databaseAccounts/$name/sqlDatabases/statusdb/containers/statuscontainer" module_path="module.cosmosdb.azurerm_cosmosdb_sql_container.log_container" import_resource_if_needed "$module_path" "$resourceId$providers" -secret_id=$(get_secret "COSMOSDB-KEY") -module_path="module.cosmosdb.azurerm_key_vault_secret.cosmos_db_key" -import_resource_if_needed "$module_path" "$secret_id" # Search Service diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index 980e23ab7..02ef9bacf 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -15,25 +15,8 @@ if [ -n "${IN_AUTOMATION}" ]; then az account set -s "$ARM_SUBSCRIPTION_ID" > /dev/null 2>&1 fi -secrets="{" -# Name of your Key Vault -keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) - -# Names of your secrets -secretNames=("COSMOSDB-KEY") - -# Retrieve and export each secret -for secretName in "${secretNames[@]}"; do - secretValue=$(az keyvault secret show --name $secretName --vault-name $keyVaultName --query value -o tsv) - envVarName=$(echo $secretName | tr '-' '_') - secrets+="\"$envVarName\": \"$secretValue\"," -done -secrets=${secrets%?} # Remove the trailing comma -secrets+="}" -secrets="${secrets%,}" - -jq -r --arg secrets "$secrets" ' - [ +jq -r ' + [ { "path": "AZURE_STORAGE_ACCOUNT", "env_var": "BLOB_STORAGE_ACCOUNT" @@ -188,7 +171,7 @@ jq -r --arg secrets "$secrets" ' "LOCAL_DEBUG": "true", "AzureWebJobsStorage": "", "STORAGE_CONNECTION_STRING": "" - } + ($secrets | fromjson) + } )} ' diff --git a/scripts/json-to-env.sh b/scripts/json-to-env.sh index bba4972ea..0778cab44 100755 --- a/scripts/json-to-env.sh +++ b/scripts/json-to-env.sh @@ -145,25 +145,4 @@ jq -r ' | .[] ' | sed "s/\"/'/g" # replace double quote with single quote to handle special chars - -if [ -n "${IN_AUTOMATION}" ]; then - if [ -n "${AZURE_ENVIRONMENT}" ] && [[ $AZURE_ENVIRONMENT == "AzureUSGovernment" ]]; then - az cloud set --name AZUReUSGovernment > /dev/null 2>&1 - fi - - az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID" > /dev/null 2>&1 - az account set -s "$ARM_SUBSCRIPTION_ID" > /dev/null 2>&1 -fi - -# Name of your Key Vault -keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) -# Names of your secrets -secretNames=("COSMOSDB-KEY") - -# Retrieve and export each secret -for secretName in "${secretNames[@]}"; do - secretValue=$(az keyvault secret show --name $secretName --vault-name $keyVaultName --query value -o tsv) - envVarName=$(echo $secretName | tr '-' '_') - echo export $envVarName=\'$secretValue\' -done \ No newline at end of file diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index f70457eb2..af8164c3b 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -207,9 +207,9 @@ keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets if [ -n "${SECURE_MODE}" ]; then - secretNames=("COSMOSDB-KEY") + secretNames=() else - secretNames=("COSMOSDB-KEY" "BINGSEARCH-KEY") + secretNames=("BINGSEARCH-KEY") fi diff --git a/scripts/merge-databases.py b/scripts/merge-databases.py index 3294fc9a0..729607529 100755 --- a/scripts/merge-databases.py +++ b/scripts/merge-databases.py @@ -75,8 +75,8 @@ def get_keyvault_url(keyvault_name, resource_group=None): key_vault_name = f'infoasst-kv-{old_random_text}' key_vault_url = get_keyvault_url(key_vault_name) -sClient = SecretClient(vault_url=key_vault_url, credential=credential) -cosmosdb_key = sClient.get_secret('COSMOSDB-KEY') +sClient = SecretClient(vault_url=key_vault_url, credential=credential) +cosmosdb_key = sClient.get_secret('COSMOSDB-KEY') # ************************************************************************* From 52b98ac6f9da79a17887d3ce8e0a81eb3e6dd4b8 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Sat, 20 Jul 2024 11:19:00 +0100 Subject: [PATCH 15/33] fix pipeline errors --- infra/core/search/search-services.tf | 2 +- infra/providers.tf | 2 +- scripts/deploy-search-indexes.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/infra/core/search/search-services.tf b/infra/core/search/search-services.tf index 02bb827e3..b0afd64dd 100644 --- a/infra/core/search/search-services.tf +++ b/infra/core/search/search-services.tf @@ -6,7 +6,7 @@ resource "azurerm_search_service" "search" { sku = var.sku["name"] tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true - local_authentication_enabled = var.is_secure_mode ? false : true + local_authentication_enabled = false replica_count = 1 partition_count = 1 semantic_search_sku = var.semanticSearch diff --git a/infra/providers.tf b/infra/providers.tf index b2923ae5e..5b367f3e8 100644 --- a/infra/providers.tf +++ b/infra/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.93.0" + version = "~> 3.113.0" } azuread = { source = "hashicorp/azuread" diff --git a/scripts/deploy-search-indexes.sh b/scripts/deploy-search-indexes.sh index 01d6531b6..2abc785f2 100755 --- a/scripts/deploy-search-indexes.sh +++ b/scripts/deploy-search-indexes.sh @@ -25,7 +25,7 @@ fi search_url="${AZURE_SEARCH_SERVICE_ENDPOINT}" # Obtain an access token for Azure Search -access_token=$(az account get-access-token --resource https://search.azure.com --query accessToken -o tsv) +access_token=$(az account get-access-token --query accessToken -o tsv) # Fetch existing index definition if it exists From 33aebe32f68f5c17e6ba6bdb66c184a2c62a77fd Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Sat, 20 Jul 2024 11:48:13 +0100 Subject: [PATCH 16/33] change to trigger new build --- infra/core/host/functions/functions.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 6214592b5..618b267de 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -133,6 +133,8 @@ resource "azurerm_linux_function_app" "function_app" { AzureWebJobsStorage__accountName = var.blobStorageAccountName AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" + AzureWebJobsStorage__tableServiceUri = "https://${var.blobStorageAccountName}.table.${var.endpointSuffix}" + AzureWebJobsStorage__fileServiceUri = "https://${var.blobStorageAccountName}.file.${var.endpointSuffix}" STORAGE_CONNECTION_STRING = "" STORAGE_CONNECTION_STRING__accountName = var.blobStorageAccountName STORAGE_CONNECTION_STRING__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" From aad223d1a1c6122b3e4ffef165030a51874d1be5 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Sat, 20 Jul 2024 17:02:16 +0100 Subject: [PATCH 17/33] whitespace change to trigger new build --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index db2542260..af4aae0ef 100644 --- a/Makefile +++ b/Makefile @@ -92,4 +92,3 @@ manual-inf-destroy: ## A command triggered by a user to destroy a resource group run-backend-tests: ## Run backend tests pip install -r ./app/backend/requirements.txt --disable-pip-version-check -q pytest ./app/backend/testsuite.py - From b4e688d62d092efb765e555c2f59b8c6cce7297b Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Sat, 20 Jul 2024 17:40:28 +0100 Subject: [PATCH 18/33] Update Azure Search scope URLs in deployment scripts --- scripts/deploy-search-indexes.sh | 3 +-- scripts/environments/AzureEnvironments/AzureCloud.env | 1 + scripts/environments/AzureEnvironments/AzureUSGovernment.env | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/deploy-search-indexes.sh b/scripts/deploy-search-indexes.sh index 2abc785f2..945e00625 100755 --- a/scripts/deploy-search-indexes.sh +++ b/scripts/deploy-search-indexes.sh @@ -25,8 +25,7 @@ fi search_url="${AZURE_SEARCH_SERVICE_ENDPOINT}" # Obtain an access token for Azure Search -access_token=$(az account get-access-token --query accessToken -o tsv) - +access_token=$(az account get-access-token --resource $TF_VAR_azure_search_scope --query accessToken -o tsv) # Fetch existing index definition if it exists index_vector_json=$(cat ${DIR}/../azure_search/create_vector_index.json | envsubst | tr -d "\n" | tr -d "\r") diff --git a/scripts/environments/AzureEnvironments/AzureCloud.env b/scripts/environments/AzureEnvironments/AzureCloud.env index 04af10bd0..31fe65463 100644 --- a/scripts/environments/AzureEnvironments/AzureCloud.env +++ b/scripts/environments/AzureEnvironments/AzureCloud.env @@ -2,6 +2,7 @@ export TF_VAR_arm_template_schema_mgmt_api="https://schema.management.azure.com" export TF_VAR_azure_portal_domain="https://portal.azure.com" export TF_VAR_azure_ai_form_recognizer_domain="api.cognitive.microsoft.com" export TF_VAR_azure_search_domain="search.windows.net" +export TF_VAR_azure_search_scope="https://search.azure.com" export TF_VAR_use_semantic_reranker=true export TF_VAR_azure_storage_domain="core.windows.net" export TF_VAR_azure_openai_domain="openai.azure.com" diff --git a/scripts/environments/AzureEnvironments/AzureUSGovernment.env b/scripts/environments/AzureEnvironments/AzureUSGovernment.env index 4de80a9d2..c3f4e0579 100644 --- a/scripts/environments/AzureEnvironments/AzureUSGovernment.env +++ b/scripts/environments/AzureEnvironments/AzureUSGovernment.env @@ -2,6 +2,7 @@ export TF_VAR_arm_template_schema_mgmt_api="https://schema.management.usgovcloud export TF_VAR_azure_portal_domain="https://portal.azure.us" export TF_VAR_azure_ai_form_recognizer_domain="api.cognitive.microsoft.us" export TF_VAR_azure_search_domain="search.azure.us" +export TF_VAR_azure_search_scope="https://search.azure.us" export TF_VAR_use_semantic_reranker=false export TF_VAR_azure_storage_domain="core.usgovcloudapi.net" export TF_VAR_azure_openai_domain="openai.azure.us" From 579518628e3c96db0f438207684c6f1ab604ef13 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Sun, 21 Jul 2024 16:54:45 +0100 Subject: [PATCH 19/33] Fix Azure Function startup errors --- functions/FileFormRecPollingPDF/function.json | 2 +- functions/FileFormRecSubmissionPDF/function.json | 2 +- functions/FileLayoutParsingOther/function.json | 2 +- functions/FileUploadedFunc/function.json | 2 +- functions/ImageEnrichment/function.json | 2 +- functions/TextEnrichment/function.json | 2 +- infra/core/host/functions/functions.tf | 13 +++++++------ infra/core/host/functions/outputs.tf | 4 ---- infra/main.tf | 12 ++++++------ scripts/json-to-env.function.debug.sh | 2 -- 10 files changed, 19 insertions(+), 24 deletions(-) diff --git a/functions/FileFormRecPollingPDF/function.json b/functions/FileFormRecPollingPDF/function.json index 664548722..76d26036f 100644 --- a/functions/FileFormRecPollingPDF/function.json +++ b/functions/FileFormRecPollingPDF/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "pdf-polling-queue", - "connection": "STORAGE_CONNECTION_STRING" + "connection": "" } ] } \ No newline at end of file diff --git a/functions/FileFormRecSubmissionPDF/function.json b/functions/FileFormRecSubmissionPDF/function.json index cda624b76..c79da65a2 100644 --- a/functions/FileFormRecSubmissionPDF/function.json +++ b/functions/FileFormRecSubmissionPDF/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "pdf-submit-queue", - "connection": "STORAGE_CONNECTION_STRING" + "connection": "" } ] } \ No newline at end of file diff --git a/functions/FileLayoutParsingOther/function.json b/functions/FileLayoutParsingOther/function.json index cc4480cda..2edb946c6 100644 --- a/functions/FileLayoutParsingOther/function.json +++ b/functions/FileLayoutParsingOther/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "non-pdf-submit-queue", - "connection": "STORAGE_CONNECTION_STRING" + "connection": "" } ] } \ No newline at end of file diff --git a/functions/FileUploadedFunc/function.json b/functions/FileUploadedFunc/function.json index ee7415f7b..2dd30a86c 100644 --- a/functions/FileUploadedFunc/function.json +++ b/functions/FileUploadedFunc/function.json @@ -6,7 +6,7 @@ "type": "blobTrigger", "direction": "in", "path": "upload", - "connection": "STORAGE_CONNECTION_STRING" + "connection": "" } ], "retry": { diff --git a/functions/ImageEnrichment/function.json b/functions/ImageEnrichment/function.json index 9b17f12de..237b3893a 100644 --- a/functions/ImageEnrichment/function.json +++ b/functions/ImageEnrichment/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "image-enrichment-queue", - "connection": "STORAGE_CONNECTION_STRING" + "connection": "" } ] } \ No newline at end of file diff --git a/functions/TextEnrichment/function.json b/functions/TextEnrichment/function.json index 8a4dde318..34ffdf923 100644 --- a/functions/TextEnrichment/function.json +++ b/functions/TextEnrichment/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "text-enrichment-queue", - "connection": "STORAGE_CONNECTION_STRING" + "connection": "" } ] } \ No newline at end of file diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 618b267de..7c5b8459d 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -129,16 +129,13 @@ resource "azurerm_linux_function_app" "function_app" { SCM_DO_BUILD_DURING_DEPLOYMENT = "false" ENABLE_ORYX_BUILD = "false" #Set all connections to use Managed Identity instead of connection strings - AzureWebJobsStorage = "" AzureWebJobsStorage__accountName = var.blobStorageAccountName AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" AzureWebJobsStorage__tableServiceUri = "https://${var.blobStorageAccountName}.table.${var.endpointSuffix}" AzureWebJobsStorage__fileServiceUri = "https://${var.blobStorageAccountName}.file.${var.endpointSuffix}" - STORAGE_CONNECTION_STRING = "" - STORAGE_CONNECTION_STRING__accountName = var.blobStorageAccountName - STORAGE_CONNECTION_STRING__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" - STORAGE_CONNECTION_STRING__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" + AzureWebJobsSecretStorageKeyVaultUri = data.azurerm_key_vault.existing.vault_uri + AzureWebJobsSecretStorageType = "keyvault" FUNCTIONS_WORKER_RUNTIME = var.runtime FUNCTIONS_EXTENSION_VERSION = "~4" @@ -184,6 +181,8 @@ resource "azurerm_linux_function_app" "function_app" { AZURE_SEARCH_SERVICE_ENDPOINT = var.azureSearchServiceEndpoint AZURE_SEARCH_INDEX = var.azureSearchIndex AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_credential_domain + AZURE_OPENAI_AUTHORITY_HOST = var.azure_environment + LOCAL_DEBUG = "false" } } @@ -232,7 +231,9 @@ resource "azurerm_key_vault_access_policy" "policy" { secret_permissions = [ "Get", - "List" + "List", + "Set", + "Delete" ] } diff --git a/infra/core/host/functions/outputs.tf b/infra/core/host/functions/outputs.tf index 077218d54..0a287708c 100644 --- a/infra/core/host/functions/outputs.tf +++ b/infra/core/host/functions/outputs.tf @@ -2,10 +2,6 @@ output "function_app_name" { value = azurerm_linux_function_app.function_app.name } -output "function_app_identity_principal_id" { - value = azurerm_linux_function_app.function_app.identity.0.principal_id -} - output "name" { value = azurerm_service_plan.funcServicePlan.name } diff --git a/infra/main.tf b/infra/main.tf index 1b6030aff..477eeedff 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -720,7 +720,7 @@ module "enrichmentApp_OpenAiRole" { module "webApp_CognitiveServicesUser" { source = "./core/security/role" - scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + scope = azurerm_resource_group.rg.id principalId = module.webapp.identityPrincipalId roleDefinitionId = local.azure_roles.CognitiveServicesUser principalType = "ServicePrincipal" @@ -731,7 +731,7 @@ module "webApp_CognitiveServicesUser" { module "functionApp_CognitiveServicesUser" { source = "./core/security/role" - scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + scope = azurerm_resource_group.rg.id principalId = module.functions.identityPrincipalId roleDefinitionId = local.azure_roles.CognitiveServicesUser principalType = "ServicePrincipal" @@ -742,7 +742,7 @@ module "functionApp_CognitiveServicesUser" { module "enrichmentApp_StorageQueueDataContributor" { source = "./core/security/role" - scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + scope = azurerm_resource_group.rg.id principalId = module.enrichmentApp.identityPrincipalId roleDefinitionId = local.azure_roles.StorageQueueDataContributor principalType = "ServicePrincipal" @@ -753,7 +753,7 @@ module "enrichmentApp_StorageQueueDataContributor" { module "functionApp_StorageQueueDataContributor" { source = "./core/security/role" - scope = var.useExistingAOAIService ? data.azurerm_resource_group.existing[0].id : azurerm_resource_group.rg.id + scope = azurerm_resource_group.rg.id principalId = module.functions.identityPrincipalId roleDefinitionId = local.azure_roles.StorageQueueDataContributor principalType = "ServicePrincipal" @@ -809,7 +809,7 @@ module "fuctionApp_StorageBlobDataOwner" { source = "./core/security/role" scope = azurerm_resource_group.rg.id - principalId = module.functions.function_app_identity_principal_id + principalId = module.functions.identityPrincipalId roleDefinitionId = local.azure_roles.StorageBlobDataOwner principalType = "ServicePrincipal" subscriptionId = data.azurerm_client_config.current.subscription_id @@ -828,7 +828,7 @@ resource "azurerm_cosmosdb_sql_role_assignment" "functionApp_cosmosdb_data_contr resource_group_name = azurerm_resource_group.rg.name account_name = module.cosmosdb.name role_definition_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.DocumentDB/databaseAccounts/${module.cosmosdb.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002" #Cosmos DB Built-in Data Contributor - principal_id = module.functions.function_app_identity_principal_id + principal_id = module.functions.identityPrincipalId scope = module.cosmosdb.id } diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index 02ef9bacf..b81a1eb60 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -169,8 +169,6 @@ jq -r ' "TEXT_ENRICHMENT_QUEUE": "text-enrichment-queue", "IMAGE_ENRICHMENT_QUEUE": "image-enrichment-queue", "LOCAL_DEBUG": "true", - "AzureWebJobsStorage": "", - "STORAGE_CONNECTION_STRING": "" } )} From dece0909adcd2c31c57c717fb22faa98ac0bfafb Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:25:50 +0100 Subject: [PATCH 20/33] White space change to trigger build From 3b4d713714c3643feff66e1dee9aae4bdfdc7459 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:01:55 +0100 Subject: [PATCH 21/33] White space update to trigger new build --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index af4aae0ef..db2542260 100644 --- a/Makefile +++ b/Makefile @@ -92,3 +92,4 @@ manual-inf-destroy: ## A command triggered by a user to destroy a resource group run-backend-tests: ## Run backend tests pip install -r ./app/backend/requirements.txt --disable-pip-version-check -q pytest ./app/backend/testsuite.py + From 761020bf5b63804e88ad6c111d42ac90331f2a47 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:58:38 +0100 Subject: [PATCH 22/33] Update Azure Functions deployment to use managed identities --- functions/FileFormRecPollingPDF/function.json | 2 +- .../FileFormRecSubmissionPDF/function.json | 2 +- .../FileLayoutParsingOther/function.json | 2 +- functions/FileUploadedFunc/function.json | 2 +- functions/ImageEnrichment/function.json | 2 +- functions/TextEnrichment/function.json | 2 +- infra/core/db/cosmosdb.tf | 4 +-- infra/core/host/functions/functions.tf | 28 +++++++++------ infra/main.tf | 35 ++++++++++++++++++- 9 files changed, 59 insertions(+), 20 deletions(-) diff --git a/functions/FileFormRecPollingPDF/function.json b/functions/FileFormRecPollingPDF/function.json index 76d26036f..a81487751 100644 --- a/functions/FileFormRecPollingPDF/function.json +++ b/functions/FileFormRecPollingPDF/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "pdf-polling-queue", - "connection": "" + "connection": "AzureStorageConnection1" } ] } \ No newline at end of file diff --git a/functions/FileFormRecSubmissionPDF/function.json b/functions/FileFormRecSubmissionPDF/function.json index c79da65a2..41639b032 100644 --- a/functions/FileFormRecSubmissionPDF/function.json +++ b/functions/FileFormRecSubmissionPDF/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "pdf-submit-queue", - "connection": "" + "connection": "AzureStorageConnection1" } ] } \ No newline at end of file diff --git a/functions/FileLayoutParsingOther/function.json b/functions/FileLayoutParsingOther/function.json index 2edb946c6..a40410fcc 100644 --- a/functions/FileLayoutParsingOther/function.json +++ b/functions/FileLayoutParsingOther/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "non-pdf-submit-queue", - "connection": "" + "connection": "AzureStorageConnection1" } ] } \ No newline at end of file diff --git a/functions/FileUploadedFunc/function.json b/functions/FileUploadedFunc/function.json index 2dd30a86c..68776e21d 100644 --- a/functions/FileUploadedFunc/function.json +++ b/functions/FileUploadedFunc/function.json @@ -6,7 +6,7 @@ "type": "blobTrigger", "direction": "in", "path": "upload", - "connection": "" + "connection": "AzureStorageConnection1" } ], "retry": { diff --git a/functions/ImageEnrichment/function.json b/functions/ImageEnrichment/function.json index 237b3893a..5b04da35b 100644 --- a/functions/ImageEnrichment/function.json +++ b/functions/ImageEnrichment/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "image-enrichment-queue", - "connection": "" + "connection": "AzureStorageConnection1" } ] } \ No newline at end of file diff --git a/functions/TextEnrichment/function.json b/functions/TextEnrichment/function.json index 34ffdf923..a53d80939 100644 --- a/functions/TextEnrichment/function.json +++ b/functions/TextEnrichment/function.json @@ -6,7 +6,7 @@ "type": "queueTrigger", "direction": "in", "queueName": "text-enrichment-queue", - "connection": "" + "connection": "AzureStorageConnection1" } ] } \ No newline at end of file diff --git a/infra/core/db/cosmosdb.tf b/infra/core/db/cosmosdb.tf index ed85cda4b..c6fdc615a 100644 --- a/infra/core/db/cosmosdb.tf +++ b/infra/core/db/cosmosdb.tf @@ -35,7 +35,7 @@ resource "azurerm_cosmosdb_account" "cosmosdb_account" { kind = "GlobalDocumentDB" tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true - #local_authentication_disabled = var.is_secure_mode ? true : false + local_authentication_disabled = var.is_secure_mode ? true : false consistency_policy { consistency_level = var.defaultConsistencyLevel @@ -65,7 +65,7 @@ resource "azurerm_cosmosdb_sql_container" "log_container" { account_name = azurerm_cosmosdb_account.cosmosdb_account.name database_name = azurerm_cosmosdb_sql_database.log_database.name - partition_key_path = "/file_name" + partition_key_paths = ["/file_name"] } data "azurerm_subnet" "subnet" { diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 7c5b8459d..d918456d1 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -98,21 +98,21 @@ resource "azurerm_linux_function_app" "function_app" { site_config { application_stack { docker { - image_name = "${var.container_registry}/functionapp" - image_tag = "latest" - registry_url = "https://${var.container_registry}" - registry_username = var.container_registry_admin_username - registry_password = var.container_registry_admin_password + image_name = "${var.container_registry}/functionapp" + image_tag = "latest" + registry_url = "https://${var.container_registry}" + registry_username = var.container_registry_admin_username + registry_password = var.container_registry_admin_password } } - container_registry_use_managed_identity = true - always_on = true - http2_enabled = true - ftps_state = var.is_secure_mode ? "Disabled" : var.ftpsState + container_registry_use_managed_identity = true + always_on = true + http2_enabled = true + ftps_state = var.is_secure_mode ? "Disabled" : var.ftpsState cors { - allowed_origins = concat([var.azure_portal_domain, "https://ms.portal.azure.com"], var.allowedOrigins) + allowed_origins = concat([var.azure_portal_domain, "https://ms.portal.azure.com"], var.allowedOrigins) } - vnet_route_all_enabled = var.is_secure_mode ? true : false + vnet_route_all_enabled = var.is_secure_mode ? true : false } identity { @@ -136,6 +136,12 @@ resource "azurerm_linux_function_app" "function_app" { AzureWebJobsStorage__fileServiceUri = "https://${var.blobStorageAccountName}.file.${var.endpointSuffix}" AzureWebJobsSecretStorageKeyVaultUri = data.azurerm_key_vault.existing.vault_uri AzureWebJobsSecretStorageType = "keyvault" + + AzureStorageConnection1__accountName = var.blobStorageAccountName + AzureStorageConnection1__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" + AzureStorageConnection1__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" + AzureStorageConnection1__tableServiceUri = "https://${var.blobStorageAccountName}.table.${var.endpointSuffix}" + AzureStorageConnection1__fileServiceUri = "https://${var.blobStorageAccountName}.file.${var.endpointSuffix}" FUNCTIONS_WORKER_RUNTIME = var.runtime FUNCTIONS_EXTENSION_VERSION = "~4" diff --git a/infra/main.tf b/infra/main.tf index 477eeedff..831d5197c 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -739,6 +739,17 @@ module "functionApp_CognitiveServicesUser" { resourceGroupId = azurerm_resource_group.rg.id } +module "enrichmentApp_CognitiveServicesUser" { + source = "./core/security/role" + + scope = azurerm_resource_group.rg.id + principalId = module.enrichmentApp.identityPrincipalId + roleDefinitionId = local.azure_roles.CognitiveServicesUser + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + module "enrichmentApp_StorageQueueDataContributor" { source = "./core/security/role" @@ -799,7 +810,7 @@ module "encrichmentApp_SearchIndexDataReader" { scope = azurerm_resource_group.rg.id principalId = module.enrichmentApp.identityPrincipalId - roleDefinitionId = local.azure_roles.SearchIndexDataReader + roleDefinitionId = local.azure_roles.SearchIndexDataContributor principalType = "ServicePrincipal" subscriptionId = data.azurerm_client_config.current.subscription_id resourceGroupId = azurerm_resource_group.rg.id @@ -816,6 +827,28 @@ module "fuctionApp_StorageBlobDataOwner" { resourceGroupId = azurerm_resource_group.rg.id } +module "enrichmentApp_StorageBlobDataOwner" { + source = "./core/security/role" + + scope = azurerm_resource_group.rg.id + principalId = module.enrichmentApp.identityPrincipalId + roleDefinitionId = local.azure_roles.StorageBlobDataOwner + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + +module "fuctionApp_StorageAccountContributor" { + source = "./core/security/role" + + scope = azurerm_resource_group.rg.id + principalId = module.functions.identityPrincipalId + roleDefinitionId = local.azure_roles.StorageAccountContributor + principalType = "ServicePrincipal" + subscriptionId = data.azurerm_client_config.current.subscription_id + resourceGroupId = azurerm_resource_group.rg.id +} + resource "azurerm_cosmosdb_sql_role_assignment" "webApp_cosmosdb_data_contributor" { resource_group_name = azurerm_resource_group.rg.name account_name = module.cosmosdb.name From cc5fda8e266e6fdf63023368240e2f52e08f8bcd Mon Sep 17 00:00:00 2001 From: bjakems <165402330+bjakems@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:39:32 +0100 Subject: [PATCH 23/33] restore Vision to be key based access --- functions/ImageEnrichment/__init__.py | 4 +++- infra/core/ai/cogServices/cogServices.tf | 13 ++++++++++++- infra/core/host/functions/functions.tf | 1 + scripts/inf-import-state.sh | 3 +++ scripts/json-to-env.function.debug.sh | 19 ++++++++++++++++++- scripts/json-to-env.webapp.debug.sh | 6 +++--- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 024783909..e51e85118 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -4,6 +4,7 @@ import azure.functions as func import requests +from azure.core.credentials import AzureKeyCredential from azure.ai.vision.imageanalysis import ImageAnalysisClient from azure.ai.vision.imageanalysis.models import VisualFeatures from azure.storage.blob import BlobServiceClient @@ -37,6 +38,7 @@ cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] # Cognitive Services +azure_ai_key = os.environ["AZURE_AI_KEY"] azure_ai_endpoint = os.environ["AZURE_AI_ENDPOINT"] azure_ai_location = os.environ["AZURE_AI_LOCATION"] azure_ai_credential_domain = os.environ["AZURE_AI_CREDENTIAL_DOMAIN"] @@ -100,7 +102,7 @@ vision_client = ImageAnalysisClient( endpoint=azure_ai_endpoint, - credential=azure_credential + credential=AzureKeyCredential(azure_ai_key) ) FUNCTION_NAME = "ImageEnrichment" diff --git a/infra/core/ai/cogServices/cogServices.tf b/infra/core/ai/cogServices/cogServices.tf index 083913caf..0593e15e5 100644 --- a/infra/core/ai/cogServices/cogServices.tf +++ b/infra/core/ai/cogServices/cogServices.tf @@ -7,7 +7,18 @@ resource "azurerm_cognitive_account" "cognitiveService" { tags = var.tags custom_subdomain_name = var.name public_network_access_enabled = var.is_secure_mode ? false : true - local_auth_enabled = var.is_secure_mode ? false : true +} + +module "cog_service_key" { + source = "../../security/keyvaultSecret" + arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api + key_vault_name = var.key_vault_name + resourceGroupName = var.resourceGroupName + secret_name = "AZURE-AI-KEY" + secret_value = azurerm_cognitive_account.cognitiveService.primary_access_key + alias = "aisvckey" + tags = var.tags + kv_secret_expiration = var.kv_secret_expiration } data "azurerm_subnet" "subnet" { diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index d918456d1..3e425fb51 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -176,6 +176,7 @@ resource "azurerm_linux_function_app" "function_app" { SUBMIT_REQUEUE_HIDE_SECONDS = var.submitRequeueHideSeconds POLLING_BACKOFF = var.pollingBackoff MAX_READ_ATTEMPTS = var.maxReadAttempts + AZURE_AI_KEY = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-AI-KEY)" AZURE_AI_ENDPOINT = var.enrichmentEndpoint ENRICHMENT_NAME = var.enrichmentName AZURE_AI_LOCATION = var.enrichmentLocation diff --git a/scripts/inf-import-state.sh b/scripts/inf-import-state.sh index 7521cb119..231e8977c 100755 --- a/scripts/inf-import-state.sh +++ b/scripts/inf-import-state.sh @@ -365,6 +365,9 @@ name="infoasst-enrichment-cog-$random_text" providers="/providers/Microsoft.CognitiveServices/accounts/$name" module_path="module.cognitiveServices.azurerm_cognitive_account.cognitiveService" import_resource_if_needed "$module_path" "$resourceId$providers" +secret_id=$(get_secret "AZURE-AI-KEY") +module_path="module.cognitiveServices.azurerm_key_vault_secret.search_service_key" +import_resource_if_needed "$module_path" "$secret_id" # Key Vault echo diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index b81a1eb60..3ed938708 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -15,7 +15,24 @@ if [ -n "${IN_AUTOMATION}" ]; then az account set -s "$ARM_SUBSCRIPTION_ID" > /dev/null 2>&1 fi -jq -r ' +secrets="{" +# Name of your Key Vault +keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) + +# Names of your secrets +secretNames=("AZURE-AI-KEY") + +# Retrieve and export each secret +for secretName in "${secretNames[@]}"; do + secretValue=$(az keyvault secret show --name $secretName --vault-name $keyVaultName --query value -o tsv) + envVarName=$(echo $secretName | tr '-' '_') + secrets+="\"$envVarName\": \"$secretValue\"," +done +secrets=${secrets%?} # Remove the trailing comma +secrets+="}" +secrets="${secrets%,}" + +jq -r --arg secrets "$secrets" ' [ { "path": "AZURE_STORAGE_ACCOUNT", diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index af8164c3b..4d585fab2 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -207,9 +207,9 @@ keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets if [ -n "${SECURE_MODE}" ]; then - secretNames=() + secretNames=("AZURE-AI-KEY") else - secretNames=("BINGSEARCH-KEY") + secretNames=("BINGSEARCH-KEY" "AZURE-AI-KEY") fi @@ -218,4 +218,4 @@ for secretName in "${secretNames[@]}"; do secretValue=$(az keyvault secret show --name $secretName --vault-name $keyVaultName --query value -o tsv) envVarName=$(echo $secretName | tr '-' '_') echo $envVarName=\'$secretValue\' -done \ No newline at end of file +done From 4d57b27f0299a8dc190563c17674fd69b683bebd Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:24:21 +0100 Subject: [PATCH 24/33] chore: Update npm dependencies and fix vulnerabilities --- app/frontend/package.json | 4 +- app/frontend/vite.config.ts | 13 +++- functions/ImageEnrichment/__init__.py | 93 ++++++++++++-------------- functions/requirements.txt | 1 - infra/core/host/functions/functions.tf | 23 +++---- infra/core/network/network/output.tf | 4 ++ infra/core/storage/storage-account.tf | 14 +++- infra/main.tf | 2 +- scripts/build.sh | 1 + scripts/json-to-env.function.debug.sh | 17 +++-- 10 files changed, 99 insertions(+), 73 deletions(-) diff --git a/app/frontend/package.json b/app/frontend/package.json index 3987602b0..f1df48085 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -9,7 +9,7 @@ "watch": "tsc && vite build --watch" }, "dependencies": { - "@azure/storage-blob": "^12.13.0", + "@azure/storage-blob": "^12.24.0", "@fluentui/react": "^8.110.7", "@fluentui/react-icons": "^2.0.195", "@react-spring/web": "^9.7.1", @@ -43,6 +43,6 @@ "prettier": "^2.8.3", "typescript": "^4.9.3", "vite": "^5.0.10", - "vite-plugin-node-polyfills": "^0.22.0" + "vite-plugin-node-polyfills": "^0.2.0" } } diff --git a/app/frontend/vite.config.ts b/app/frontend/vite.config.ts index abd3b6854..8675f4d75 100644 --- a/app/frontend/vite.config.ts +++ b/app/frontend/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import postcssNesting from 'postcss-nesting'; import { nodePolyfills } from 'vite-plugin-node-polyfills' +import rollupNodePolyFill from 'rollup-plugin-node-polyfills' // https://vitejs.dev/config/ export default defineConfig({ @@ -9,7 +10,12 @@ export default defineConfig({ build: { outDir: "../backend/static", emptyOutDir: true, - sourcemap: true + sourcemap: true, + rollupOptions: { + plugins: [ + rollupNodePolyFill() + ] + } }, server: { proxy: { @@ -23,5 +29,10 @@ export default defineConfig({ postcssNesting ], }, + }, + resolve: { + alias: { + buffer: 'rollup-plugin-node-polyfills/polyfills/buffer-es6' + } } }); diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index e51e85118..b5e1af861 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -4,10 +4,8 @@ import azure.functions as func import requests -from azure.core.credentials import AzureKeyCredential -from azure.ai.vision.imageanalysis import ImageAnalysisClient -from azure.ai.vision.imageanalysis.models import VisualFeatures from azure.storage.blob import BlobServiceClient +from azure.core.credentials import AzureKeyCredential from azure.identity import ManagedIdentityCredential, DefaultAzureCredential, get_bearer_token_provider, AzureAuthorityHosts from shared_code.status_log import State, StatusClassification, StatusLog from shared_code.utilities import Utilities, MediaType @@ -89,21 +87,17 @@ "westus", ]: GPU_REGION = True - visual_features = [VisualFeatures.CAPTION, - VisualFeatures.DENSE_CAPTIONS, - VisualFeatures.OBJECTS, - VisualFeatures.TAGS, - VisualFeatures.READ] + VISION_ENDPOINT = f"{azure_ai_endpoint}computervision/imageanalysis:analyze?api-version=2023-04-01-preview&features=caption,denseCaptions,objects,tags,read&gender-neutral-caption=true" else: GPU_REGION = False - visual_features = [VisualFeatures.OBJECTS, - VisualFeatures.TAGS, - VisualFeatures.READ] + VISION_ENDPOINT = f"{azure_ai_endpoint}computervision/imageanalysis:analyze?api-version=2023-04-01-preview&features=objects,tags,read&gender-neutral-caption=true" -vision_client = ImageAnalysisClient( - endpoint=azure_ai_endpoint, - credential=AzureKeyCredential(azure_ai_key) - ) +vision_api_headers = { + "Ocp-Apim-Subscription-Key": azure_ai_key, + "Content-type": "application/octet-stream", + "Accept": "application/json", + "Ocp-Apim-Subscription-Region": azure_ai_location, +} FUNCTION_NAME = "ImageEnrichment" @@ -173,74 +167,75 @@ def main(msg: func.QueueMessage) -> None: blob_path) path = blob_path.split("/", 1)[1] - try: - blob_service_client = BlobServiceClient(account_url=azure_blob_storage_endpoint, + blob_service_client = BlobServiceClient(account_url=azure_blob_storage_endpoint, credential=azure_credential) - blob_client = blob_service_client.get_blob_client(container=azure_blob_drop_storage_container, + blob_client = blob_service_client.get_blob_client(container=azure_blob_drop_storage_container, blob=path) - image_data = blob_client.download_blob().readall() - response = vision_client.analyze(image_data=image_data, - visual_features=visual_features, - gender_neutral_caption=True) - - print(response) - + image_data = blob_client.download_blob().readall() + files = {"file": image_data} + response = requests.post(VISION_ENDPOINT, + headers=vision_api_headers, + files=files) + + if response.status_code == 200: + result = response.json() text_image_summary = "" index_content = "" complete_ocr_text = None if GPU_REGION: - if response.caption is not None: + if result["captionResult"] is not None: text_image_summary += "Caption:\n" text_image_summary += "\t'{}', Confidence {:.4f}\n".format( - response.caption.text, response.caption.confidence + result["captionResult"]["text"], result["captionResult"]["confidence"] ) - index_content += "Caption: {}\n ".format( - response.caption.text) + index_content += "Caption: {}\n ".format(result["captionResult"]["text"]) - if response.dense_captions is not None: + if result["denseCaptionsResult"] is not None: text_image_summary += "Dense Captions:\n" index_content += "DeepCaptions: " - for caption in response.dense_captions.values: + for caption in result["denseCaptionsResult"]["values"]: text_image_summary += "\t'{}', Confidence: {:.4f}\n".format( - caption.text, caption.confidence + caption["text"], caption["confidence"] ) - index_content += "{}\n ".format(caption.text) + index_content += "{}\n ".format(caption["text"]) - if response.objects is not None: + if result["objectsResult"] is not None: text_image_summary += "Objects:\n" index_content += "Descriptions: " - for object_detection in response.objects.values: + for object_detection in result["objectsResult"]["values"]: text_image_summary += "\t'{}', Confidence: {:.4f}\n".format( - object_detection.tags[0].name, object_detection.tags[0].confidence + object_detection["name"], object_detection["confidence"] ) - index_content += "{}\n ".format( - object_detection.tags[0].name) + index_content += "{}\n ".format(object_detection["name"]) - if response.tags is not None: + if result["tagsResult"] is not None: text_image_summary += "Tags:\n" - for tag in response.tags.values: + for tag in result["tagsResult"]["values"]: text_image_summary += "\t'{}', Confidence {:.4f}\n".format( - tag.name, tag.confidence + tag["name"], tag["confidence"] ) - index_content += "{}\n ".format(tag.name) + index_content += "{}\n ".format(tag["name"]) - if response.read is not None: + if result["readResult"] is not None: text_image_summary += "Raw OCR Text:\n" complete_ocr_text = "" - for line in response.read.blocks[0].lines: - complete_ocr_text += "{}\n".format(line.text) + for line in result["readResult"]["pages"][0]["words"]: + complete_ocr_text += "{}\n".format(line["content"]) text_image_summary += complete_ocr_text - except Exception as ex: - logging.error(f"{FUNCTION_NAME} - Image analysis failed for {blob_path}: {str(ex)}") + else: + logging.error("%s - Image analysis failed for %s: %s", + FUNCTION_NAME, + blob_path, + str(response.json())) statusLog.upsert_document( blob_path, - f"{FUNCTION_NAME} - Image analysis failed: {str(ex)}", + f"{FUNCTION_NAME} - Image analysis failed: {str(response.json())}", StatusClassification.ERROR, State.ERROR, ) - raise ex + raise requests.exceptions.HTTPError(response.json()) if complete_ocr_text not in [None, ""]: # Detect language diff --git a/functions/requirements.txt b/functions/requirements.txt index 149ae81e9..03f044a21 100644 --- a/functions/requirements.txt +++ b/functions/requirements.txt @@ -14,7 +14,6 @@ azure-cosmos == 4.7.0 azure-storage-queue == 12.6.0 nltk == 3.8.1 tenacity == 8.2.3 -azure-ai-vision-imageanalysis == 1.0.0b2 unstructured[csv,doc,docx,email,html,md,msg,ppt,pptx,text,xlsx,xml] == 0.12.5 pyoo == 1.4 azure-search-documents == 11.4.0b11 diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 3e425fb51..ff7dc5906 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -88,13 +88,15 @@ resource "azurerm_linux_function_app" "function_app" { resource_group_name = var.resourceGroupName service_plan_id = azurerm_service_plan.funcServicePlan.id storage_account_name = var.blobStorageAccountName - storage_uses_managed_identity = true + storage_account_access_key = data.azurerm_storage_account.existing_sa.primary_access_key + #storage_uses_managed_identity = true https_only = true tags = var.tags public_network_access_enabled = var.is_secure_mode ? false : true virtual_network_subnet_id = var.is_secure_mode ? var.subnetIntegration_id : null content_share_force_disabled = true + site_config { application_stack { docker { @@ -121,27 +123,22 @@ resource "azurerm_linux_function_app" "function_app" { app_settings = { # Network realated settings for secure mode - WEBSITE_VNET_ROUTE_ALL = var.is_secure_mode ? "1" : "0" - WEBSITE_CONTENTOVERVNET = var.is_secure_mode ? "1" : "0" WEBSITE_PULL_IMAGE_OVER_VNET = var.is_secure_mode ? "true" : "false" - WEBSITE_SKIP_CONTENTSHARE_VALIDATION = var.is_secure_mode ? "1" : "0" SCM_DO_BUILD_DURING_DEPLOYMENT = "false" ENABLE_ORYX_BUILD = "false" #Set all connections to use Managed Identity instead of connection strings - AzureWebJobsStorage__accountName = var.blobStorageAccountName - AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" - AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" - AzureWebJobsStorage__tableServiceUri = "https://${var.blobStorageAccountName}.table.${var.endpointSuffix}" - AzureWebJobsStorage__fileServiceUri = "https://${var.blobStorageAccountName}.file.${var.endpointSuffix}" - AzureWebJobsSecretStorageKeyVaultUri = data.azurerm_key_vault.existing.vault_uri - AzureWebJobsSecretStorageType = "keyvault" + AzureWebJobsStorage = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-STORAGE-CONECTION-STRING)" + # These will need to be enabled one Azure Functions has support for Managed Identity + #AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" + #AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" + #AzureWebJobsStorage__tableServiceUri = "https://${var.blobStorageAccountName}.table.${var.endpointSuffix}" + #AzureWebJobsSecretStorageKeyVaultUri = data.azurerm_key_vault.existing.vault_uri + #AzureWebJobsSecretStorageType = "keyvault" - AzureStorageConnection1__accountName = var.blobStorageAccountName AzureStorageConnection1__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" AzureStorageConnection1__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" AzureStorageConnection1__tableServiceUri = "https://${var.blobStorageAccountName}.table.${var.endpointSuffix}" - AzureStorageConnection1__fileServiceUri = "https://${var.blobStorageAccountName}.file.${var.endpointSuffix}" FUNCTIONS_WORKER_RUNTIME = var.runtime FUNCTIONS_EXTENSION_VERSION = "~4" diff --git a/infra/core/network/network/output.tf b/infra/core/network/network/output.tf index 676617de2..159202654 100644 --- a/infra/core/network/network/output.tf +++ b/infra/core/network/network/output.tf @@ -71,6 +71,10 @@ output "snetIntegration_id" { value = data.azurerm_subnet.integration.id } +output "snetIntegration_name" { + value = data.azurerm_subnet.integration.name +} + output "snetSearch_name" { value = data.azurerm_subnet.aiSearch.name } diff --git a/infra/core/storage/storage-account.tf b/infra/core/storage/storage-account.tf index 223394bd5..97ad81ff3 100644 --- a/infra/core/storage/storage-account.tf +++ b/infra/core/storage/storage-account.tf @@ -15,7 +15,7 @@ resource "azurerm_storage_account" "storage" { enable_https_traffic_only = true public_network_access_enabled = var.is_secure_mode ? false : true allow_nested_items_to_be_public = false - shared_access_key_enabled = var.is_secure_mode ? false : true + shared_access_key_enabled = true #var.is_secure_mode ? false : true # This will need to be enabled once the Azure Functions can support Entra ID auth network_rules { default_action = var.is_secure_mode ? "Deny" : "Allow" @@ -183,6 +183,18 @@ resource "azurerm_resource_group_template_deployment" "queue" { deployment_mode = "Incremental" } +module "storage_connection_string" { + source = "../security/keyvaultSecret" + resourceGroupName = var.resourceGroupName + arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api + key_vault_name = var.key_vault_name + secret_name = "AZURE-STORAGE-CONECTION-STRING" + secret_value = azurerm_storage_account.storage.primary_blob_connection_string + tags = var.tags + alias = "blobconnstring" + kv_secret_expiration = var.kv_secret_expiration +} + data "azurerm_subnet" "subnet" { count = var.is_secure_mode ? 1 : 0 name = var.subnet_name diff --git a/infra/main.tf b/infra/main.tf index 831d5197c..975f1e44e 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -237,7 +237,7 @@ module "storage" { containers = ["content","website","upload","function","logs","config"] queueNames = ["pdf-submit-queue","pdf-polling-queue","non-pdf-submit-queue","media-submit-queue","text-enrichment-queue","image-enrichment-queue","embeddings-queue"] is_secure_mode = var.is_secure_mode - subnet_name = var.is_secure_mode ? module.network[0].snetStorage_name : null + subnet_name = var.is_secure_mode ? module.network[0].snetFunction_name : null vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneStorageAccountBlob[0].privateDnsZoneResourceId, module.privateDnsZoneStorageAccountFile[0].privateDnsZoneResourceId, diff --git a/scripts/build.sh b/scripts/build.sh index f1c62d6e0..4f504adc3 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -25,6 +25,7 @@ rm -rf ${BINARIES_OUTPUT_PATH} && mkdir -p ${BINARIES_OUTPUT_PATH} #Build the AzLib that contains the JavaScript functions that enable the upload feature cd app/frontend +npm audit fix npm install npm run build diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index 3ed938708..37f0ea359 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -20,13 +20,20 @@ secrets="{" keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("AZURE-AI-KEY") +secretNames=("AZURE-AI-KEY" "AZURE-STORAGE-CONECTION-STRING") +azWebJobSecretName="AZURE-STORAGE-CONECTION-STRING" +azWebJobVarName="AzureWebJobsStorage" # Retrieve and export each secret for secretName in "${secretNames[@]}"; do secretValue=$(az keyvault secret show --name $secretName --vault-name $keyVaultName --query value -o tsv) envVarName=$(echo $secretName | tr '-' '_') secrets+="\"$envVarName\": \"$secretValue\"," + + if [ "$secretName" == "$azWebJobSecretName" ]; then + export $azWebJobVarName=$secretValue + secrets+="\"$azWebJobVarName\": \"$secretValue\"," + fi done secrets=${secrets%?} # Remove the trailing comma secrets+="}" @@ -88,15 +95,15 @@ jq -r --arg secrets "$secrets" ' }, { "path": "FUNC_STORAGE_CONNECTION_STRING__accountName", - "env_var": "STORAGE_CONNECTION_STRING__accountName" + "env_var": "AzureStorageConnection1__accountName" }, { "path": "FUNC_STORAGE_CONNECTION_STRING__queueServiceUri", - "env_var": "STORAGE_CONNECTION_STRING__queueServiceUri" + "env_var": "AzureStorageConnection1__queueServiceUri" }, { "path": "FUNC_STORAGE_CONNECTION_STRING__blobServiceUri", - "env_var": "STORAGE_CONNECTION_STRING__blobServiceUri" + "env_var": "AzureStorageConnection1__blobServiceUri" }, { "path": "AZURE_AI_ENDPOINT", @@ -186,7 +193,7 @@ jq -r --arg secrets "$secrets" ' "TEXT_ENRICHMENT_QUEUE": "text-enrichment-queue", "IMAGE_ENRICHMENT_QUEUE": "image-enrichment-queue", "LOCAL_DEBUG": "true", - } + } + ($secrets | fromjson) )} ' From e2f05461d99ab282a70590884302148bd9013e30 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:38:43 +0100 Subject: [PATCH 25/33] chore: Update vite.config.ts to add process polyfill --- app/frontend/vite.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/frontend/vite.config.ts b/app/frontend/vite.config.ts index 8675f4d75..45a21c59c 100644 --- a/app/frontend/vite.config.ts +++ b/app/frontend/vite.config.ts @@ -32,7 +32,8 @@ export default defineConfig({ }, resolve: { alias: { - buffer: 'rollup-plugin-node-polyfills/polyfills/buffer-es6' + buffer: 'rollup-plugin-node-polyfills/polyfills/buffer-es6', + process: 'rollup-plugin-node-polyfills/polyfills/process-es6' } } }); From ee08e09ada20217ac54af8e65521877be7ff8e70 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:36:43 +0100 Subject: [PATCH 26/33] fixes for Entra Auth issues with translation --- functions/ImageEnrichment/__init__.py | 4 ++-- functions/TextEnrichment/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/functions/ImageEnrichment/__init__.py b/functions/ImageEnrichment/__init__.py index 140755ea5..b9ba06247 100644 --- a/functions/ImageEnrichment/__init__.py +++ b/functions/ImageEnrichment/__init__.py @@ -68,7 +68,7 @@ MAX_CHARS_FOR_DETECTION = 1000 translator_api_headers = { - "Auhorization": f"Bearer {token_provider()}", + "Ocp-Apim-Subscription-Key": azure_ai_key, "Content-type": "application/json", "Ocp-Apim-Subscription-Region": azure_ai_location, } @@ -186,7 +186,7 @@ def main(msg: func.QueueMessage) -> None: files = {"file": image_data} response = requests.post(VISION_ENDPOINT, headers=vision_api_headers, - files=files) + data=image_data) if response.status_code == 200: result = response.json() diff --git a/functions/TextEnrichment/__init__.py b/functions/TextEnrichment/__init__.py index bed1e9f57..94751eb2b 100644 --- a/functions/TextEnrichment/__init__.py +++ b/functions/TextEnrichment/__init__.py @@ -29,6 +29,7 @@ cosmosdb_log_container_name = os.environ["COSMOSDB_LOG_CONTAINER_NAME"] text_enrichment_queue = os.environ["TEXT_ENRICHMENT_QUEUE"] azure_ai_endpoint = os.environ["AZURE_AI_ENDPOINT"] +azure_ai_key = os.environ["AZURE_AI_KEY"] targetTranslationLanguage = os.environ["TARGET_TRANSLATION_LANGUAGE"] max_requeue_count = int(os.environ["MAX_ENRICHMENT_REQUEUE_COUNT"]) enrichment_backoff = int(os.environ["ENRICHMENT_BACKOFF"]) @@ -124,7 +125,7 @@ def main(msg: func.QueueMessage) -> None: # detect language headers = { - 'Authorization': f'Bearer {token_provider()}', + "Ocp-Apim-Subscription-Key": azure_ai_key, 'Content-type': 'application/json', 'Ocp-Apim-Subscription-Region': azure_ai_location } From 2ae1d44100221c8d6fffd6c297504dce8cf0de71 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:05:10 +0100 Subject: [PATCH 27/33] Remove Video Indexer and ENABLE_MULTIMEDIA feature flag --- app/backend/app.py | 3 - app/backend/testsuite.py | 1 - app/frontend/src/api/models.ts | 1 - docs/deployment/deployment.md | 2 - docs/secure_deployment/secure_deployment.md | 2 - .../video_indexer/avi.template.json | 68 ----------------- infra/core/network/network/variables.tf | 4 - infra/core/videoindexer/variables.tf | 29 -------- infra/core/videoindexer/vi.tf | 73 ------------------- infra/main.tf | 33 +-------- infra/outputs.tf | 2 +- infra/variables.tf | 24 ------ .../AzureEnvironments/AzureCloud.env | 2 - .../AzureEnvironments/AzureUSGovernment.env | 2 - scripts/environments/local.env.example | 5 -- scripts/environments/shared-ia-dev.env | 7 -- scripts/environments/shared-ia.env | 7 -- scripts/environments/tmp-ia.env | 7 -- scripts/environments/usgov-ia.env | 7 -- scripts/inf-import-state.sh | 15 ---- scripts/json-to-env.webapp.debug.sh | 1 - scripts/load-env.sh | 6 -- scripts/prepare-tf-variables.sh | 2 - scripts/terraform-init.sh | 18 ----- 24 files changed, 4 insertions(+), 317 deletions(-) delete mode 100644 infra/arm_templates/video_indexer/avi.template.json delete mode 100644 infra/core/videoindexer/variables.tf delete mode 100644 infra/core/videoindexer/vi.tf diff --git a/app/backend/app.py b/app/backend/app.py index 5e2e00ea0..6886b2cf3 100644 --- a/app/backend/app.py +++ b/app/backend/app.py @@ -87,7 +87,6 @@ "ENABLE_UNGROUNDED_CHAT": "false", "ENABLE_MATH_ASSISTANT": "false", "ENABLE_TABULAR_DATA_ASSISTANT": "false", - "ENABLE_MULTIMEDIA": "false", "MAX_CSV_FILE_SIZE": "7", "LOCAL_DEBUG": "false", "AZURE_AI_CREDENTIAL_DOMAIN": "cognitiveservices.azure.com" @@ -849,14 +848,12 @@ async def get_feature_flags(): - "ENABLE_UNGROUNDED_CHAT": Flag indicating whether ungrounded chat is enabled. - "ENABLE_MATH_ASSISTANT": Flag indicating whether the math assistant is enabled. - "ENABLE_TABULAR_DATA_ASSISTANT": Flag indicating whether the tabular data assistant is enabled. - - "ENABLE_MULTIMEDIA": Flag indicating whether multimedia is enabled. """ response = { "ENABLE_WEB_CHAT": str_to_bool.get(ENV["ENABLE_WEB_CHAT"]), "ENABLE_UNGROUNDED_CHAT": str_to_bool.get(ENV["ENABLE_UNGROUNDED_CHAT"]), "ENABLE_MATH_ASSISTANT": str_to_bool.get(ENV["ENABLE_MATH_ASSISTANT"]), "ENABLE_TABULAR_DATA_ASSISTANT": str_to_bool.get(ENV["ENABLE_TABULAR_DATA_ASSISTANT"]), - "ENABLE_MULTIMEDIA": str_to_bool.get(ENV["ENABLE_MULTIMEDIA"]), } return response diff --git a/app/backend/testsuite.py b/app/backend/testsuite.py index 66308eef1..241aabfa8 100644 --- a/app/backend/testsuite.py +++ b/app/backend/testsuite.py @@ -309,7 +309,6 @@ def test_get_feature_flags(): "ENABLE_UNGROUNDED_CHAT": os.getenv("ENABLE_UNGROUNDED_CHAT") == "true", "ENABLE_MATH_ASSISTANT": os.getenv("ENABLE_MATH_ASSISTANT") == "true", "ENABLE_TABULAR_DATA_ASSISTANT": os.getenv("ENABLE_TABULAR_DATA_ASSISTANT") == "true", - "ENABLE_MULTIMEDIA": os.getenv("ENABLE_MULTIMEDIA") == "true", } assert response.json() == expected_response diff --git a/app/frontend/src/api/models.ts b/app/frontend/src/api/models.ts index 34a13191e..a518dce30 100644 --- a/app/frontend/src/api/models.ts +++ b/app/frontend/src/api/models.ts @@ -214,6 +214,5 @@ export type GetFeatureFlagsResponse = { ENABLE_UNGROUNDED_CHAT: boolean; ENABLE_MATH_ASSISTANT: boolean; ENABLE_TABULAR_DATA_ASSISTANT: boolean; - ENABLE_MULTIMEDIA: boolean; error?: string; } \ No newline at end of file diff --git a/docs/deployment/deployment.md b/docs/deployment/deployment.md index 5dbcc24b4..8a39f8057 100644 --- a/docs/deployment/deployment.md +++ b/docs/deployment/deployment.md @@ -50,8 +50,6 @@ Variable | Required | Description --- | --- | --- LOCATION | Yes | The location (West Europe is the default). The Terraform templates use this value. To get a list of all the current Azure regions you can run `az account list-locations -o table`. The value here needs to be the *Name* value and not *Display Name*. WORKSPACE | Yes | The workspace name (use something simple and unique to you). This will appended to infoasst-????? as the name of the resource group created in your subscription. -SUBSCRIPTION_ID | Yes | The GUID that represents the Azure Subscription you want the Accelerator to be deployed into. This can be obtained from the *Subscription* blade in the Azure Portal. -TENANT_ID | Yes | The GUID that represents the Azure Active Directory Tenant for the Subscription you want the accelerator to be deployed into. This can be obtained from the *Tenant Info* blade in the Azure Portal. AZURE_ENVIRONMENT | Yes | This will determine the Azure cloud environment the deployment will target. Information Assistant currently supports, AzureCloud and AzureUSGovernment. Info available at [Azure cloud environments](https://docs.microsoft.com/en-us/cli/azure/manage-clouds-azure-cli?toc=/cli/azure/toc.json&bc=/cli/azure/breadcrumb/toc.json). If you are targeting "AzureUSGovernment" please see our [sovereign deployment support documentation](/docs/deployment/enable_sovereign_deployment.md). SECURE_MODE | Yes | Defaults to `false`. This feature flag will determine if the Information Assistant deploys it's Azure Infrastructure in a secure mode or not.
:warning: Before enabling secure mode please read the extra instructions on [Enabling Secure Deployment](/docs/secure_deployment/secure_deployment.md) ENABLE_WEB_CHAT | Yes | Defaults to `false`. This feature flag will enable the ability to use Web Search results as a data source for generating answers from the LLM. This feature will also deploy a Bing v7 Search instance in Azure to retrieve web results from, however Bing v7 Search is not available in AzureUSGovernment regions, so this feature flag is **NOT** compatible with `AZURE_ENVIRONMENT=AzureUSGovernment`. diff --git a/docs/secure_deployment/secure_deployment.md b/docs/secure_deployment/secure_deployment.md index b028c4bb3..90fb4e557 100644 --- a/docs/secure_deployment/secure_deployment.md +++ b/docs/secure_deployment/secure_deployment.md @@ -8,7 +8,6 @@ > * Using an existing Azure OpenAI Services > * Web chat (secure endpoints for Bing API services are not yet available) > * SharePoint connector (secure endpoints for Azure Logic Apps and SharePoint connector for Logic Apps are not yet available) -> * Multimedia (secure endpoints for Azure Video Indexer services are not yet available) > >Secure mode requires a DDOS Protection Plan for Virtual Network Protection. There is a limit of 1 DDOS protection plan for a subscription in a region. You can reuse an existing DDOS plan in your tenant or Info Assistant can deploy one for you. > @@ -138,7 +137,6 @@ To perform a secure deployment, follow these steps: export ENABLE_WEB_CHAT=false export USE_EXISTING_AOAI=false export ENABLE_SHAREPOINT_CONNECTOR=false - export ENABLE_MULTIMEDIA=false ``` *Note: Secure mode is blocked when using an existing Azure OpenAI service. We have blocked this scenario to prevent updating a shared instance of Azure OpenAI that may be in use by other workloads* diff --git a/infra/arm_templates/video_indexer/avi.template.json b/infra/arm_templates/video_indexer/avi.template.json deleted file mode 100644 index 251401870..000000000 --- a/infra/arm_templates/video_indexer/avi.template.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "$schema": "${arm_template_schema_mgmt_api}/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "name": { - "type": "string", - "metadata" : { - "description" : "The name of the AVAM resource" - }, - "defaultValue": "avam-account" - }, - "managedIdentityId": { - "type": "string", - "metadata" : { - "description" : "The managed identity used to grant access to the Azure Storage account" - }, - "defaultValue": "" - }, - "storageServicesResourceId": { - "type": "string", - "metadata" : { - "description" : "The Storage Account Id for storing uploaded videos. The Account needs to be created prior to the creation of this template" - }, - "defaultValue" : "" - }, - "tags": { - "type": "object", - "metadata" : { - "description" : "Arm Template Tags" - }, - "defaultValue": {} - }, - "apiVersion": { - "type": "string", - "metadata": { - "description": "Video indexer api version" - } - } - }, - "resources": [ - { - "type": "Microsoft.VideoIndexer/accounts", - "apiVersion": "[parameters('apiVersion')]", - "name": "[parameters('name')]", - - "location": "[resourceGroup().location]", - "tags": "[parameters('tags')]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[parameters('managedIdentityId')]": {} - } - }, - "properties": { - "storageServices": { - "resourceId": "[parameters('storageServicesResourceId')]", - "userAssignedIdentity": "[parameters('managedIdentityId')]" - } - } - } - ], - "outputs": { - "avam_id": { - "value": "[reference(resourceId('Microsoft.VideoIndexer/accounts',parameters('name'))).accountId]", - "type": "string" - } - } -} \ No newline at end of file diff --git a/infra/core/network/network/variables.tf b/infra/core/network/network/variables.tf index 3e88d047f..d3b8a35d2 100644 --- a/infra/core/network/network/variables.tf +++ b/infra/core/network/network/variables.tf @@ -70,10 +70,6 @@ variable "snetSearchServiceCIDR" { type = string } -variable "snetAzureVideoIndexerCIDR" { - type = string -} - variable "snetBingServiceCIDR" { type = string } diff --git a/infra/core/videoindexer/variables.tf b/infra/core/videoindexer/variables.tf deleted file mode 100644 index ffe18e438..000000000 --- a/infra/core/videoindexer/variables.tf +++ /dev/null @@ -1,29 +0,0 @@ -variable "resource_group_name" { - type = string -} - -variable "location" { - type = string -} - -variable "random_string" { - type = string -} - -variable "tags" {} - -variable "azuread_service_principal_object_id" { - type = string -} - -variable "subscription_id" { - type = string -} - -variable "arm_template_schema_mgmt_api" { - type = string -} - -variable "video_indexer_api_version" { - type = string -} diff --git a/infra/core/videoindexer/vi.tf b/infra/core/videoindexer/vi.tf deleted file mode 100644 index 3dde8e9de..000000000 --- a/infra/core/videoindexer/vi.tf +++ /dev/null @@ -1,73 +0,0 @@ -locals { - arm_file_path = "arm_templates/video_indexer/avi.template.json" -} - -# Create a media services instance -resource "azurerm_storage_account" "media_storage" { - location = var.location - resource_group_name = var.resource_group_name - tags = var.tags - - account_tier = "Standard" - account_replication_type = "LRS" - name = "infoasststoremedia${var.random_string}" - enable_https_traffic_only = true - allow_nested_items_to_be_public = false -} - -# Create the VI instance via ARM Template -data "template_file" "workflow" { - template = file(local.arm_file_path) - vars = { - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - } -} - -resource "azurerm_user_assigned_identity" "vi" { - resource_group_name = var.resource_group_name - location = var.location - name = "infoasst-ua-ident-${var.random_string}" -} - -resource "azurerm_role_assignment" "vi_storageaccount_mi_access" { - scope = azurerm_storage_account.media_storage.id - role_definition_name = "Storage Blob Data Contributor" - principal_id = azurerm_user_assigned_identity.vi.principal_id -} - -resource "azurerm_resource_group_template_deployment" "vi" { - depends_on = [azurerm_role_assignment.vi_storageaccount_mi_access] - resource_group_name = var.resource_group_name - parameters_content = jsonencode({ - "name" = { value = "infoasst-avi-${var.random_string}" }, - "managedIdentityId" = { value = azurerm_user_assigned_identity.vi.id }, - "storageServicesResourceId" = { value = azurerm_storage_account.media_storage.id }, - "tags" = { value = var.tags }, - "apiVersion" = { value = var.video_indexer_api_version } - }) - template_content = data.template_file.workflow.template - # The filemd5 forces this to run when the file is changed - # this ensures the keys are up-to-date - name = "avi-${filemd5(local.arm_file_path)}" - deployment_mode = "Incremental" -} - -output "account_id" { - value = jsondecode(azurerm_resource_group_template_deployment.vi.output_content).avam_id.value -} - -output "media_storage_account_name" { - value = azurerm_storage_account.media_storage.name -} - -output "media_storage_account_id" { - value = azurerm_storage_account.media_storage.id -} - -output "vi_name" { - value = "infoasst-avi-${var.random_string}" -} - -output "vi_id" { - value = "/subscriptions/${var.subscription_id}/resourceGroups/${var.resource_group_name}/providers/Microsoft.VideoIndexer/accounts/infoasst-avi-${var.random_string}" -} \ No newline at end of file diff --git a/infra/main.tf b/infra/main.tf index 975f1e44e..d9f593c90 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -63,7 +63,6 @@ module "network" { snetEnrichmentCIDR = var.enrichment_app_CIDR snetIntegrationCIDR = var.integration_CIDR snetSearchServiceCIDR = var.search_service_CIDR - snetAzureVideoIndexerCIDR = var.azure_video_indexer_CIDR snetBingServiceCIDR = var.bing_service_CIDR snetAzureOpenAICIDR = var.azure_openAI_CIDR snetACRCIDR = var.acr_CIDR @@ -281,7 +280,7 @@ module "enrichmentApp" { kind = "linux" reserved = true resourceGroupName = azurerm_resource_group.rg.name - storageAccountId = "/subscriptions/${var.subscriptionId}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.Storage/storageAccounts/${module.storage.name}/services/queue/queues/${var.embeddingsQueue}" + storageAccountId = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.Storage/storageAccounts/${module.storage.name}/services/queue/queues/${var.embeddingsQueue}" scmDoBuildDuringDeployment = false enableOryxBuild = false managedIdentity = true @@ -356,7 +355,7 @@ module "webapp" { applicationInsightsConnectionString = module.logging.applicationInsightsConnectionString keyVaultUri = module.kvModule.keyVaultUri keyVaultName = module.kvModule.keyVaultName - tenantId = var.tenantId + tenantId = data.azurerm_client_config.current.tenant_id is_secure_mode = var.is_secure_mode subnet_name = var.is_secure_mode ? module.network[0].snetApp_name : null vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null @@ -411,7 +410,6 @@ module "webapp" { ENABLE_UNGROUNDED_CHAT = var.enableUngroundedChat ENABLE_MATH_ASSISTANT = var.enableMathAssitant ENABLE_TABULAR_DATA_ASSISTANT = var.enableTabularDataAssistant - ENABLE_MULTIMEDIA = var.enableMultimedia MAX_CSV_FILE_SIZE = var.maxCsvFileSize AZURE_AI_CREDENTIAL_DOMAIN = var.azure_ai_private_link_domain } @@ -632,27 +630,13 @@ module "sharepoint" { ] } -// Video Indexer is not supported in secure mode -module "video_indexer" { - count = var.is_secure_mode ? 0 : var.enableMultimedia ? 1 : 0 - source = "./core/videoindexer" - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - subscription_id = data.azurerm_client_config.current.subscription_id - random_string = random_string.random.result - tags = local.tags - azuread_service_principal_object_id = module.entraObjects.azure_ad_web_app_client_id - arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - video_indexer_api_version = var.video_indexer_api_version -} - module "azMonitor" { source = "./core/logging/monitor" logAnalyticsName = module.logging.logAnalyticsName location = var.location logWorkbookName = "infoasst-lw-${random_string.random.result}" resourceGroupName = azurerm_resource_group.rg.name - componentResource = "/subscriptions/${var.subscriptionId}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.OperationalInsights/workspaces/${module.logging.logAnalyticsName}" + componentResource = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${azurerm_resource_group.rg.name}/providers/Microsoft.OperationalInsights/workspaces/${module.logging.logAnalyticsName}" } // Bing Search is not supported in US Government or Secure Mode @@ -883,17 +867,6 @@ module "docIntel_StorageBlobDataReader" { resourceGroupId = azurerm_resource_group.rg.id } -module "aviRoleBackend" { - source = "./core/security/role" - count = var.enableMultimedia ? 1 : 0 - scope = module.video_indexer[0].vi_id - principalId = module.webapp.identityPrincipalId - roleDefinitionId = local.azure_roles.Contributor - principalType = "ServicePrincipal" - subscriptionId = data.azurerm_client_config.current.subscription_id - resourceGroupId = azurerm_resource_group.rg.id -} - # // MANAGEMENT SERVICE PRINCIPAL ROLES module "openAiRoleMgmt" { source = "./core/security/role" diff --git a/infra/outputs.tf b/infra/outputs.tf index 72b29a3b5..627fc18b3 100644 --- a/infra/outputs.tf +++ b/infra/outputs.tf @@ -112,7 +112,7 @@ output "ENABLE_DEV_CODE" { } output "AZURE_SUBSCRIPTION_ID" { - value = var.subscriptionId + value = data.azurerm_client_config.current.subscription_id } output "BLOB_STORAGE_ACCOUNT_ENDPOINT" { diff --git a/infra/variables.tf b/infra/variables.tf index d0ba64eb1..2b456d32c 100644 --- a/infra/variables.tf +++ b/infra/variables.tf @@ -13,16 +13,6 @@ variable "resourceGroupName" { default = "" } -variable "tenantId" { - type = string - default = "" -} - -variable "subscriptionId" { - type = string - default = "" -} - variable "buildNumber" { type = string default = "local" @@ -73,11 +63,6 @@ variable "enableSharePointConnector" { type = bool default = false } - -variable "enableMultimedia" { - type = bool - default = false -} //// //// Variables that can vary based on the Azure environment being targeted @@ -158,10 +143,6 @@ variable "azure_ai_document_intelligence_domain" { type = string } -variable "azure_ai_videoindexer_domain" { - type = string -} - variable "azure_bing_search_domain" { type = string } @@ -474,11 +455,6 @@ variable "functionsAppSkuTier" { default = "Standard" } -variable "videoIndexerName" { - type = string - default = "" -} - variable "searchServicesName" { type = string default = "" diff --git a/scripts/environments/AzureEnvironments/AzureCloud.env b/scripts/environments/AzureEnvironments/AzureCloud.env index 06b7efcb3..1c93db4c6 100644 --- a/scripts/environments/AzureEnvironments/AzureCloud.env +++ b/scripts/environments/AzureEnvironments/AzureCloud.env @@ -10,7 +10,6 @@ export TF_VAR_azure_sts_issuer_domain="sts.windows.net" export TF_VAR_azure_websites_domain="azurewebsites.net" export TF_VAR_azure_access_token_domain="login.microsoftonline.com" export TF_VAR_azure_arm_management_api="https://management.azure.com" -export TF_VAR_azure_avam_domain="https://www.videoindexer.ai" export TF_VAR_azure_keyvault_domain="vaultcore.azure.net" export TF_VAR_cosmosdb_domain="documents.azure.com" export TF_VAR_azure_monitor_domain="monitor.azure.com" @@ -18,7 +17,6 @@ export TF_VAR_azure_monitor_oms_domain="oms.opinsights.azure.com" export TF_VAR_azure_monitor_ods_domain="ods.opinsights.azure.com" export TF_VAR_azure_automation_domain="azure-automation.net" export TF_VAR_azure_ai_document_intelligence_domain="cognitiveservices.azure.com" -export TF_VAR_azure_ai_videoindexer_domain="api.videoindexer.ai" export TF_VAR_azure_bing_search_domain="api.bing.microsoft.com" export TF_VAR_azure_ai_private_link_domain="cognitiveservices.azure.com" export TF_VAR_azure_acr_domain="azurecr.io" \ No newline at end of file diff --git a/scripts/environments/AzureEnvironments/AzureUSGovernment.env b/scripts/environments/AzureEnvironments/AzureUSGovernment.env index e3c8cad6d..f55fd21d1 100644 --- a/scripts/environments/AzureEnvironments/AzureUSGovernment.env +++ b/scripts/environments/AzureEnvironments/AzureUSGovernment.env @@ -10,7 +10,6 @@ export TF_VAR_azure_sts_issuer_domain="login.microsoftonline.us" export TF_VAR_azure_websites_domain="azurewebsites.us" export TF_VAR_azure_access_token_domain="login.microsoftonline.us" export TF_VAR_azure_arm_management_api="https://management.usgovcloudapi.net" -export TF_VAR_azure_avam_domain="https://videoindexer.ai.azure.us" export TF_VAR_azure_keyvault_domain="vaultcore.usgovcloudapi.net" export TF_VAR_cosmosdb_domain="documents.azure.us" export TF_VAR_azure_monitor_domain="monitor.azure.us" @@ -18,7 +17,6 @@ export TF_VAR_azure_monitor_oms_domain="oms.opinsights.azure.us" export TF_VAR_azure_monitor_ods_domain="ods.opinsights.azure.us" export TF_VAR_azure_automation_domain="azure-automation.us" export TF_VAR_azure_ai_document_intelligence_domain="cognitiveservices.azure.us" -export TF_VAR_azure_ai_videoindexer_domain="api.videoindexer.ai.azure.us" export TF_VAR_azure_bing_search_domain="" #blank as Bing Search in not available in Azure Government export TF_VAR_azure_ai_private_link_domain="cognitiveservices.azure.us" export TF_VAR_azure_acr_domain="azurecr.us" diff --git a/scripts/environments/local.env.example b/scripts/environments/local.env.example index 35df53c60..b0d690bf4 100644 --- a/scripts/environments/local.env.example +++ b/scripts/environments/local.env.example @@ -5,8 +5,6 @@ # This is set by the Azure Pipeline for other environments. export LOCATION="westeurope" # Required export WORKSPACE="myworkspace" # Required -export SUBSCRIPTION_ID="" # Required -export TENANT_ID="" # Required # ---------------------------------------------------------- # The following values determine the features that are enabled in the deployment. @@ -140,9 +138,6 @@ export CUSTOMER_USAGE_ATTRIBUTION_ID="7a01ff74-15c2-4fec-9f14-63db7d3d6131" # Leave application title blank for the default name export APPLICATION_TITLE="" -# Video Indexer API Version used in ARM deployment of Azure Video Indexer -export VIDEO_INDEXER_API_VERSION="2024-01-01" - # Enable capabilities under development. This should be set to false export ENABLE_DEV_CODE=false diff --git a/scripts/environments/shared-ia-dev.env b/scripts/environments/shared-ia-dev.env index b8595e90f..d03bfe5c2 100644 --- a/scripts/environments/shared-ia-dev.env +++ b/scripts/environments/shared-ia-dev.env @@ -42,10 +42,6 @@ export ENABLE_SHAREPOINT_CONNECTOR=true # The forward slash for at the beginning of the folder is required i.e. "/Shared Documents" # Specifying the root folder of "/Shared Documents" will crawl all your documents in your sharepoint site export SHAREPOINT_TO_SYNC='' -# Update to "true" if you want to deploy the solution with the ability to use the Multimedia feature. -# This feature will allow users to use the Azure Video Indexer feature to process video and audio files. -# Defaults to false, as this is a future feature. -export ENABLE_MULTIMEDIA=false # ---------------------------------------------------------- # End of feature flags # ---------------------------------------------------------- @@ -91,9 +87,6 @@ export CUSTOMER_USAGE_ATTRIBUTION_ID="" export ENABLE_DEV_CODE=false -# Video Indexer API Version used in ARM deployment of Azure Video Indexer -export VIDEO_INDEXER_API_VERSION="2024-01-01" - export PASSWORD_LIFETIME=365 export ENABLE_DDOS_PROTECTION_PLAN=false \ No newline at end of file diff --git a/scripts/environments/shared-ia.env b/scripts/environments/shared-ia.env index d94dc80a9..ed626265e 100644 --- a/scripts/environments/shared-ia.env +++ b/scripts/environments/shared-ia.env @@ -42,10 +42,6 @@ export ENABLE_SHAREPOINT_CONNECTOR=true # The forward slash for at the beginning of the folder is required i.e. "/Shared Documents" # Specifying the root folder of "/Shared Documents" will crawl all your documents in your sharepoint site export SHAREPOINT_TO_SYNC='' -# Update to "true" if you want to deploy the solution with the ability to use the Multimedia feature. -# This feature will allow users to use the Azure Video Indexer feature to process video and audio files. -# Defaults to false, as this is a future feature. -export ENABLE_MULTIMEDIA=false # ---------------------------------------------------------- # End of feature flags # ---------------------------------------------------------- @@ -91,9 +87,6 @@ export CUSTOMER_USAGE_ATTRIBUTION_ID="" export ENABLE_DEV_CODE=false -# Video Indexer API Version used in ARM deployment of Azure Video Indexer -export VIDEO_INDEXER_API_VERSION="2024-01-01" - export PASSWORD_LIFETIME=365 export ENABLE_DDOS_PROTECTION_PLAN=false \ No newline at end of file diff --git a/scripts/environments/tmp-ia.env b/scripts/environments/tmp-ia.env index ca32f8c6d..c3ce41fcb 100644 --- a/scripts/environments/tmp-ia.env +++ b/scripts/environments/tmp-ia.env @@ -42,10 +42,6 @@ export ENABLE_SHAREPOINT_CONNECTOR=true # The forward slash for at the beginning of the folder is required i.e. "/Shared Documents" # Specifying the root folder of "/Shared Documents" will crawl all your documents in your sharepoint site export SHAREPOINT_TO_SYNC='' -# Update to "true" if you want to deploy the solution with the ability to use the Multimedia feature. -# This feature will allow users to use the Azure Video Indexer feature to process video and audio files. -# Defaults to false, as this is a future feature. -export ENABLE_MULTIMEDIA=false # ---------------------------------------------------------- # End of feature flags # ---------------------------------------------------------- @@ -91,9 +87,6 @@ export CUSTOMER_USAGE_ATTRIBUTION_ID="" export ENABLE_DEV_CODE=false -# Video Indexer API Version used in ARM deployment of Azure Video Indexer -export VIDEO_INDEXER_API_VERSION="2024-01-01" - export PASSWORD_LIFETIME=365 export ENABLE_DDOS_PROTECTION_PLAN=false \ No newline at end of file diff --git a/scripts/environments/usgov-ia.env b/scripts/environments/usgov-ia.env index 68a4ef0b6..d6836c01a 100644 --- a/scripts/environments/usgov-ia.env +++ b/scripts/environments/usgov-ia.env @@ -42,10 +42,6 @@ export ENABLE_SHAREPOINT_CONNECTOR=true # The forward slash for at the beginning of the folder is required i.e. "/Shared Documents" # Specifying the root folder of "/Shared Documents" will crawl all your documents in your sharepoint site export SHAREPOINT_TO_SYNC='' -# Update to "true" if you want to deploy the solution with the ability to use the Multimedia feature. -# This feature will allow users to use the Azure Video Indexer feature to process video and audio files. -# Defaults to false, as this is a future feature. -export ENABLE_MULTIMEDIA=false # ---------------------------------------------------------- # End of feature flags # ---------------------------------------------------------- @@ -90,9 +86,6 @@ export CUSTOMER_USAGE_ATTRIBUTION_ID="" export ENABLE_DEV_CODE=false -# Video Indexer API Version used in ARM deployment of Azure Video Indexer -export VIDEO_INDEXER_API_VERSION="2024-01-01" - export PASSWORD_LIFETIME=365 export ENABLE_DDOS_PROTECTION_PLAN=false \ No newline at end of file diff --git a/scripts/inf-import-state.sh b/scripts/inf-import-state.sh index 231e8977c..552975224 100755 --- a/scripts/inf-import-state.sh +++ b/scripts/inf-import-state.sh @@ -335,21 +335,6 @@ module_path="module.entraObjects.azuread_service_principal.aad_mgmt_sp[0]" import_resource_if_needed $module_path "$sp_id" -# # Video Indexer -# echo -# figlet "Video Indexer" -# # Pelase note: we do not import vi state as a hotfix was pushed to main to not deploy vi due to -# # changes in the service in azure. -# name="infoasststoremedia$random_text" -# providers="/providers/Microsoft.Storage/storageAccounts/$name" -# module_path="module.video_indexer.azurerm_storage_account.media_storage" -# import_resource_if_needed $module_path "$resourceId$providers" -# name="infoasst-ua-ident-$random_text" -# providers="/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$name" -# module_path="module.video_indexer.azurerm_user_assigned_identity.vi" -# import_resource_if_needed $module_path "$resourceId$providers" - - # Form Recognizer echo figlet "Form Recognizer" diff --git a/scripts/json-to-env.webapp.debug.sh b/scripts/json-to-env.webapp.debug.sh index 4d585fab2..bd6c3a89c 100755 --- a/scripts/json-to-env.webapp.debug.sh +++ b/scripts/json-to-env.webapp.debug.sh @@ -190,7 +190,6 @@ jq -r ' echo "ENABLE_UNGROUNDED_CHAT=$ENABLE_UNGROUNDED_CHAT" echo "ENABLE_MATH_ASSISTANT=$ENABLE_MATH_ASSISTANT" echo "ENABLE_TABULAR_DATA_ASSISTANT=$ENABLE_TABULAR_DATA_ASSISTANT" - echo "ENABLE_MULTIMEDIA=$ENABLE_MULTIMEDIA" echo "LOCAL_DEBUG=true" if [ -n "${IN_AUTOMATION}" ]; then diff --git a/scripts/load-env.sh b/scripts/load-env.sh index d7db3bcb7..ddd2f2763 100755 --- a/scripts/load-env.sh +++ b/scripts/load-env.sh @@ -76,12 +76,6 @@ if [[ $SECURE_MODE == true && $USE_EXISTING_AOAI == true ]]; then exit 1 fi -if [[ $SECURE_MODE == true && $ENABLE_MULTIMEDIA == true ]]; then - echo -e "\n" - echo -e "Multimedia feature is not available in secure mode. Check your values for SECURE_MODE and ENABLE_MULTIMEDIA.\e[0m\n" - exit 1 -fi - #SharePoint if [[ $SECURE_MODE == true && $ENABLE_SHAREPOINT_CONNECTOR == true ]]; then echo -e "\n" diff --git a/scripts/prepare-tf-variables.sh b/scripts/prepare-tf-variables.sh index ed2509fbe..dc35493ad 100755 --- a/scripts/prepare-tf-variables.sh +++ b/scripts/prepare-tf-variables.sh @@ -8,8 +8,6 @@ ENV_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # To maintain backward compatibility, we need to convert some of the variables to TF_VAR_ format export TF_VAR_environmentName=$WORKSPACE export TF_VAR_location=$LOCATION -export TF_VAR_tenantId=$TENANT_ID -export TF_VAR_subscriptionId=$SUBSCRIPTION_ID export TF_VAR_useExistingAOAIService=$USE_EXISTING_AOAI export TF_VAR_azureOpenAIResourceGroup=$AZURE_OPENAI_RESOURCE_GROUP export TF_VAR_azureOpenAIServiceName=$AZURE_OPENAI_SERVICE_NAME diff --git a/scripts/terraform-init.sh b/scripts/terraform-init.sh index d8b7859c7..bcc8f5376 100755 --- a/scripts/terraform-init.sh +++ b/scripts/terraform-init.sh @@ -39,24 +39,6 @@ function finish { } trap finish EXIT -# Default to user az cli if not set -# if [ -z $ARM_SUBSCRIPTION_ID ] || [ -z $ARM_TENANT_ID ]; -# then -# printf "$YELLOW\nCredentials for terraform not provided. Do you want to continue using your az login? (Y/n)$RESET\n" -# read answer -# if [[ "$answer" == "Y" ]]; -# then -# export ARM_SUBSCRIPTION_ID=$(az account show --query id --output tsv) -# export ARM_TENANT_ID=$(az account show --query tenantId --output tsv) - -# echo "Using subscription id: $ARM_SUBSCRIPTION_ID" -# echo "Using tenant id: $ARM_TENANT_ID" -# fi - -# fi - - - if [ -n "${IN_AUTOMATION}" ] then if [ -n "${AZURE_ENVIRONMENT}" ] && [[ "$AZURE_ENVIRONMENT" == "AzureUSGovernment" ]]; then From a339530a1e4114fd4678d9ba4d6d9e960fa4e795 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:10:16 +0100 Subject: [PATCH 28/33] remove auto update for npm --- scripts/build.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index 4f504adc3..f1c62d6e0 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -25,7 +25,6 @@ rm -rf ${BINARIES_OUTPUT_PATH} && mkdir -p ${BINARIES_OUTPUT_PATH} #Build the AzLib that contains the JavaScript functions that enable the upload feature cd app/frontend -npm audit fix npm install npm run build From 8de5f2c92b66e28e3652e5a29a11570af6686c9a Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:58:24 +0100 Subject: [PATCH 29/33] Move storage back to storage subnet and fix name of kv secret --- infra/core/host/functions/functions.tf | 5 +++-- infra/core/host/functions/variables.tf | 5 ----- infra/core/storage/storage-account.tf | 2 +- infra/main.tf | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/infra/core/host/functions/functions.tf b/infra/core/host/functions/functions.tf index 4092ba739..65772604d 100644 --- a/infra/core/host/functions/functions.tf +++ b/infra/core/host/functions/functions.tf @@ -81,6 +81,7 @@ data "azurerm_storage_account" "existing_sa" { name = var.blobStorageAccountName resource_group_name = var.resourceGroupName } + // Create function app resource resource "azurerm_linux_function_app" "function_app" { name = var.name @@ -110,7 +111,7 @@ resource "azurerm_linux_function_app" "function_app" { container_registry_use_managed_identity = true always_on = true http2_enabled = true - ftps_state = var.is_secure_mode ? "Disabled" : var.ftpsState + ftps_state = "Disabled" cors { allowed_origins = concat([var.azure_portal_domain, "https://ms.portal.azure.com"], var.allowedOrigins) } @@ -128,7 +129,7 @@ resource "azurerm_linux_function_app" "function_app" { SCM_DO_BUILD_DURING_DEPLOYMENT = "false" ENABLE_ORYX_BUILD = "false" #Set all connections to use Managed Identity instead of connection strings - AzureWebJobsStorage = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-STORAGE-CONECTION-STRING)" + AzureWebJobsStorage = "@Microsoft.KeyVault(SecretUri=${var.keyVaultUri}secrets/AZURE-STORAGE-CONNECTION-STRING)" # These will need to be enabled one Azure Functions has support for Managed Identity #AzureWebJobsStorage__blobServiceUri = "https://${var.blobStorageAccountName}.blob.${var.endpointSuffix}" #AzureWebJobsStorage__queueServiceUri = "https://${var.blobStorageAccountName}.queue.${var.endpointSuffix}" diff --git a/infra/core/host/functions/variables.tf b/infra/core/host/functions/variables.tf index dad4f1474..38aa7904e 100644 --- a/infra/core/host/functions/variables.tf +++ b/infra/core/host/functions/variables.tf @@ -279,11 +279,6 @@ variable "managedIdentity" { default = false } -variable "ftpsState" { - type = string - default = "FtpsOnly" -} - variable "azure_portal_domain" { type = string } diff --git a/infra/core/storage/storage-account.tf b/infra/core/storage/storage-account.tf index 97ad81ff3..1ee8c5395 100644 --- a/infra/core/storage/storage-account.tf +++ b/infra/core/storage/storage-account.tf @@ -188,7 +188,7 @@ module "storage_connection_string" { resourceGroupName = var.resourceGroupName arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api key_vault_name = var.key_vault_name - secret_name = "AZURE-STORAGE-CONECTION-STRING" + secret_name = "AZURE-STORAGE-CONNECTION-STRING" secret_value = azurerm_storage_account.storage.primary_blob_connection_string tags = var.tags alias = "blobconnstring" diff --git a/infra/main.tf b/infra/main.tf index d9f593c90..129bc21d0 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -236,7 +236,7 @@ module "storage" { containers = ["content","website","upload","function","logs","config"] queueNames = ["pdf-submit-queue","pdf-polling-queue","non-pdf-submit-queue","media-submit-queue","text-enrichment-queue","image-enrichment-queue","embeddings-queue"] is_secure_mode = var.is_secure_mode - subnet_name = var.is_secure_mode ? module.network[0].snetFunction_name : null + subnet_name = var.is_secure_mode ? module.network[0].snetStorage_name : null vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneStorageAccountBlob[0].privateDnsZoneResourceId, module.privateDnsZoneStorageAccountFile[0].privateDnsZoneResourceId, From afb64b98831685eb1534519efaa608c6931256be Mon Sep 17 00:00:00 2001 From: bjakems <165402330+bjakems@users.noreply.github.com> Date: Tue, 13 Aug 2024 19:27:45 +0100 Subject: [PATCH 30/33] Fixing secret name --- scripts/json-to-env.function.debug.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/json-to-env.function.debug.sh b/scripts/json-to-env.function.debug.sh index 37f0ea359..2c9d4af27 100755 --- a/scripts/json-to-env.function.debug.sh +++ b/scripts/json-to-env.function.debug.sh @@ -20,8 +20,8 @@ secrets="{" keyVaultName=$(cat inf_output.json | jq -r .AZURE_KEYVAULT_NAME.value) # Names of your secrets -secretNames=("AZURE-AI-KEY" "AZURE-STORAGE-CONECTION-STRING") -azWebJobSecretName="AZURE-STORAGE-CONECTION-STRING" +secretNames=("AZURE-AI-KEY" "AZURE-STORAGE-CONNECTION-STRING") +azWebJobSecretName="AZURE-STORAGE-CONNECTION-STRING" azWebJobVarName="AzureWebJobsStorage" # Retrieve and export each secret From b847a15793476e26f2bf787a9c83398781cfb999 Mon Sep 17 00:00:00 2001 From: dayland <48474707+dayland@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:08:00 +0100 Subject: [PATCH 31/33] update KV secrets to have content type & expiration dates compliant with BAPS360SecurityPolicy --- infra/arm_templates/kv_secret/kv_secret.template.json | 6 +++++- infra/core/ai/bingSearch/bingSearch.tf | 1 + infra/core/ai/cogServices/cogServices.tf | 1 + infra/core/ai/docintelligence/variables.tf | 5 ----- infra/core/ai/openaiservices/variables.tf | 5 ----- infra/core/db/variables.tf | 5 ----- infra/core/search/variables.tf | 5 ----- infra/core/security/keyvault/variables.tf | 5 ----- infra/core/security/keyvaultSecret/keyvaultSecret.tf | 1 + infra/core/security/keyvaultSecret/variables.tf | 4 ++++ infra/core/storage/storage-account.tf | 1 + infra/main.tf | 5 ----- scripts/environments/local.env.example | 2 +- 13 files changed, 14 insertions(+), 32 deletions(-) diff --git a/infra/arm_templates/kv_secret/kv_secret.template.json b/infra/arm_templates/kv_secret/kv_secret.template.json index c27d690cc..bb204c703 100644 --- a/infra/arm_templates/kv_secret/kv_secret.template.json +++ b/infra/arm_templates/kv_secret/kv_secret.template.json @@ -16,6 +16,9 @@ }, "expiration": { "type": "string" + }, + "contentType": { + "type": "string" } }, "resources": [ @@ -29,7 +32,8 @@ "attributes": { "enabled": true, "exp": "[parameters('expiration')]" - } + }, + "contentType": "[parameters('contentType')]" } } ] diff --git a/infra/core/ai/bingSearch/bingSearch.tf b/infra/core/ai/bingSearch/bingSearch.tf index 0ca882ddf..e980e1c87 100644 --- a/infra/core/ai/bingSearch/bingSearch.tf +++ b/infra/core/ai/bingSearch/bingSearch.tf @@ -35,4 +35,5 @@ module "bing_search_key" { alias = "bingkey" tags = var.tags kv_secret_expiration = var.kv_secret_expiration + contentType = "application/vnd.bag-StrongEncPasswordString" } \ No newline at end of file diff --git a/infra/core/ai/cogServices/cogServices.tf b/infra/core/ai/cogServices/cogServices.tf index 0593e15e5..0214bb34b 100644 --- a/infra/core/ai/cogServices/cogServices.tf +++ b/infra/core/ai/cogServices/cogServices.tf @@ -19,6 +19,7 @@ module "cog_service_key" { alias = "aisvckey" tags = var.tags kv_secret_expiration = var.kv_secret_expiration + contentType = "application/vnd.bag-StrongEncPasswordString" } data "azurerm_subnet" "subnet" { diff --git a/infra/core/ai/docintelligence/variables.tf b/infra/core/ai/docintelligence/variables.tf index f040b0e81..2c74f3820 100644 --- a/infra/core/ai/docintelligence/variables.tf +++ b/infra/core/ai/docintelligence/variables.tf @@ -58,9 +58,4 @@ variable "subnet_name" { variable "arm_template_schema_mgmt_api" { type = string -} - -variable "kv_secret_expiration" { - type = string - description = "The value for key vault secret expiration in seconds since 1970-01-01T00:00:00Z" } \ No newline at end of file diff --git a/infra/core/ai/openaiservices/variables.tf b/infra/core/ai/openaiservices/variables.tf index 47657659a..c1d110d33 100644 --- a/infra/core/ai/openaiservices/variables.tf +++ b/infra/core/ai/openaiservices/variables.tf @@ -82,11 +82,6 @@ variable "arm_template_schema_mgmt_api" { type = string } -variable "kv_secret_expiration" { - type = string - description = "The value for key vault secret expiration in seconds since 1970-01-01T00:00:00Z" -} - variable "logAnalyticsWorkspaceResourceId" { type = string } \ No newline at end of file diff --git a/infra/core/db/variables.tf b/infra/core/db/variables.tf index 5b91a4c3b..5de215019 100644 --- a/infra/core/db/variables.tf +++ b/infra/core/db/variables.tf @@ -84,9 +84,4 @@ variable "subnet_name" { variable "arm_template_schema_mgmt_api" { type = string -} - -variable "kv_secret_expiration" { - type = string - description = "The value for key vault secret expiration in seconds since 1970-01-01T00:00:00Z" } \ No newline at end of file diff --git a/infra/core/search/variables.tf b/infra/core/search/variables.tf index 9b3ec3fa2..3e0cba828 100644 --- a/infra/core/search/variables.tf +++ b/infra/core/search/variables.tf @@ -62,9 +62,4 @@ variable "private_dns_zone_ids" { variable "arm_template_schema_mgmt_api" { type = string -} - -variable "kv_secret_expiration" { - type = string - description = "The value for key vault secret expiration in seconds since 1970-01-01T00:00:00Z" } \ No newline at end of file diff --git a/infra/core/security/keyvault/variables.tf b/infra/core/security/keyvault/variables.tf index 0ad56e79e..9671fc795 100644 --- a/infra/core/security/keyvault/variables.tf +++ b/infra/core/security/keyvault/variables.tf @@ -57,9 +57,4 @@ variable "azure_keyvault_domain" { variable "arm_template_schema_mgmt_api" { type = string -} - -variable "kv_secret_expiration" { - type = string - description = "The value for key vault secret expiration in seconds since 1970-01-01T00:00:00Z" } \ No newline at end of file diff --git a/infra/core/security/keyvaultSecret/keyvaultSecret.tf b/infra/core/security/keyvaultSecret/keyvaultSecret.tf index 2e5dcc803..eddb86a50 100644 --- a/infra/core/security/keyvaultSecret/keyvaultSecret.tf +++ b/infra/core/security/keyvaultSecret/keyvaultSecret.tf @@ -18,6 +18,7 @@ resource "azurerm_resource_group_template_deployment" "kv_secret" { "value" = { value = "${var.secret_value}" }, "tags" = { value = var.tags }, "expiration" = { value = var.kv_secret_expiration }, + "contentType" = { value = var.contentType }, }) template_content = data.template_file.workflow.template # The filemd5 forces this to run when the file is changed diff --git a/infra/core/security/keyvaultSecret/variables.tf b/infra/core/security/keyvaultSecret/variables.tf index fae0ad97c..cdf3bdc1e 100644 --- a/infra/core/security/keyvaultSecret/variables.tf +++ b/infra/core/security/keyvaultSecret/variables.tf @@ -31,4 +31,8 @@ variable "alias" { variable "kv_secret_expiration" { type = string description = "The value for key vault secret expiration in seconds since 1970-01-01T00:00:00Z" +} + +variable "contentType" { + type = string } \ No newline at end of file diff --git a/infra/core/storage/storage-account.tf b/infra/core/storage/storage-account.tf index 1ee8c5395..90fb7c4f4 100644 --- a/infra/core/storage/storage-account.tf +++ b/infra/core/storage/storage-account.tf @@ -193,6 +193,7 @@ module "storage_connection_string" { tags = var.tags alias = "blobconnstring" kv_secret_expiration = var.kv_secret_expiration + contentType = "application/vnd.ms-StorageConnectionString" } data "azurerm_subnet" "subnet" { diff --git a/infra/main.tf b/infra/main.tf index 129bc21d0..b7682d2e4 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -263,7 +263,6 @@ module "kvModule" { depends_on = [ module.entraObjects, module.privateDnsZoneKeyVault[0] ] azure_keyvault_domain = var.azure_keyvault_domain arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - kv_secret_expiration = var.kv_secret_expiration } module "enrichmentApp" { @@ -505,7 +504,6 @@ module "openaiServices" { private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneAzureOpenAi[0].privateDnsZoneResourceId] : null arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api key_vault_name = module.kvModule.keyVaultName - kv_secret_expiration = var.kv_secret_expiration logAnalyticsWorkspaceResourceId = module.logging.logAnalyticsId deployments = [ @@ -547,7 +545,6 @@ module "aiDocIntelligence" { vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneAzureAi[0].privateDnsZoneResourceId] : null arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - kv_secret_expiration = var.kv_secret_expiration } module "cognitiveServices" { @@ -580,7 +577,6 @@ module "searchServices" { private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneSearchService[0].privateDnsZoneResourceId] : null arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api key_vault_name = module.kvModule.keyVaultName - kv_secret_expiration = var.kv_secret_expiration } module "cosmosdb" { @@ -597,7 +593,6 @@ module "cosmosdb" { vnet_name = var.is_secure_mode ? module.network[0].vnet_name : null private_dns_zone_ids = var.is_secure_mode ? [module.privateDnsZoneCosmosDb[0].privateDnsZoneResourceId] : null arm_template_schema_mgmt_api = var.arm_template_schema_mgmt_api - kv_secret_expiration = var.kv_secret_expiration } module "acr"{ diff --git a/scripts/environments/local.env.example b/scripts/environments/local.env.example index b0d690bf4..2aec77f83 100644 --- a/scripts/environments/local.env.example +++ b/scripts/environments/local.env.example @@ -57,7 +57,7 @@ export REQUIRE_WEBSITE_SECURITY_MEMBERSHIP=false # Required # with Microsoft's recommended guardrails for Azure Key Vault policy. We have NOT included automatic secret rotation in this deployment. See # https://learn.microsoft.com/en-us/azure/key-vault/keys/how-to-configure-key-rotation for more information on enabling cryptographic key auto-rotation. # The following setting will set the secret expiration to the current day plus the number of days specified. -export SECRET_EXPIRATION_DAYS=120 # Required +export SECRET_EXPIRATION_DAYS=730 # Required # Uncomment this if you want to avoid the "are you sure?" prompt when applying TF changes # export SKIP_PLAN_CHECK=1 From 3471b5e1925d8abf007b871673dd2bcc7d5f0aa8 Mon Sep 17 00:00:00 2001 From: Corina McCleary <98787845+cmccleary8@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:20:54 +0100 Subject: [PATCH 32/33] Updated process flow diagram to use Azure OpenAI logo and added in additional Azure AI services used in solution. Added Shared Responsibility and Microsoft Legal Notice to Readme. --- README.md | 76 ++++++++++++++++++------------ SECURITY.md | 6 +-- SUPPORT.md | 10 ++-- docs/process_flow_chat.drawio.png | Bin 1988090 -> 1871050 bytes 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index d4200b964..a80617b89 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,34 @@ -# Information Assistant Accelerator +# Information Assistant (IA) accelerator > [!IMPORTANT] > As of November 15, 2023, Azure Cognitive Search has been renamed to Azure AI Search. Azure Cognitive Services have also been renamed to Azure AI Services. ## Table of Contents -- [Response Generation Approaches](#response-generation-approaches) +- [Response generation approaches](#response-generation-approaches) - [Features](#features) - [Azure account requirements](#azure-account-requirements) -- [Azure Deployment](./docs/deployment/deployment.md) - - [GitHub Codespaces Setup](./docs/deployment/deployment.md#development-environment-configuration) - - [Cost Estimation](./docs/deployment/deployment.md#sizing-estimator) +- [Azure deployment](./docs/deployment/deployment.md) + - [GitHub Codespaces setup](./docs/deployment/deployment.md#development-environment-configuration) + - [Cost estimation](./docs/deployment/deployment.md#sizing-estimator) - [Configuring ENV parameters](./docs/deployment/deployment.md#configure-env-files) - [Authenticating to Azure](./docs/deployment/deployment.md#log-into-azure-using-the-azure-cli) - [Deploying to Azure](./docs/deployment/deployment.md#deploy-and-configure-azure-resources) - - [Troubleshooting Common Issues](./docs/deployment/troubleshooting.md) - - [Considerations for Production Adoption](./docs/deployment/considerations_production.md) -- [Secure-Mode Deployment](./docs/secure_deployment/secure_deployment.md) + - [Troubleshooting common issues](./docs/deployment/troubleshooting.md) + - [Considerations for production adoption](./docs/deployment/considerations_production.md) +- [Secure-mode deployment](./docs/secure_deployment/secure_deployment.md) - [Enabling optional features](./docs/features/optional_features.md) - [Using the app](/docs/deployment/using_ia_first_time.md) - [Responsible AI](#responsible-ai) - [Transparency Note](#transparency-note) - [Content Safety](#content-safety) - [Data Collection Notice](#data-collection-notice) +- [Shared responsibility and customer responsibilities](#shared-responsibility-and-customer-responsibilities) - [Resources](#resources) - - [Known Issues](./docs/knownissues.md) - - [Functional Tests](./tests/README.md) + - [Known issues](./docs/knownissues.md) + - [Functional tests](./tests/README.md) - [Navigating the source code](#navigating-the-source-code) - - [Architectural Decisions](/docs/features/architectural_decisions.md) + - [Architectural decisions](/docs/features/architectural_decisions.md) - [References](#references) - [Trademarks](#trademarks) - [Code of Conduct](#code-of-conduct) @@ -36,23 +37,23 @@ [![Open in GitHub Codespaces](https://img.shields.io/static/v1?style=for-the-badge&label=GitHub+Codespaces&message=Open&color=brightgreen&logo=github)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=601652366&machine=basicLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=eastus) -This industry accelerator showcases integration between Azure and OpenAI's large language models. It leverages Azure AI Search for data retrieval and ChatGPT-style Q&A interactions. Using the Retrieval Augmented Generation (RAG) design pattern with Azure OpenAI's GPT models, it provides a natural language interaction to discover relevant responses to user queries. Azure AI Search simplifies data ingestion, transformation, indexing, and multilingual translation. +Information Assistant (IA) is an industry accelerator that showcases integration between Azure and OpenAI's large language models. It leverages Azure AI Search for data retrieval and ChatGPT-style Q&A interactions. Using the Retrieval Augmented Generation (RAG) design pattern with Azure OpenAI's GPT models, it provides a natural language interaction to discover relevant responses to user queries. Azure AI Search simplifies data ingestion, transformation, indexing, and multilingual translation. The accelerator adapts prompts based on the model type for enhanced performance. Users can customize settings like temperature and persona for personalized AI interactions. It offers features like explainable thought processes, referenceable citations, and direct content for verification. Please [see this video](https://aka.ms/InfoAssist/video) for use cases that may be achievable with this accelerator. -# Response Generation Approaches +# Response generation approaches -## Work(Grounded) -It utilizes a retrieval-augmented generation (RAG) pattern to generate responses grounded in specific data sourced from your own dataset. By combining retrieval of relevant information with generative capabilities, It can produce responses that are not only contextually relevant but also grounded in verified data. The RAG pipeline accesses your dataset to retrieve relevant information before generating responses, ensuring accuracy and reliability. Additionally, each response includes a citation to the document chunk from which the answer is derived, providing transparency and allowing users to verify the source. This approach is particularly advantageous in domains where precision and factuality are paramount. Users can trust that the responses generated are based on reliable data sources, enhancing the credibility and usefulness of the application. Specific information on our Grounded (RAG) can be found in [RAG](docs/features/cognitive_search.md#azure-ai-search-integration) +## Work (Grounded) +It utilizes a retrieval-augmented generation (RAG) pattern to generate responses grounded in specific data sourced from your own dataset. By combining retrieval of relevant information with generative capabilities, it can produce responses that are not only contextually relevant but also grounded in verified data. The RAG pipeline accesses your dataset to retrieve relevant information before generating responses, ensuring accuracy and reliability. Additionally, each response includes a citation to the document chunk from which the answer is derived, providing transparency and allowing users to verify the source. This approach is particularly advantageous in domains where precision and factuality are paramount. Users can trust that the responses generated are based on reliable data sources, enhancing the credibility and usefulness of the application. Specific information on our Grounded (RAG) can be found in [RAG](docs/features/cognitive_search.md#azure-ai-search-integration). ## Ungrounded It leverages the capabilities of a large language model (LLM) to generate responses in an ungrounded manner, without relying on external data sources or retrieval-augmented generation techniques. The LLM has been trained on a vast corpus of text data, enabling it to generate coherent and contextually relevant responses solely based on the input provided. This approach allows for open-ended and creative generation, making it suitable for tasks such as ideation, brainstorming, and exploring hypothetical scenarios. It's important to note that the generated responses are not grounded in specific factual data and should be evaluated critically, especially in domains where accuracy and verifiability are paramount. ## Work and Web -It offers 3 response options: one generated through our retrieval-augmented generation (RAG) pipeline, and the other grounded in content directly from the web. When users opt for the RAG response, they receive a grounded answer sourced from your data, complete with citations to document chunks for transparency and verification. Conversely, selecting the web response provides access to a broader range of sources, potentially offering more diverse perspectives. Each web response is grounded in content from the web accompanied by citations of web links, allowing users to explore the original sources for further context and validation. Upon request, It can also generate a final response that compares and contrasts both responses. This comparative analysis allows users to make informed decisions based on the reliability, relevance, and context of the information provided. -Specific information about our Grounded and Web can be found in [Web](/docs/features/features.md#bing-search-and-compare) +It offers 2 response options: one generated through our retrieval-augmented generation (RAG) pipeline, and the other grounded in content directly from the web. When users opt for the RAG response, they receive a grounded answer sourced from their data, complete with citations to document chunks for transparency and verification. Conversely, selecting the web response provides access to a broader range of sources, potentially offering more diverse perspectives. Each web response is grounded in content from the web accompanied by citations of web links, allowing users to explore the original sources for further context and validation. Upon request, It can also generate a final response that compares and contrasts both responses. This comparative analysis allows users to make informed decisions based on the reliability, relevance, and context of the information provided. +Specific information about our Work and Web can be found in [Web](/docs/features/features.md#bing-search-and-compare). ## Assistants It generates response by using LLM as a reasoning engine. The key strength lies in agent's ability to autonomously reason about tasks, decompose them into steps, and determine the appropriate tools and data sources to leverage, all without the need for predefined task definitions or rigid workflows. This approach allows for a dynamic and adaptive response generation process without predefining set of tasks. It harnesses the capabilities of LLM to understand natural language queries and generate responses tailored to specific tasks. These Agents are being released in preview mode as we continue to evaluate and mitigate the potential risks associated with autonomous reasoning, such as misuse of external tools, lack of transparency, biased outputs, privacy concerns, and remote code execution vulnerabilities. With future releases, we plan to work to enhance the safety and robustness of these autonomous reasoning capabilities. Specific information on our preview agents can be found in [Assistants](/docs/features/features.md#autonomous-reasoning-with-assistants-agents). @@ -69,11 +70,11 @@ The IA Accelerator contains several features, many of which have their own docum For a detailed review see our [Features](./docs/features/features.md) page. -### Process Flow for Work(Grounded), Ungrounded, and Work and Web +### Process flow for Work (Grounded), Ungrounded, and Work and Web ![Process Flow for Chat](/docs/process_flow_chat.png) -### Process Flow for Assistants +### Process flow for Assistants ![Process Flow for Assistants](/docs/process_flow_agent.png) @@ -101,7 +102,7 @@ For a detailed review see our [Features](./docs/features/features.md) page. * **Azure account permissions**: * Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner) on the subscription. * Your Azure account also needs `Microsoft.Resources/deployments/write` permissions on the subscription level. - * Your Azure account also needs `microsoft.directory/applications/create` and `microsoft.directory/servicePrincipals/create`, such as [Application Administrator](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#application-administrator) Entra built-in role. + * Your Azure account also needs `microsoft.directory/applications/create` and `microsoft.directory/servicePrincipals/create`, such as [Application Administrator](https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#application-administrator) Entra built-in role. * **To have accepted the Azure AI Services Responsible AI Notice** for your subscription. If you have not manually accepted this notice please follow our guide at [Accepting Azure AI Service Responsible AI Notice](./docs/deployment/accepting_responsible_ai_notice.md). * (Optional) Have [Visual Studio Code](https://code.visualstudio.com/) installed on your development machine. If your Azure tenant and subscription have conditional access policies or device policies required, you may need to open your GitHub Codespaces in VS Code to satisfy the required polices. @@ -115,17 +116,17 @@ You may choose to **[view the deployment and usage click-through guides](https:/ ## Responsible AI -The Information Assistant (IA) Accelerator and Microsoft are committed to the advancement of AI driven by ethical principles that put people first. +The Information Assistant (IA) accelerator and Microsoft are committed to the advancement of AI driven by ethical principles that put people first. ### Transparency Note -**Read our [Transparency Note](/docs/transparency.md)** +**Read our [Transparency Note](/docs/transparency.md).** -Find out more with Microsoft's [Responsible AI resources](https://www.microsoft.com/en-us/ai/responsible-ai) +Find out more with Microsoft's [Responsible AI resources](https://www.microsoft.com/ai/responsible-ai). ### Content Safety -Content safety is provided through Azure OpenAI service. The Azure OpenAI Service includes a content filtering system that runs alongside the core AI models. This system uses an ensemble of classification models to detect four categories of potentially harmful content (violence, hate, sexual, and self-harm) at four severity levels (safe, low, medium, high).These 4 categories may not be sufficient for all use cases, especially for minors. Please read our [Transparency Note](/docs/transparency.md) +Content safety is provided through Azure OpenAI service. The Azure OpenAI Service includes a content filtering system that runs alongside the core AI models. This system uses an ensemble of classification models to detect four categories of potentially harmful content (violence, hate, sexual, and self-harm) at four severity levels (safe, low, medium, high).These 4 categories may not be sufficient for all use cases, especially for minors. Please read our [Transparency Note](/docs/transparency.md). By default, the content filters are set to filter out prompts and completions that are detected as medium or high severity for those four harm categories. Content labeled as low or safe severity is not filtered. @@ -135,7 +136,7 @@ The filtering configuration can be customized at the resource level, allowing cu This provides controls for Azure customers to tailor the content filtering behavior to their needs while aiming to prevent potentially harmful generated content and any copyright violations from public content. -Instructions on how to configure content filters via Azure OpenAI Studio can be found here +Learn how to [configure content filters via Azure OpenAI Studio (preview)](https://learn.microsoft.com/azure/ai-services/openai/how-to/content-filters#configuring-content-filters-via-azure-openai-studio-preview). ## Data Collection Notice @@ -153,7 +154,7 @@ To disable data collection, follow the instructions in the [Configure ENV files] ## Resources -### Navigating the Source Code +### Navigating the source code This project has the following structure: @@ -183,14 +184,27 @@ README.md | Starting point for this repo. It covers overviews of the Accelerator - [Azure AI Search](https://learn.microsoft.com/azure/search/search-what-is-azure-search) - [Azure OpenAI Service](https://learn.microsoft.com/azure/cognitive-services/openai/overview) -### Trademarks +## Shared responsibility and customer responsibilities -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft’s Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies. +To ensure your data is secure and your privacy controls are addressed, we recommend that you follow a set of best practices when deploying into Azure: -### Code of Conduct +- [Azure security best practices and patterns](https://learn.microsoft.com/azure/security/fundamentals/best-practices-and-patterns) +- [Microsoft Services in Cybersecurity](https://learn.microsoft.com/azure/security/fundamentals/cyber-services) + +Protecting your data also requires that all aspects of your security and compliance program include your cloud infrastructure and data. The following guidance can help you to secure your deployment. + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft’s Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies. + +## Microsoft Legal Notice + +**Notice**. The Information Assistant accelerator (the "IA") is PROVIDED "AS-IS," "WITH ALL FAULTS," AND "AS AVAILABLE," AND ARE EXCLUDED FROM THE SERVICE LEVEL AGREEMENTS AND LIMITED WARRANTY. The IA may employ lesser or different privacy and security measures than those typically present in Azure Services. Unless otherwise noted, The IA should not be used to process Personal Data or other data that is subject to legal or regulatory compliance requirements. The following terms in the DPA do not apply to the IA: Processing of Personal Data, GDPR, Data Security, and HIPAA Business Associate. We may change or discontinue the IA at any time without notice. The IA (1) is not designed, intended, or made available as legal services, (2) is not intended to substitute for professional legal counsel or judgment, and (3) should not be used in place of consulting with a qualified professional legal professional for your specific needs. Microsoft makes no warranty that the IA is accurate, up-to-date, or complete. You are wholly responsible for ensuring your own compliance with all applicable laws and regulations. + +## Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. -### Reporting Security Issues +## Reporting security issues -For security concerns, please see [Security Guidelines](./SECURITY.md) \ No newline at end of file +For security concerns, please see [Security Guidelines](./SECURITY.md). \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index 2364913c7..cbbd35b30 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,7 @@ Microsoft takes the security of our software products and services seriously, wh If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. -## Reporting Security Issues +## Reporting security issues **Please do not report security vulnerabilities through public GitHub issues.** @@ -30,11 +30,11 @@ This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. -## Providing Feedback +## Providing feedback Please refer to the [Contributing](./CONTRIBUTING.md) guidelines for acceptable methods to provide feedback for issues which are not security related. -## Preferred Languages +## Preferred languages We prefer all communications to be in English. diff --git a/SUPPORT.md b/SUPPORT.md index 5a63fa282..58d87dab4 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -2,15 +2,17 @@ ## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing +This project uses [GitHub Issues](https://github.com/microsoft/PubSec-Info-Assistant/issues) to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. +feature request as a new Issue. -For help and questions about using this project, please use the [Discussion](https://github.com/microsoft/PubSec-Info-Assistant/discussions) forums on our GitHub Repo page. +Please provide as much information as possible when filing an issue (please redact any sensitive information). + +For help and questions about using this project, please use the [Discussion](https://github.com/microsoft/PubSec-Info-Assistant/discussions) forums on our GitHub repo page. For customer support deploying this accelerator, please reach out to your local Microsoft representative or email the [Industry Solutions Accelerator Team](mailto:isat-support@microsoft.com). -## Providing Feedback +## Providing feedback Please refer to the [Contributing](./CONTRIBUTING.md) guidelines for acceptable methods to provide feedback which are not security related. diff --git a/docs/process_flow_chat.drawio.png b/docs/process_flow_chat.drawio.png index 5e30eec4d753d85a69e62ef887850a748f01f8c1..4bd9aaf25e093a06ed5d0e9a4cc41510ccbeff81 100644 GIT binary patch delta 563096 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%0AP_VvU83NwG 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

ODYZ`eS(d4oLFG8a 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 z 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
b@_W!kc_>iQT{0dbq%pqDN&U5sUhz z?r&~;RD{7_OdUXdR5{Z$kMP?!sX6+*v#$75`F66^nnYi49JMr0Na2fdhWiZ*o}>M4 zQAUVQ2-tOVdsS>z>SVvgIYu7f_&ta|TWHdcVP@e+*shy2{e6cg^o^3{6_cD9G~Tow zN0}>`z~3ljqk0ghO2uw&EFxvU#_zP5P}@ZC(CB_D`CDP4fTk^t5q31hb`j z)vi`rf73s*&{`#Mz9RLZ01~ggwkrJ(EyQ6cK5OSr5^=BxM~~qxf!uGC0nl+=C~tNe zN*k3NQ*fK+udzer?L#!I`G=`dXy}j06>D7~l>7S8)kU6Ey{FRuwtIacQWqTsuxWz) z!-H;y60^!@g8U--l7ou^yli}OsX&e!fqt=t^Am#WaSiU|vqYFNB;^_lpK(8f z$_8_IfIENmY0YJZjBq|PkF{JU&-?J-k>t!rfZx?w6U;h%6l0)zS$}^Ep&1fq$xFC&a zAOUxi^O?=Ft%2T>Zn;SF3N$MDvDQC^hwkTKXjAOV$Sbe>)x+s=*P1JH6s2)GK+`cca6rLDHm58be=u@r&rnR z`>SLTul-<-Le(-Qf&6dXqWD(21nOqf@a=#=;JL*4r=QzWRm3H3lFMLLGCR-BNl{6a zsH;xo-=`FKINVl==PX20OdoLf=;Q1p^AK{<6H!o;t`>frx;4&wIWC^#IySHM+x5KQ z=ESU2$F59+p2}KLN<5KE0f*V|MI@1P5amX^kem_rsp${bGmnY@^6EMI)de-C4L|DY zhCuPf!dH+Y9K>czxnC1M$Y zk)(S~dZSl&B7jE3nJ(3~u*%m`MKW}x+=!o>Q}H;n1fDBuftd0K4-dVp8Op~@4u?B8 z%$L%b`5bYWAyqvH0U+=`I0ewP_l7e}Y9?q_l`Fb+T-QjJUj;Q+{R9;7@e*FHQXf_7 zwnU-4j-&vJZ4$Kc2?+GK9ZEC*PWaB}F=5=_Pc`iD=lk+V`^~>64mKimCSRSt|IQUO z@~*(#`cp}U78Q{+8QILbpKF;Y_y7rC2%lJCnsX(fYR7t?k1hYQAwFIxYIx(2(1 z49Y=@-am_HhV+545Ga#kg>4uyvgnGT*?S~c{hjs)#wG2MU51Y7AOF9E*bmWhVY_8z z8+MnC%453r;q{?unzoHG^4(n;0He14xLW%Fbu9c7CF3KH^va$asB&L$(I)k%co+1+ zws*Y6UJKcs^ybYp;ka;t#*G%UO#MKS9CN^;1s}266rOuOPA?F!XW?Pc&&Xre>_5aZ4!AKP zuo1jpprUw%M7EI8vN`iSw#sKX$XA zklMgTxRTIpSDsO#p5*h6p4h9Ih4v@)vbHhCDwkp%~$GgHC?Zx z`EtV{XPL%tR};$NCv>&DBxn@jn(ZacgV=W{Rn0OATru{Ya0~H#Z@}etL}vNm&kM=< zEaNp8E?r6pD+;-W+7d%-O+|fRIRbMSWLhhGAEt@G)G(kezppuk*sQt@ zPb@|cv=X&qCy1O_V3A$1q*#5?C|8=`l`ZXlS4m@hyM7(?Ia^WP%&HIR8%H~Q5i8%l z5q$`1mdV+$gZovNt9@r@=Z(Qf$cLW}OqYF3adJ|f{|+*|-fTvxIXoI2dZ!%2(ur89 zuJc`gq};9y-&<9}xzs;Bedo8i6HG>qv8IxuRCxY$;NkRypN z*`%CXT+Eqts{f&e!p5e&p{{%zen_MNbvPbotG#!0k3CmG_IQcA8yX_|YrWiH_x>^&{el1&e4(X7#vi{< zSbj7NiXmhxI?S*AA};DzM3EvJ4W>iguJ>!!^kO1Y8jqg|CA{1mC1gzVzOI9Zt6hEh z%0e0&Blb8BWYYAWQE-?#bm&lSQp-}R3;3o)r}^M{h8l#w7Wi@HF5-93YyL8R7R{&M z8YuLHX94gd`fakWSa2$tfSOJ|>uw6DWy1sk7WK+#fkVm6HRJ-ul9ap)d-LdGHf!jJ zf&RiMLT|C!FzGYkGxO_FXNE;4IT!*Zt_eL@3V5T`nR45out{ROo$(ONd?&e|4ks=NPx?0;&+|M#^s)&j%TE?@X-eX!-v1gMpXx#aX9yQVogvDP zhOHfAn?Hc)lwqW@=e7pp@F5w32Rkw1^ zl55XUpp+NR@Jy>>yJC|m*gBsyv>jtefcc%40ttK?r?RmKKT=%=N<8|R+nB!JKg0`( zi5XSvu`tD(oOXr+PRU_&=R!Y=O8mJ>MQe@2wgcJI`|y;@Rl6}WGqVp|X|?t3gZk+l zFV8i}jUwdaI=WF;MG5`N(c;sO&=2gJ&|L(4ta7 z;}hea&Ck}IA*0i-cBut{x@h_b@^P4#Q zuvjdfm+Bs6@u|jnw28-{=f>NA;o_Gb>CihAjuu+H^#h$3UNU68R-{Ywc4&zZc8tG` z8&Hb7BrW`lSr9NdW>i>6h~_#E8N+bl-EYSa|G3dA8{ z>~qMY<@7H6W;jX+Vy^VixMw2NEQkl^r*A7oXU@d`f-LEF=MBzpD}$W{G!e5z)Z z5cT^FDw+kQJ&=sU;^jT5Xy8(u-f3m!cmlw&l^By0f;{)_})^*94TpZ>5tb zw=Au+{f3$!Zo%iVI1caKD5c*+Kk1wPa9IgOLq{2qH$HG5IR^a=uXzhDf+l0=h3+o0 zwFnuB>xRB#r9hIoEKTTV zT2TZ{8|RG~Z@~W)X~pOIz>fLN-LWx+X-Ka5fmhe)N0MG|M!63B!6-gO`BjDk| z;spilI>hHduL<|lTfPS&8=WMt?0)hQiZu+_-NJXZru+oc{q2yeOPIqY7Psbw9)YIR zsOryVQfTmqCk?hUnGGj&^7h;;X#t1AMK{gT6Mp0G1*Q!&uL8zBGuEh3N2ZS#`BdJW zS7s_=!=+uP3VVqe!yEigqu>o^5$&gZWn2t>BBXyhW_Kg*azfm9Y^SmMh z1^WrR;7sB8$fwGdIpv{!(W+;B4W!IGG`^cR_fB3ox?VM|eDE2{hzD}CS@%w!NlX6` zaa(vvQnKY2V?GPj9C>jSMg?_o{10!;c;tf7QKvd&`RVu8LoeAeffFp*4x;y4)mb>LaFwy=!gC9xz!$v-N{0d{aWLzc5sw{(Rzz8Xn)!ae;HpEJ|$Df9W zIOqbyzka)2oQl8VaiUF!ctX(Y`5crnVE215!rK8^sTj~&?L zAC=qtHgB#Sjvuu{jm@BRa$hXpgAdJ@qF^vL4oP2YIe$wO#;M2NYR0Wck zgp;aD)NLp)6tT%$*uIr`^zFU_O=#niXY(aCCk_58zMbtZp0{o}FAc&enGkgE1f}LA z-F+z|rw97G5TKCP!@-pw(pQyp1~qgXw{Yq-!WUmN>s@~+&QRA(;5OuzUKO$C7GLx+ zY^43oTc=4j%gMn>apgej8E zV|a7Zm%~b1Dp31bBVUFQrkFfZ>P%?i%$#(sNPAD3jJgx*9Q1+6SpdV>Tu^l>uj12i zU!Tw2Cy_G;IH9$NQ*$A#DUFcv5R#Ub=AboS0?`kpL@9O$vk!`tv&y50y0R)O+;-tT zZfx6-OX@Sy&}ln5g}`ClYh6%$d1rDsJv}ywImRjw97Bx`Qf7Yk{D(E~Bok+B4i1~o8M{nq2WqhVj zRuIvq9)kJp;6YR2NoKFbn(=_zjGV5?v|Y%e*|hJI$5{-??6ui+mQA*i^AqM5S`Gmk6udkG2cK2Kt~Y zf@6Nmu@g;Eq4BHRSvSlpfQZWLq4ry)LMl(`ahznP9u2A9Zr7{GRqiy@@>PsOEgS+= zeO--gcUklev))h7{YpmjBRZ!+x1Bp+*^J_w+E^-Y+r%!p_RT*tBO}dj;6Zs_B^Lc` zq4qSbYTlQlTLLmY%E|;+@0|a;c8esQlirXzJ9gVCwjdczlM8jeA;N}yoq!zgT((2e;)cIjj(Oc9*~8O zv#q@6f7FrI9>>slc<5QSg!CD%nk2YDwO)9#xCh6rhg|K+--$msx>&V$R>Ln_BvIym zVTUf)BEt+VE=obf`}|e)|L_8tR@~zfE9U4(8Vy#{v1;jSBMUICGHyM-tFN@w=}h-*P)0#!B%gDx+T5CD)C% z#;G<~{MgWk9gRIS6d@?Nony2lna~!u{Qj3$DKKcFO8J7POiMlXYezQv_{I&8G z|ksBHY| zO^5jpB@5@T2jLi{)nBk2TEBX88bEH`!odhvQkX*><6EwhYYh&dD8GOnZomOm zxc;r8pN7o5#>c0{KC!)*9KXKMBfD(T(}T)wYgU0dU);H|{BtID1Fe<@LP@w&{HX9|tWv?I+`;aKYBb#LWd>YS zxED!F5_gn*8rzWv10AEGk8s~x+=T2ubcI)n?HS0bUjP*tTXR8-VA6ZFAooqyXw#Qb zy2^<~+rkTfRFgV`M+_LB-IMg*EmjJrWP!&(97ZfI*Rh3nQ4x-!%k_GR8sS*6aA~WH zGZZKg|JI<(x-#lQvj7$42j+liO3~{LkzYUSK93}&@>G`nKfc~Ftjexy8wCU@k(BNZ z0qKw~3F+<vX#@nMK^BdafPj>AcZ-yC_|1j)bG-Z4{;i_UYsMJoIHT6T z+`h&CK*Lz(zIEOJMG1~tS!h}3y8GRt%%Tm)MsO4mB=M|t46ZIDW8yC9uGMD5@K>3> zQXV5dd3sG(;ec5*&ok^7l}qd=zbl$omj7n$3rIDOAs4~}p7A~4vjHbCvoBBVEMRa8 zAx{9wup9W!kbj3BqHZJ(+PXOvH1TYXij!Lo8G-G_jAM0!Eq0#cy<`D|j5|JrU3$LE zVc5CNCliKncjP_15*wrKxL4l@vriAIJs!+<9DQW*7Z}e4&2v-|OsDWs=CeV3_{*#n zA|c8DHXG$Xh)6!^zxSHP3>j&+wp0S@8)3764c>dJX8O%mPLHT}*#PMabVDiBFufD% zOy?;5Dq>Ep*PRwQ=6QhBD~wzc0x3-#b{gd7Bn0E}t@7@i5>Ow~XDH>=PyKnCoKp#Y z80lc9s(7PdNkbfLAeEWXl1u?3mpRA#Vm+mu>2HHP+!GV{0!k(jXAXFhLjvhrGTndK z+3Kg^jsE(5xe-DXr`nEWI!XN%&XzBM{#n%-08*>rP6{;U;kyZwQSz$VMVQ&wx(GoT2b)_ zpo8lI^!hTB{zqi`gLn{pkp#0OTwJR0++U4UU#dU1ubw-^_u7gW>D;8NUvNsF{t`>Q z@A{|Rrxt{{=E`9a`Lk7~ekNb)MXs){J^&eX@Fb!zzCtTIm_B~VK(@H4AJa%v!E|TW zgf#oa?a!DR&z|Si^?!gw^1;SlrrNFQ8U({(E!A zMub?Fv<`S2&H!(*1d+J5t`Y3HTfAmvvV|~(X8qD`@7E3Mglc2_Z+_2d2rL>g=va!) z+)u5D9Ct~4NxfIsd;cQxs2Sqr&vpnz39rzM)Kt3!@Q{xQZekUvn#7(33>FdiJ_)+P zbTaKniNI1m7JA5lSw6EX9Rl-hbA0H=XwjS#W+>kBru zZh(b^BHp`iw2D`nnVphS_`77Aeh%38e!*d5V33{nDJ%pgzC6N-&~`BPIIzA#;j#Xq z=6Z-o2A-5J{mwSnFg^Sd;AoBuUIR%W!Fq2;Vm$Qcqv%*iZIZ9vT0y#_24@O`&gcdS zzx{{taL;oddEHImkuk-fLstmMRC0dtY|6;>-Y2#v)Og0d%m*jDxH~{&^1gotIO6^| zzYtA|n*#G4D49z5?w09?mrFC88pc^6&zX_`b^dJ_fw1)CRrwv76UF`V;NhICInMr2 zi|0tRIy@VV@;XvEyhzvf%P$`UXR}|&fO=s;(MNaXsU)0Qr5?@9Or5{rZosJ7u-;TddLe9u&;d*QK;&(_EAu>x$nwfow7 zdLPDPd2}Pk|FB2p%Q*-`nU(3{ zL0}^LlkMRjs1}~FHf4L$D=8fc7-f+E>Ho+CgXdo;C@5m#2$$${d>ot{@+^Qy8Z!*9 zwDiFTmdHsW*_(jhYuM8L0cc2FmMpZs>Bm~|#+?8hnl+F-^cqetT|MNB>NN3OMq&7{ zdPchks|Nb_nKN393V3B^1l^plzN!9*vrqN+Jlqz)uX3Zbd!h#*SGq{T?92gqbR5B@ zTc_mYuY+DR zOsunlqkKY|C3obwO!@X5EG2E@LwCI4x~5NIZMzuBLB45i3;7gNjs0Vh1y=7ow#I71 zRgayt5AWLb;K4lDw0^TE7xVMo*}4kZg3AF0jM=G)V}NYR!feOwb-pP-f*f-|ep%Jn zDX)EYZ>>bU=qWJ(Nnd=Kx03y4YQRR-k{1L!mAVYFharyhC;TCM$&k4%Xd-(A+c?OH z)iijT3k-LbK?bg7y6abFoqo|j_#EZ+^Ba6DsCszkt6FxC#jG{Lp2^CxATk=ugZl=Y z>@2B(Ge!co7xrExsk4yp5Q)oj!tQX5EvqnAJw-yMY57<+C`DBwLu>dqXUyEVShp>- zKS!>;$fj@GCvd~cQ#uBgeFVW^Y6K^N0S+|+$~(79%Q@gcUQAKIC^s5Vwa!2>2`}JI zRGay|{&s}hGT+Z@+XG?XB#XT52!+Hwvo6)|c#HqMmTgMxy__;QF2ylf%A z#5(wzNP;pdt4byD(_OWNNM3gGZ}q`H}%GesrJ{k^rNX<1w%JSyB$TwWg~D;xN?|w#+ScYyPRFTE#ME^ zGReUfOI6iHbfk6u61Kuw3r(_k8TDG6SQ^@?cE%DoBC#7cYQcW~0+m?kH(Pt>(F#-X z%DTs{m{?eRg}DQVAkeRa#DY)74cLD&5Li+Y93^P?VsW=fOUd#Ncz}TJIXocCc7Rdf zZWc_%ll=gjVpTZ9R1o=sN%teMwi-%T*xzZ_H$g(7?VeDN=Nd$aQljPQheateHE+{< z4KJ7jG4Q|7U2?v#ztIgHI;+8d$#B*=cpST=`w4^6?-v(?iL;EFU|wWo&EeGjDnQRH zGHGd~D|pN0`vGBASA#$ZgzJIuH#u||F~RG3r%tTm8^*sTX`WfRoML9uhZ7*ub%2&V ztAGDdT7PO7R8M<0Khr=EHL@OCrgZTHy{qbn14zfK2fW9mspnK+BY2YAZF*+=9!2(L zE|xHBSY8bQY(3b4b9-v8K$*-J)HNH~0vKz|KMiYMCG<(|hm{F^QO~b)T9*3q>NP0e za`1Uu>Ad=b(R}42IPC1P`u0-|oCdb58TVDiuAy$)Pnd{AxHwg5(GvZBy>3J;!5;40vM4d&3rJgqZN7F5n-dK0)DR%O%74Xc(h%Bzi1db8O;(5 zj_h?8?6sC$rU^I(bL@G0JCw<>J_n_p0hYjeF6`|x{sk3u%>{>=V4vFuav*?_D#O}8 zsG%;T5w6eHz7SCQ#=cM_u zz9t(>?efYA0}cFXJa9wOZxWNZylnOMCb8OpSj5R)6R%dB%#mN9wJC#s zGKXKFG=RHV@J2VW(&~)Xt*l+o73DOMqIdu!7e$#E2)nUMHR-T5OdT6Tdc zzsT@1gLfQVtKs)_jmNeWp(%hL8W#5M-tD}2>bL14_%Co~;>X(PQU#t!{RT zCaB|Bi^bC3R9W^Uza(yo|3sYy{ZR+8f9@4ls!EJ(cT1QhgWY2-2(Ib z4S=wvUnq^71OC-2mBAfXOVW$`Z<8;9m?=Cp2<-A*EEE(#8i)WCSP)hqq?<7wwitDw z=B&fBBCG(mIi1tWAFfipvy#gpVb}^+&L&zQ9ddJWTi|=qDL2{D3Eq#78r`?{U@+3p zPXJV8!#DQt>TRzIeJ+fc7Tn`r7E3b)L04wY=~6N{weqZ2Xdniqa^0S z0FyR))f4L9PyaWaddC_^9>fHkwqh+*b&HR2kv502dqDH${CiFao_7Ec7w9mduOhj3 zz^&GId~UpPd?tE#x8h9b;VB`x-wobl)gN+0S)yGOJtnVQGQhe}%2Hnp(=_v0MfEk9 zoa>L%&fpshaCTykCf;9`Fc9VW3$($kJ+nZ!U2m^EHRHFdqcma)C{1IVXvUuZmi9Mo zB{1ea8OES0{YlFY@e{(0JNJJ7`^7kL2k<>pd(Uq+yFc@sklO+lW=RHs$z1& zHorrr)H;9stx<>s3-Z*?F?>V`B2aT?V#A=25IyqV0g}d&k>#~$&aVgpReoGLWd@<( zmL4&>EDCGQ)BJ^KDx+>akYM~bG~#p!gXpeb(8d2C<|ho$9!GV{x-_4W*54i>C`Jxs zo9H+mFFbmFwUlWU+uu_OY6UCnciutE(nD_Eb&qr8(hAQk(|QS!Cfj5U67Re^%qqG`?P~N1$+yjy=VL z-+p;_t3iFSg$O{xPsJ7LH`YK81yz)d%ZEnl4ak?!G@u1)ph1=FyJd9?#Rh2_G2O|g z_U&DQ-hYblss})fX^RIaurGO^)yf;!IcBD|XATh2(Cp-^@{ABs{d|Q~jD2n5c~?*S z2DMdN^^a}li6Px!j68K@@}5{gxt-s_T{uOT@$f>)hA!AasA~v3o>_DqbX~0K5n{mL zOv`Q<0--c@n@s5N|6UXF>HcDc(FC)}Xf9B4Y)zDrZQW2VZKgUTF$a4e}4eYL8GvJ=i%S#TThUxgpKTcTi^m%i!9MxJ}(`2 zdRYirrcV3f@OVIeP=ZM%qYZ3B#9G=(T3~{QJ^{ku&E*Rc--8ACDoM@5quH{#Ki@Q& zYbGr5)2b|A3HDkoffc+1MUUaDm7J6DAA4qywvEuie!SZ(n!nce9S)KlJy<=u@~Aa> zWJr<8>34_CV-r4A3N^Y$!mtxE_WF>3NjyQ{U)27K*XhL-=DxrpjeFS6j!(lJG_pT! z*-x;HB`1-}bwVjGPvqMOxxJu-dw48BLz|qjd+h|7z!XD?iv3mzzREtY82wB=>~9 zQNfO#S_PN)8;Z66djsj=*95to8Mr=ekA02)HrCf~$q^8(gG*eMg!v1#7CJS@m98u_ zkPwZlm-ntPpTp~pqLLvOP?kGQSp�#8WLa)n|XPg%lQ7T?TCm2R>5?RxsnRY&*+0 z62#xw94`5|myj|!pKVQ`+4&0~74y3)P{w7@^YLjnly1-dMnOeoOMzJrh*VHMt4^qb zFApc2IJsU8pR6A&Q?aMXBjazax|JD0f98}@6=(V)J z@W(f0LiB6GKAbU#)%pNXuo}|DXt1IxG;iPd3UP2iB)ghHFnoQ!gVFZDR}`)f%-wx* zshi7esb&G+pzujD{KJ9%Ol}GE4TN#|4dI(KX9whKl*SP&zN4C1O&s*pd^|cZjIu|` zVISTqq5WZdtovEE>vqchD~*bE4>cbf;pbGovMg~>ZfKS5Osl@vsH_DJ+tHU>CNd)i z(5qLk8tQx7?_T3)csV7ER4+c>`$9-C{?kg?faqyrO$-Baa|*!kn%_8l;=a533-R@; zy@8rp{8JzC%o_p=OIsgrg~Zkr_{*NcQTl!zhRk6i9B=BM{%NPggzx?&Hu*jbrf4p; zG}YUw*Ww?EgiAGv5iiXjZ}M;eshqkuE!G2RiZv6!UhlOWH4I=IfSVvoz5fkkE!!xxf{6TEIsP)Cr7W^F z9rqJd^S}nQL(aA(Z$mXv!ImEvCi5<+7@SvzqKu4;at&7xNVuVIY~=pnio4%9Y25e% z@0tsQC}|n0*)-{X?$=NuM3Tx$M2?Dzs&wDgS+CYnQ!}G11O9G1euBcnjfp(px52u3 z@_^gM>v%`e;cgL!7%M%;MKYSqT%xHaUh~?@OdkCTqW7s_I5@7}20?zBC$Z!ri0TRB z&9yo#&mlC=d+_ z9}iY2v3u*UZOX;xF{0x<1hR<%9yLe1Mg9@nu}{4Qpaq7v3TqGUK2=DuO(n+kh0T9}CP)P@;;C+9{-1#8BC_bQA`Ec-T))}}arK^8MJ#J|LO^XT3 zQm!3^@dRkA9Rg$6CAX_}j(B>XaWJvK-{xQ(LFbha7yZ0?9ZUdPu2*-TS)t$krF`I; zdAEBvv8P6kQ8eo<_7e1wE#2BwG za>I|cyp~E@^Aa%Iqlk<~RA)kGy!$Kcc$> z5564Q#x`j|R(ZrVA#t8dgExd$*bZsod6}7zoaz1n3;32qgyBeZjjwy9s z#!JTTxNz=9!4ie~RL$C}N$TI5_K@G%|VjKk#B7J?f1dkp4VvPk9il3rht6L%R zQ$23`bdB~PdNCk`d9>BXg1lJ^yOnY;?6^UDV0*M>12D>JU%h;vl=Ml)4m>h2=65Q1 zAc$Qf7Y2Bhp0r54`ga8zH`29;d9>2kLXD+ifK`ldXmki8MqPoPc26g@Lt%--YG061v!qiTsXJ9ULPw zxk;{0w=4Qd3kSEBxSI1GOtC>hzQ3Ngac zpmuv;$F+GIDZZLRgI@>NPrbzsIla-)t}gLN?73{7rY|gi?*~7F-FL_ho$L zuv3lW1fnu&Eix592fM#97a7sd!Otnr9uS9b+43oh@^gw40a~6JbmR!hggsln3|#d? zFB{vDKypLOe;ewkbSfv?W&3g{TS`KLx>z^1>J6)q3i$bK$lTRGaq$R(+S;g_&R5ZZoS8K$$uSM!7c^YUb9T@`y1Fok4?xr{dWn4gPn?xO5E(iu966WR~NmO2#@n23V z%WO{FP@??OnD05rZrDb{!s-GpQsdpNqV1>l1Z9~*merAyL1eBkv;ZjJ6P93kw^hKo znvxC3Jp?mQxKT1;4@jWE6ZJXO${zV&|A8y7q@;P)t)QSl$l|s&EIChxiu|2ZZyv_a zU4ioi32ZzP^GEck@Piz%k28!pEG6ABT;SECA#2xq&5udMnW^iG0r-6&VcAPt^WPA;;}Yds7CqCDkj6{{$E*x_D-&Pjt>ey!iWwb~P)zx$rg zEt~$ROcD%CzslrP7U5tftLV~J>kQ1$(F@l6IO6%j^W^oRmiu!VJ$gB8wt4#C9<}^r zP{TK!I>_#&xsSA1nwoa7!Mp_nL7`%?otCZr2xhTs=Cn^H+Dtnn4+g9MLw48cB@ie2 zlGxgnnb?bUE6VvhA-*@rlstA5kH;^cQNcPma$fGuzoK%j%=prHda!hk8}aK85|U+8 zQ7jBpok4ub0$Wddj!zr~$b(m%Ez9L%e`a^H?Hjo?gL47D#NgE@%Lu9AXIqeB9!e?d zT{J}b?@@S)4s?`MAXW%rR?H6fPRmHj`)Uaq9DCsi7IzNb**b=BYJxpYjqLlj?NIYs zyGPC?^MFm+V?>KzpJT zS9gctG-{kTfz;TMcwYFA-Y>2|Ib=3mQB1Vwk2F0=$U+r;qhTP1l*v6v_=* z0r(aN`#}%(1ST56qep@1OTZQu+0c*M(^4koiEx}VPxA|>PG8{6EC?<9K~5vYu1gJ` z8ZNOAAj*I3j*-@({n_Q@Q_f5agtKs}5W-8mUHRUVN1>hc@4^1%tMTF_yt2^HP$7G< zJHP|JBIyz)CRw(aeLC}(ujU(Jm(3g{U6HTfz1soxp%V(C*thm83$ZcvBhjd{E;nBI zjoMZRzqINm42vJZzYTFilahiN-Z%ioeea!BQ+ovEM7Q13_!RwfNC~QT z`fWKgGHzRRnsex^OY!Ff=?Ct;?o`8Yjr>|bWyuisxlW5}a^H$AKra&s2+3zg771^= zm2Rr^>Jg3(vAM(rt%if4n-r|*)ro~V&Lp1cMm$ZS$L_-!MeL}^5NXP9g^!{>vjc8xz17+y?cPlBd|1u<( zIzlmllX7V2Qv={l1s_5IjAB}pL7+c{-7pMP<)6h-Hfw1&m$1K!@>0kO6|#5tvSCP7 zLSN@CXN3>kJlx59i(=Pegyj`WqH`@Ecg5f|L{I245*3$teqsXWp-(2e#x|q#TEsdh z|M&N~FpzB}XwUI~2LPdK!%aw8QL!3f1k@4?2FL4WPdk8N4Qo|QOBHxOfbCZQLTA7G z9FW=v%sMt_H0J(m@YxIoj8h~Dy@EobHz+@0;fTqir%hpw2LdJW(Un%kNEhqH` zMIuz%E1Him^j3jzq2e|L`{DaXm#zz5n-n64-|+j4r=}%wCjl2f;_2`DJgL2{%*<@M z?m~Qes)%WzUhHzZE+1}ieR*)J2QPj1nLuN+Z)*8j4Zg#`2*sPJ_~%iHMi+KhS8^tI zU$NDz){%%W1cbf z#a>$|G&W>e2m@jp?5R<|G9H&SOq3a$Mk>>F<~?y;uG{&rl!F6UN6PsHSYuDVqIiD01g2`sw}!-8w#9RmrN&LQd5Tp&8SqHwk+cQnAg0M;ZKcSGDHCn5P7wUM4+)?{ zt7Kp{4Avmdl?7r@;1YL)HbjvSt=SbhFF2Cu*m~nZKuFlZEG7PO!uQXXIs-X_9Xn|1-uI`8d|!Tk#9fQ? zZ>&Z zquu28*o)J>S+<@qV6ddaIIozkBuP!dP+k2vyJo4IMb?fFm<|0Q=Z06(;F^Wd0)do^ zLE`1EsNcacK&F3-p~?dt<3YCnB|30xdc$__wVRGB+b}od(D(0cU_=xHSdI^Y1$IZh zv(?|A7pK3biN*az;CcgJz<@@>#(v{%Bj@(X)nmk0FbvxT=uV^~|I{pra_vv&NyIg9EqZ;@3~5WvR9==o!N97{F1c+KE5s3Ih^|1jy{#pOt!{uG;6@+sQXmN-EdC z@&(6Acd!qXl1xFToV!G%aqsr7QO0hq6l@0WB7m(-4WRxo(C3w$=DEFe3V&yyzvOw` zHy{)>G4;-b%56XA75n4IKem5aA>Hk8oCAL(0vk}h0}O#9nHHxOr3{cz&Y*ks8XqIZ z6-{lQ36+psSX=^bxHF)ZEUQA7__rV`hMJdm%(l^u&KLCY0Fb4cVz77B8USPM(Ww#R z*t4$BpFdk?_^O3**ny7Soxp)6O4_XM8O8r1ypaIf)NrR{*(gMU*_K-=vMPaAdj~_ zi4h7jZ1N~`skZ`rveBu8SXi^tplUnAvmDppDbJt%-Bx&S$(Y~|P5^MBjCh#YZ2cc}16 zS!Wtsc{VNdA&nabV1^CSR&qIGC2BlAd%()A>!gO(9a}%Jvrh=Ia%W}0p=)=fYu}IG zdGivpImwNYM_iJ;gaiM?@Lo6DH?q>PR+Gv-%v#!J@RGR&8!o~sksg%2)QF9#&aUss z91NUzY5)&tsT&qzhpv|KEQQZ;W+ym(hM=sWNjuff=DR(-Hv!G^V|emZJHY4J4SsiZ zceA#~LniN6OE2by%JaV261+L2WMyGdfDx;Lo{*N_-+Htq+Ci7ah;9_vd8-9TFW=C` ziv)pGi4PPlyj-|lD&)bm6br%XZ@c&`nlCoeJ$F7A;^5$z$@Tc$za^8E&v>_aa00AG z*gnnz-b#7MYa?BD&&3|d(&WL0oU`~4u+W3Bw~DTS=)*8#{9LIPj7L_$8P~edlo$#} z)DsA`An0ki`rWjSSE4?~5AsS*)R3kR&C$zrHso-V7;9Fq3GAx;n09{-4gIw?0W?94 zhk>UN<5Mn2Afyp7)Rn+@9x#q|ID6pHxVH93-Ybzz??1#e0NwaA^efMb?kk8pYEUnn zQGzZSjMj@+VL3VbfvA*fXR{(-fgV6{xX_pryab=|hSlffC8;NaCHak7!3mIVAa}j- z`~|lyIyN|P-{`1f5y@YAyCUk~zjvAT#2ks=ek#ZRtC#3ce^)P}g%Kk(EseWR=Ltrb4=RgF{Lj z5HT#Fg^4NU_5#gT7gH1J2&!;26M8I3^4$)-a*t_VyPhN;MS!a5&3+uHg}of+v($(M zC|C?eE3l5sK}O0<4o@y-7?V>H`5lNawZw`jtaK=rlP{5$%|j64X(y-%mCA{^NdXJS z$@e)CaaO=tw*gOZ0|KF&tHr-Mn;OJ0 zknX+SEbB}oYXh8%bT)_8_kf#V6Q0Jl>ylmbn;hb}Rkbl0^JGSsJT_8Scw6amf7E4F zl*Z7+2y^T|+e91?tS~v&X&PYM>K=A~i zmNIWPTwnXbWh{NN(UOM?`3mm{1a7YN_4SMQp6*&s+3tRB&Dv&uHUWqS00ITKqk++H zB2E)6T7HbJKMn7@gY1U5^s$e#`5n@{{8(yp4M}%%pbv3SA&#mkkKtE4j@MQ&L}MU- zHNeCes6U-CaJtq1V*U97W*0@ zl%_9IDklSZi&0khbCewDb%}>vWn@)Tjza=y&?+-e00}{x1I$&4Mcoo3l_FMdvw+Zj!EeL}DzFGfke6yJN*ZOc#bb&3E_F>Ex-!{C4%$neHwX1$ej zo4x2#IkdCb9L7KL(A^y?KsDg!6Me!22k!0A^0P#Ow0DVgJ*z+7$kFl7T={bv znp3;vKEwOo*ZXZwR=jwQu7j2fRIQZy;e`x9Vy|k*MYNV~>PeFRFtanwuds6Uz$-rs z8j_LxSW8p%!uAu|_;V><&G!Ufj2{)JqB!c_KRPZ6G8pd|u6E@0u8ooYTYDIRhVCZ$ zi92)z-6_ujAP|CXTW@C&UN{AZlZgT+JoR*2ylz^FLxfow=XY^r33o`*Op!Gv=RXV% zT(plEB)HIw^8ppqmJ4|wn->Z%iHdWmIVJ~5f{bxLL*(38EqD78_nM}?kGhCVYn(Vn zzsUeEg=Sl2{UehY6`Q&RZ#L%2+=vq3USW+$V5m|p`w5n_6^+EAeEtc2n;GW{2-nzW zf{%P#^q%9FII9RdA2yoJ4uWpph$o4$n>Ai9HqO4Z?!_yilnik`h;N)#H4B~rul8M+ zvWj#lPcLresb9KkZ14o57N?*Hm@(lM9h#(FPYr%~@hXOeGXF`81)w4LJYT;Bx`*QA z&$YR{RWrb1p-m^tS1oYz!Th-5ePcYYvL`juC1+FQqbf}J=5fm}oV4%yW8~(2+s!vCNCi@7Z^`~_+eI3@nh3m*YOpW+{z-$=a zw=30Vj?BNW7k(4&k>n%dtj#9CEhGsiD{;0CbaW|g_KTX7O$igvU3I%;Gclnd=HZ&L z?E+S{noVeEm?dCU%PEbp9phG|>2lb0MGHf|{P=$}8nApcIZa4p@G}p^`|*lXO$0)3 zr1<(*l{a;;u-kc78_gtUJ=MN5E~g{cS9KC9T}~u|%qn0ug0Ql(;)@@Jsy%S}jG)x{ ziv|?|na8gw^&3!f{i}J@NRaHc6;k;8)I6nA_-Y&fggI%E4JXFgz0f47nh}6Nk`;h6 zR!P5s!d>yLoca`q`2qY=>@W97#IKYLi5B+Nf0eH*aM8|>F%4 z2NitNk=W>5CcC(Dkn$r3>gK-n(t_7K#hiNhIyMz{c3pOrH5YrI`)AxsVh4Q8T2?2N^n zC_eGp*y;^y?2bUEd*;=wUo$x~GPxu*7=O6>-yi~*{yv0!D%Zn5KT>StcDp|H4bO@Ti^T0V2m^C0h};P1r#g4^(6u98{V{RJ@B2q?T@&|69)%wZr9_AwN3Aj zY4;h27qR^eCp$%@XLFqQY{OO=(1lVEX8o%_;m%XnD$j10*SJ%jKzvq7?>OAsRfowbJeCeIAqyk5dOuJMkEubVH9Gl`a&w(s9b$pg- zI*;NAz2{_gB7?SZ1mHSUz%{j@6|u`WIyyS|!85c3{k_Vz?GysWmhK-m#^h3-)^3a$ z_}RM8bQ;-h@7Puu^xB20I4&^iK%?hOOiXlKT(}^4l7~|PKzrw6UHgLY@rWlJC-LeQ zR!F}4x4mX+D4miz4#9xs8pW(Zui6#xay zZ+;I1e^4;FZAIJF`F96N6ROp&YAphgfGtQ+3vi+Wk){fighz^3Aoh+vLlrFX;LBgQ zJkL|>?i~wI$&Rd-?1>G?*;ok|YnPrBqDOrq9(>R@CtOMFZb^GML*X}^Yf|&f={-H$ zGim_=UD(Obx*Jd%NP(TA#m3YqC-2D^_z!gQe;1cD>=%;D*XS+(-}8wB7*)awhm?>M zm0N1=a3(FeDpnqF#1$&3Dq97CK3Z4%n^`_8=wE7p#H}=RLxLZu!gXaRUic949hbzF-;PUvT2e+FkMq#7c$-!{~V}K7&-*5mx#@af6 zK>F6r!t$xMp?SZ}&Z%!V}KnnAfaOKcZNBXI3&;In`64|Pfcz(yMWz$Kk%rr#QoJgR$02clN>8| z0r;~Xc!I9K>66PZrU&x^16#_U84Y-QP;sJSAksnjOPdovLKlk(q=TfnZZBtJGs-8| z0DHJXzc1>M%Y_SJ!o|2jApstoD z7Yn_AHDJ)sv0r2ad_?ZH*-QK!CxB+y#}>u^o^LAAiO3?BdsNZ_oHLiG26~k&OJJXV z0E@8|-bOj~JKrYOz@&wnlF}1Fm$e96#J+GsX2lZCp6X15*1G1SKDM3s4c`J-(pYr1 z;^0TvL-(*Rq$n3~#ik0Jl?d4yxW<2~v9#;?D}>Yq3-P>Mwv0J@`MTaeF`kizhwa872bV$-NA9& zuNxU99kB9&m4j2!dpLOXg9aR5WirGl1jx4Yy9cal?^!sle(}G#+yX+Rnj5!EjCy~h znQ_g^Zb+OV4_FZP8B5KyH2NFAkh`l2>u_>#LiWC$sMk3fuU8XN?CCqUfK7-+{6s>? zBcVhL2onmC4GGe095@;X;ixo6!^GgojHJekQ^1X1hO6^9wCsH@Kre50=Vvu|lw#Tw zPo;ZYs#n$VSZ7s~VRMamzxg;0_|I>5xe<8XtaCz$D0YK5C669GVmTz2^~&};B>^-G zvFM8v2hP8L3dNIV^_5Mjgft1xmV&Vj*7G)BvJ;h01479m2<6g`oL}(XWnY&lLdX1j zTG#s7;7@rs7$GX=z&)?hHy;n^$I=Q3xA}qoZsTOcl&N$buzlO^LD(W@G;_xj{5cGZ zhA9ZP~3jH@)qfMnKsF$nbPcJ{J z+EwAZU&*ev-4PkgJMV+6uE}4&h=_@cX8dbyMmI?79-^wSOOpg3P)IWC&DOonmgRSw zT9Tv4oS^@Qe}jupD+Ix~2&kT3E}MLFX4bObaC(HMuL z*A-*5KexX?!A&hkVmn*5W&>mec6@?(uig+1y9ugwK||$+7U5L%7JT7!C#8ss`jwbG zf8}d&iZ8Sg7n%y08u{Z|3O?lacX`^($UUBIH!XU)sIIIOQ1@Bj#4)b0+x_fuTcX2f z7R?oQGudFWePy7p33*3NBwJSevq zz?-aRPcWL9Eef5KEhPme`0Stwqt&^t+9SBngqKmdz@~Z01&Hu1xl(-ziYXp_jhdc| z8p6$tY(BV`!J_YRY}fT^Ia~bb(*m$bkMd?oLT?%JUU0nlp_A_RVby?((g#TN1jp)d zKCO~c40-O^YyxOIm$m!^q;lrUWIt#zB#*ZuQ;Nd9XmxvV^#>YNl}zDxxM8+m^LDjw z3|Ai(0EOQ1J=U)POPPpqd`p0~v0(z^TygKeT7p{xZsH|a!YLQDh^DwDhb+6(Zx9Gv z&ry-8$^wU9Q9r(afL)8FH=~(6Ab*aQ{S$494v(+nMDy*vwG7nUmB8A%8GqF$JGdpL zh;e18w!Eki?MlFbLBUybbw|V=$Wysf&zZjSh-P#cY1niBNjOaHVAvob*X(aJKEclp zLx>hz#A-}1j5erpA1>0)FrWE#dFib{)>AEx=^Lu5;`lI8<5aI>7>2kvYstK_`W!oV zr&IS_uSwDO!f(NFqGihkw_xB(gPk+jB*Zow;CPN8sMmoj+Q>D0MTI~m@9Yk^ z4CCLh_!r4s>nv{zI(0J!KNxYX*vXh(z6(|wcwZ)wePKPXEx^AgA6|RQyPmt!SHFj6 zyYl7P-qs_tC*6s042X>^!wS*qFAQeMgoQv>7&34uy`MQw@B0p6UWA!7hb&I4UZH0| z7;>BeK2&#)E)Fv%VpQ(5V@41QyXU@SJ65JqEIGLTMh0RAZ48v;xyePnYw*NJXq15| z>Z+3YD2RPN7}qsr`IydV2FIL#x15=*59@F<*XKrTf-4i%(DY~0oAZNZ&)*H3l`n@V z*K-Tr=!_0-ilP`sz{T2;dG{eK()_`X5^+I-V81;#HC7x-Jre}TKuM)u?CKXrVPLJ+ zWe%c%ff&Q^bIKD}z!Ou?tpr|-X|fh;hr`m?AAyxQVXl+|wU|Vk$LAL%krRSU6w5o4 zb@qo6G?baY0q&7M@jlL0I_!f-C@-PWF`%ua_vH4IO&Yn5jBY?D+Et{xo3QpPGn1yv zoK@caU^wN`NgTet65M_U{e3OHgUb606*;UayX6Hn{OCII2L5uDP$R&>S=&l5eo6@q zi2;!6d+z|h>W_i(Dmq3w!S4g2nMTo4);xlNnOC(IJtqCA=&9VZrLkmU`a)CiCdlkD zk{v%sCp5Ck+f)@}t3Fb1KCIlWs8fxL^pLvks`&HqS2emrnqP);i<<%Ju>#0lp-oVu zPG$H0t>5kaZw=>@Z-E$Mfp6t!%tA&J#WBBaD-Ng5QRRkv@pAgJ%apX9i8WWxKhfIE zj~jhYh{G|)OJM{kI};J#N?YSyS*TKfR3X_z29~&z4kfFb_ zcpP<|1+wX0KUTLbrbn#x{^UbF+ia^2xZ~>>dLa5zF^;1U5X=B3@|9kv-v=N>W2?uOajHTfOTJEVMs$3mM4{(tkzY z^pAU!vuZUZhFntJpqg>*2t}nj|Bp=2C1buJ4%A)WF?-|kr);A*NbUJPq0IgrP6{5x z0ZqJWKGZEqf6h3Sf>wpC_!<5CCIKKcuIjdp- zJ2{KQEXn!R2oD%3!DJAaQ##B(@Tqjt#3PTx8A z^axX;j|VQjA@Q)G=K^Q?fjl)Qh2F97@U9KDR+fV0A=l^3Y~*EREA72uVwaI>|17uck?SSaYU{+Koa?)5Wd5f5p@ z5adh44Bs{T`&rkE2IS( zS4Ki2;3Xl0ep3j-tb%$?_e9m@l6&WX@a_L0Y$iy6PN0#^dAZbrZWt^lCzt$2P{;(0 z840A0LkGZFxR@}#qUDqn&bYkIl!Af5+GqaH1an`y3Phk%8lqTsB)}$?07jue`4#lA zlRzrj{-dqkV=utz4eY}Q6qt3AJ~XiR&+i8+JqvimiKPEBzRJe=8`QkBuB0OzN4jm4#923_SLK9VE-+tj4E>=b2zH+?(fR@9{3 z0?w9l5H}2z0)x~}zW9|#veY%;Tak>4Hvcnsbr|+qNNb{Shqr*!SrwQiAX8RL$HYX)Igh8$ScHkq0zTb>(S4sb3IAR}mfC(#V=9MR>@7i}<_tqmYi}?6@ z@#}9D?Ts@WBru-Zq4eBeXdxtt9<2<2oRhz;4Zz+F?S7Una1BDhKs;O*skPPJJ;EcQ6XQTeg#B%dTh}d6m^qLpa{C{MFyHgMhWSDuNytj_q^}-$_r9*R=9+7+xeN^8@fcEk zh#vf;Dzn(tMo#)cnUXKFloR;loM` zj~pz=EqWpj?!p5QppP%q#SFw@jq$y2pb;$xY*y3n2FkbcxN0}2!NLqrsW8GU#VGj7 zF?tG^vo`;%JrB?jnxB2L?k5%eW89IttHSEKsLG>pxr7GHi!**Edc1D(6R2|;X1ylM zbcldn2$vRxt1P5IJ}o*roAcMSsw!Di?K91gh=|i8ENbZP?PA!n!q1mmEdX_%;Uut) z1q<20Odq}}<(L~#=!a}(b2_ZcT!CqcOlgKaMPPmF4I0V#p#DhL&0gJm0Fy)ZqA5X- z`vW)|9JfBu$gUQda4`hQZ22NKNtV=F5@JlD1e&i4pq{TM^s!pbgiGO%f0kpvxm+Fy zEF)Cis-Wi+9*wFxgAOgLBIBd(QLeoiX&}~#C_S)h)*azp<-bH`@FM2^vfyZCMWny^ z<0Ne(FlT)M?QQCdF!n_~VG=S{!oJ=?QCD?8N{rqoB5md%veW?c$IMqdHgYKv55VY7 zn;qiW`3q=^9k65gVcYH__{d!p(NQ4s6(WKt^#&oGD;2W9D$diU6T|)#$Mi%QsbN}C zc7=wOW?#GHg5uKnN&k9_*V;dq-<*842B$Pqh-_D;dl`0Ooa;7Nd|E9ke*C0eN43dkH3s!eaRpa5Udj+5| zcQLPl6s3>k10q9_yFRam)A4WqL)P17vgy_s z)kv@{OAI!;R29Fcx2sE$v_OR78lwI?!o_>lI3IWWgk|_hd{3RRS-n)P3$f{78Vwl@ z{b+N!N!Ds@^Znf$iD=>Gr#g3fcaN?$DZ2Cr+9uQi9rPz{jSYmA*@W&0A$;PpU$+7Z z$$wPr-$;Rh5Jv%=DSC$wKR7I;Cj@Hj#7HBLr825ea$jnNCx^51fF_^Q3ja+lF9)Q6HhH zRpW~d$+WX38BXztF(YW#eP!#lg?$>IKVaJbPp&yog-4&T=fAh(;|0$iU zLjHBmJZso({^h$vw|q#ekq_5&w6ZjCw4}aFhoCR}eo@bsEcT}Z;Ng9O^0>oNC~+Q~UtXdEJTncp=5Y5+~2{+f=iu44BEP@%f% zG%H17*doEd!z>Vib|pRgFjI~1+v!&lj`4jkN0^F7`#?5)0DVA~R)h!htg_fdHD?x`%s{g*G>TDC8? zg1t+YFS6A4u~B;)48;tNTjSqM;bN#*;cPWaM-d{UQ0LO-4)x!@pQqz-u+OJvi%IG^ z)Kw#A+YlJKY-&C2@9#;yoP*?FGq9X5l#xW_u)))`Atu6M>m$f`qp-l!46`FJs9F*V z&Hq5~>59MknAYWRthKf#7MET{p+Y&4j8QybRFPaj?5I|zjfwxRXP|ewaIXKe!S}Nd zfdvdos6)PUw%z90*&qC>ju`=5)vjCl^P2GrZfWc8Z~8?gOnp)aF#s<>iGdT~%*qCK z^F5-+>zoHsYs?R z{bf5+?1Gqf3!&|g}JUbZU&e1n0n_GI_{AJfwY)7YvX9rm5U%Rl^u>zJo5 zOCOwU?xSpmc!o_0- zr%P5IH`ltv{OOi8<*}8~Po6WdW3*ZcgyZg@FP+Wsk58CmViN=#ctU)OwM6f894@)X z>S|R>KIbAlpSlg__#waet3w2{Z$U5YB&d{SJ^7K$lMxcT(r?!OtSKvhI08OjcDu!@ zfe8H^0Ra>2@NkBP4ce7>~`ymm_MG%2dEZ*pZlU_v~$BC zD&ePpP^COfR}-ewR3G!gr!T;}Mtqb@rp&3AtZ#0+xz?)m)o-X!u4H1G}hvpIcCsXar=|#UG{S2?UVj$vDyxpFs{{)^Hh(sLiIG^f{d>0-Ll9ph$_`dSfV>Ns zIRX&w>0khuyz{i1<);OY(-bNUqB-|zUP@?gDOjJfY=YMEwYv{5cI+4#b}#~sKPUR} zby%E+?gf|vA(>F+qhl3gjq{>6uKJ_G{H{Ctv2RqC*77Vro9_{H%oXjWtz0aR@C7_n zjIHkJP(93F@P<4Z;3ecBoysRW`367t+nXsdNKIzW6A(3S&)tOPZ{LSt6lz$+4&B2> zTD}Rohk>JiPRw;`&$`a6;p^FD&(KLY&nz3bl@9IErT{OD4hNNzli}E}u1nV$6+5=I z9d7O70ZVxjU53J^&UlyFOwYbwJ%(xs3#0?M`A_$sc7Ky;FdCBk8@XU|Tl z#%XWFcA~R?hN#VznKScToWAyHLgiv69K})gGzoBrEFY9CEGg><_uYVO^9tvxnNhy4 z4-p5j+(%-u_jBI}893WxMv}Si!Q7n{{O$jWXuxP{Z=>V3lhe(S#$H2OL zCzqyUdi5nqGDagih43OZNg*o`-g&W|0_Pj?m{YD$ixw9w;=}~ z`f5!8)1n@c964XBj6GK>M^>`+*F-OMR1uCM-y~L{dKQ@*b-yZsGUKddT|b)C3EjxR z3AqI{vtzwqi5>4EBMbyrOYmk%UcW~`e*M{($%O(!&@5H03ji%6{J6L{gka55aUjHC z)o+7i%IXdXQo|yTg4d~g{_Gh`pAajnqIMH-ML+3vvm5<)j|CAXCMHC>nO%MZ%e<6l zXxkeb8!c~LsbELg`#u8Ir=MTmkpX`LlS$wC;%;L`c9nWWqI<>&GHt1_Oy&VO>HMTt zW&6{ZujbA#Wc_HieLSHj7PYkGn3{DOH3a&4;%h3#h4rbK-7+=FH%Z0HI8+RwXoHN@ zH=V$Gpf|Kk8+sBV_(LD7ZJ=&N^U7^mxGFmH$Z2azXs=61@CW&~sMpq$Kfl}d1@4-Z z*{FQ8|GulNzCXfCk(+(;hegZ9VEdurKa9}^{r%SS60l3o0q{he z(UFoOupvU#@u^3PcoOv8h1U{?9LTV%wtG@NkB}@AZh`sawi8)eqwA}L6|57btG9a z(43e;020s%oCK(C?dm1Z)4$N?495qIIH8PaiitJf6MOqM_ZjMJS=t)bOTqg-wBNyJ z?ZQ@Q(D0q}m;5{l%+n%`DlpAWSKu{=!^u#z0wvhIhS3)!s3Q zIuUAgQ+tWC-g118UGti;e)3|M$DT9-YHd;C4vXTp=xO>f%-hgH=;xu0CB&G_rpR62 zL*#jUF;I@Qzivv@J}tGYXUdFY*xA?Tk4jXZ+k7$Y`e?_pUT%5a({#W8N3p-KG+jD7 zgr(KY=klVkDNiY;GyAtxNgBzJVnuRI4Vb&rZn+2k(UEy)JEX#p>d7YZ;hqdM=0pY2 zQ}9`SeF!$0cUXjZPJqbvFM$0A2DwvUH&9fg#AK;En{Q6{`h?tTKI2}Ps!@T$Ky6i$ z7q>gf1dg8tlf4StdY+4FOz^}m0{a6~j;My*xgv9Vyx#baCvdaP$Y)}_m&ZG72ZbvA zK#UWak?{^n#zt}J3?;bNQ1~?rmT?U%K1odo~G}^uS{1{?-{fl6s#IuM4+WnldV{_U0xdeVje!ywbep(*+J<+quUB?3W!CS-e}wG zX8iUiGuO%?*-4-Qz%4U-%&8>*1H|u8xtS!QdP%;XHC{0t@v##jqoBG2v7Svns=@jP z)&!nUJSrD4(VP$4tx#7P5^k)X30jh?@;N1l5GdTYOO9?N%p-7N#&F4~c~wf?yq7p| z3{dwpV3;i+3yULW@wkV;N*yEI;^yXtF&>yk}t4B0ANc`>^AmpKjIF z-uqT?YS8Vqk$TRpo%7f516b$86nhZkKtsB8OKu1*yx7E zOfz11dL$_(OGiO!YalFWU;OHuA_ymOS8QlyA3t5!4pu~?=g5(Qlf~+%nPhA*&bc`_ZGOg^rA-QJ-vHTN)^eBUl_DJ z^H!qww+3iwpYpSJZ>!Xj@dmUqsZ(4yN=I}x%FBztb!GVzQvMKA=cMClg|%p_BUR^& z^H@`&T5y>A@kaHis}d5AmP{y$^l4NG$R-h;j-n;;#Q6RlL-LTAp=F;;Nk@#0GX2)4 zYO~tOR=O^Ry}th&0>GY^0Uob-tEWO;))_HQZDP3s`rS5?D#9lXDZxpa%M__gTU9D? zDzDGx%XesQ$@i$|*Nu8oW%HyBM#lMLrJ?;9J+*wM+Niu&V$#DiuzVP;ZP;~BQXXPI zso7S(1fm}Upw9;rtl8$ez;bgroBnzz45gYK#$SK4%g)Sfv)TqTSl`S`t+jSsdTQBq z`St$7R2Eucxil(GHr|QRNw9oe5ieHg)tQ+A$neTwaR4EeCTwaePjv9xYNhy7Ef_f5 zMER^fP!)mb0v0SoPoMMPN+7Hp_oSv=Gb2@HUKi;V z^^oUj<*7MUD~hDyffH7x#ig!FQTp%g83Yquhzy^Klo-Z0{G7q! z1KYsnn_^|+$GykM)qD(PE1Y)qZY1S8CW^uUjr&+7&sj~ z?u}J8Kp2qDEGv_XeUL}y*4N*UgMaYhLrKDl88lC@zxg-c4yPglphyGUi4NPRHNV&} z5?jSY*HL*ol%Yio0Fpug-X4c0p$0fnCmU=nw6;D&`=q3CmHd(Q1$+i=n<2-Lt|7P~ zQ(a(m$h6Lt=LgEb%t4?(BWrT$??06Dx?mCSvH)H&&#ge-=A+LWuRP}x9Wxxg(Sad0-BC)%UR^a%2%YnTu@6kL~_>5?yESlREuYi@acO zNqtNH+VA+%(R%7ZMt@Q^B&CI(+Vo24!T9h{bWSqAgn7$Sv-fxcd@IW-Lw-PLbVf!5 z%)UwGW^6hdiir_3{{)!Y$34+w`e2h?W_7g+@?zoMuV2 zwlJv@&9UL4nl6M!F2WTvw=!NWOQ?;&#JweEi(Y|$DDfPRW6WITe!oXRK zaM_T<`$6<$a1}@YqOZ?SOB2u;cX^4rOB0QD;3dO zKcAf!y|yATj|(V$@i|O~NdCuA+MvbJP3;MtfKQ!_cVGDX*^#z$*p>W;fA3C!11u_k zpcKiOSen2^+Fxn0qL{1Hz8Fi^mnjORm7NcM7FL@iLMUbueIxEHi}h(nsB@U~_d&$g z%2CdCulEWdtDaOwcG!tl<4r$!eTjq-yL?~b)tGW_Wk!K~MrWB$qi|kPA)r2UKuX!1 zDkp1v#Q~A9N|?dl)v=xz)F? zT>RN=kV<>3xOsxQpu&_<-Di82_iqXhn#RhRnPtidt+~11p~iY0x7OWD_gU-FGu%3! z@4pAr%DE&u?Kj-fLneqd2zLLUtdg7o7XnQrLughe3M#r`w1PiOIgYBHeTu1Xk@R=!TzF%V`Q1`jbfq=7qCSvx zG^?@_3-qiIb*MOW$n&cRBk(wx!jj~ zTAkv?pT%jxG;Hs3bC65vtb#l2ZBM^#;n zDB&O3IPRj=AJ4pih_>c0El>roZN5xT=Di}GPvRqB#iYPn8JWt@D4F#y_nM99-Y1{q z&Sl%p;KuN|@yc%NY}B*q+z|SOdHEj#KD#W&UFo^(yMtAd|Gr6J%?)7~9;%>jz?)VQ zyvCn=Swbd11!R!#GjeF6YGPXe_Gi!?x(5)!_o$rz($hck*#%R-BDwK3mqBVt(+mEK z5mlU6HG(5ERZ(*qE_S3|*Is{J()B{|O@ym)*PD68Iy>(hX*`{t_l5R)Q>aG_ph?sl zr-@Zca3b#k7ued0QoMTJs8l}$#CE>KS)st|932#aFzY#pA3{i_z~0wK2zX3}mtFJe z6MYrK*w|Qye-lXNj|bD0|0Xb>^{2uaWK^1Ck~W2b5fS(Rc%^sVG1ZGGh}}^sh?Xj( zDzgnPdrUSS57E$${n&YsXcm>gjdq}$r$IV(svv~#eQoV4cFTuiSFwcVYVO2))%wl_ zGU(xYuT~>hWW7I`Dj8_`X5n-*sW0(-+dfvW6wh;)cnT@vr@&lx+b_V{_UqO?R15AE zgX)P=*mZ3)g>*fw_&*-YDr-w)4M!Dg+G*rXGvODHI1GlHN{51+zz7d9&qHvqYEdGEJvP2Ns8_AjVFYd9f&l|b zZPF=v1kXmAmCh_3mQ1bSjv-= zXqC)(Genantr9k5ACTUWysgrc-V^TXJvv`lbd>WEemPj{D7W@WT#uOvIx}Nz(YbAW zBI0%Up}2DmL`M`BpH3|O3QeP)db?JgfL{;|*exPjwmaFO15 z6TLu!_kC-ahW8QKwe<@uO|i`$JrC?>7c)E}9KA0rkX1-@omM_IxIAkhO82uEwO0lr zIJ`j;i<&<=5xWDC5=IE~kUT6cBTu3ea}VpkNJ>f)sm8;>S-r+yBlvqHy_Hdh(eA6@ zspx7Z4~}hC`TlnU-x?bS9sUH9rTD+cv$M0?-QN_*9q$UHM>E6jpd{=L=5Af7GrlYl zAHQ(44#Ce~2&}cvmcfuD789wFSNr`zycioF>do+U4YlQq;k3Vfe_G6VW_YZ7zh>ykd*atdGXltM8xFsWtc>J8A>VT{&Hx=CNxGp zv-&x|J^rE+9D_X+a9+_R!T|&4ZHz#5@*Yru)0MZOXZr7y?F z%~|#U(`(a(qGiBnQ!uogKuQvj<;k0P%;T{gjZvbeGRe+c>mk9*92v1vy(ozDTJt0& zf6d|`vHHke#gzq5i0jSVejD`n4i$k)YQ5+=WzUX~*3e=p8M;(DYeSHv{KwTHI-YPv zZXEVf;I+;Ltz>d@YrzQbVy-wsHDA|pM~(~n8%O(kz(Smp-2@*u$|aA|s8y4u zVU1MiSdLFxd_J`0v}eLisnv2o$#FfLzePzoNoPHlw;~CmXg)U1&Z|JUx^8!gakSHu zED^s>BN&=&fSrZ#Ut1Y!3gI)59!dkmVr|ufGV)|RC(X5PFGBmR-ZPv>x%}!SljCZo zUJ$^G0hAJ;n8~Do$NC60Sa99cSm3`-{RQ@F@?ux*5usf4*Nm^CYlnv${3<`HZ}!@d zmMmGK_>!SuOw2Vh-ZfOf#EyW$0MNtvkJYw1nf z=m2N#6a$2P{E~^{1=2Q`#>z)Ynhx|afm1She!mA(v9pLm8HG}=U=s^D$HpXdabWSzu6+IV&!X6G6H;HN#a?fBi8VFyB3 znp{C${aNpa_4V~+Sr=e#LawI$7b3}12-`%e5XKI=g@yI{MC+G=<+SP1G_Wur4#I%k ztF35Zkct~?<&pTg-7s{o7V%?#BoR~Tz-2f=rz|q0KgTS3DnK*OquK+h<;<>p0Ckr{ zS#zZ$$*7dqUO{7oOK2gGjEk&h^TjJBBL~ta)N^L9yG_A2b+A|E*IIs|^<&NB_iuUp zfDGuC_Lq=}mmqVP1iBm4Vw`E4nloD1W2dNob)^zHJTyg!(jCg!QebJ?1(whMS=)qU zGV`#=DUOLUu0E1+l5kLv+qsqxTH6iDdipQ&@xBn{E{KX`Ig1_w7=lMQIcfQ5?C>$<9lU>prisjf?i3_xiiO zIgH`d5p+%~+1A?2V{Q_c?t0c=3l@GucyN0uuR?fBdbJOrIH$m4I0Y>C>T0VvZ(_n@c21Nc}wNA8T4vTB73qz zq&|d`K(qH_%J+imRF|BU^Fw|JS@D0U;ges!>UbI%7)IFfWVo5chIsb=y@ot>+!g|) zoN|+hjDhc5t)y-1o*Lf{!kAXSNL%<9O`W#;E&UYr$k|B+mGPPQG|GWhP@k1`g0Ew) zP!O&&nLq@y+TQ&*T}n(ip5-VaQWPT4VU;-Xho1Jk8rNeK&nz39X4meAgNl0z2~Iu( zrOq3U=M&?FRSrfv0a0A%O)G&>)FkJm%DRTRS-oqL=-N9W&*vA;va0=#eK!mMnJ8I> zM1)}s8q=YSI1dC1atUeFpoX;0&lXn@)_6Cyr*HKIiR{ef@erSA_?kW|0L}b(SV6 z3mvnfl61E50Tie2)AkK2?W*%l-NYWwiSes-z~2>J-E*MQD2oAJ5skC>Yu+dc}&p_`usQ!(?TM^NbG(wIPWM zs(-BWZlYdRPM9h5z3J{-?J>3qCIzJ|?Jq~JO_xV+ML#0_s9|=gl<}T>W7^U{j2m~+ z8ll5T`W)1_(7z&Ae}sZ83HD|DVXc-U^%4{A;4=3mJ{8p|sqi9k;7yUtSw9{2Eg7{_ zCIT&`do6UkG-LzU&*H+S`XxOATmJKff}0AdJP~zUI73R2kvttD#XnV+7DQC-ew0+t z+O9Zq>F(LguYUTSD*M*qaRjCf-@g`68ym91`4GO)8Lnk589quLZC5Z> zx=Q-kh%;*|4x6s#$J8dQkZ@BKri&mhRRhpr!Sp(5rnEhk^&}yb+AhJvB@OKulC7aj z&4*tV;ZZ||3iAf7JJ0BA9yUHUT{+x4U0yuUR%A+*N?8zSVWtV+_Q`~U#;zoP<*7Wl z&R_buUvS`wX%XNZx%}>@%Y(j2l5b?De?)_9EL3hhKwE9j`PDncvLEw8YDK9R!8HN< zhw(y@?s@GhyO%za_gG8bCD-N4@w9>-g(0!lGHc!y)J?(6%JlI2W&TnvYjdPk4#Tgc zR0J8wjo_g)eOvac0A0bkB=DjMVC=UKNG7)WkCHAKt9eV86e;~Istc03WnMOkA<*JR z4$~78NU}beqel6uWEe$=f4^oHSC3|=Jz@MP2mhOfS%YTgHjb?Urh@TVC) z7@5|cz`o5Y+O`=$;DY<&t%U;XpC3_L17YC3xCePd=>G$ZxKzN&cns+E{Mc@tFe%^Y zd)!Z~eRzsDt6b?mCB6ep1Mj`t>3P@n!@}%fO}mqyFpZJg9XKCR@3drmWw? zix!M?x>^oW*;S<8RbW6h^}q+{P(;t=urQ+ZH)jI67S}O<>cr0hh$-?`)c6$%hEvM#7^Dh_rq_ONp5s8v*TUyILYYlUkTRFb{GQUi04nYuy$vi8LOZaa}FEtCT6H|ke zZ*hh(&~wp%+Q}rCCS;Khyp?=CX}n^B@GZ;rGH*HLYC;N?sxd`ICf-M<5xe0p(_$iV zh-`F(*Tbg>gU0D0q~IyIHFxh>!|OtmY_>eCLMdYzSw1P5mA4FZDnfhqs+6WcGR^7J z&a`|#M~OmEkd#s)Tokml`Ba0tVC0=7gb3PBjG~rcRM`YGCOR5Gpqq`m`wL@YkQI7d z>^1uO`q<#J4%}UYbVnD5V`a8#NPcz(yLhW~dc+b3?Pl)JDQ3^L@&LeA z%CmJiEz@=gwovU%-PWK#pCaR=CIXr>pCr1>+Zs)h@YEK+6;tR?2LH?VSHAnBIV&vH z@<>A&iFOI-cA3%1fS6*UBzGdMT5HRTMf9wN@gY=!`6hi! z%7m}BJ13rGr7}Kiclat{dGPyUL2QcWap<@00SUX+ShqDebK>5WiLTC&Khs0ahdCH% znik_OUsoV(+n?Saxvs}d58ZEPKHOeaKPD_)eP0=uTN<>`O>i70?}Z(1UONlWmETJ8 z2&23t|GJqM`&8q*?SLc~-C}j#tCe>y%inKpu#4(N9L+2To6^8_U7!F7_F5Cv)V4B@ zJ)Q4wvulP>a}V^r)1*5z$7)p6_Y@k{E1>OIgpQHK$tAbYnXtmPKEYe#W|ksWuX*>Z z4W#3zk(j zuw;6=!Rh&@VzAl|^k!nOe@&HVoTb5T%e>^`=>mk0e-v77y~5CK5XQ7wVHxgH_S_T# zodq~-+xtGChZs2Y$Xr@h7B{;l5V=cQx$db%cn|Eqk|V4l%5j-Qx(-Ym8X0w*TL++f z_@qZ*?U93r+*79=j7DLng>Oi_IRSU|hf{!+F3w)!D4_!xVOTnWhl8O*83C+;0f!xl zO^#H)Rn8!xD(5qO)cyO_{vuTBtNqa8eAd_X_sX&1BMC6*a+`&X&jR(7bGu}{{*^Jz zi?BD4_F+8tNuzyygPmg1*yHq+U>u?~n%@ttHmMlxqzs=ocxh-AytHzBa$`Cw97-^% zfebG>Ek?~2wp_Luh@tXk>S2~95U;?BuL~#nm);p`Uc3k1T11#VxSgu<*uU_DKy8PmwmElL-Loh z*RYC3kLIyB-@Tn*sA6ZfGtMPH?6TpX*1O8k39azEyO@VWX2M185O!Q`1d}|aLD}tY zj*}Wa`JHUVDJ~18JphHf#*u^n=5Q3pMhk!du0pU_KNZ=xD6^91N(T}{ zG>R0>!z(?Ae!WX9tl`nHSBa^Nj7O?bE2IhAxo^lp4_!(=<*+w+5r0jEUjsz(*#PKj zpNs*6JGTcia7Vnf2VcMXUNnBm4bypc1_!VgOd3K5Z$F(Ey$^K1tOCFH_LzG=IPXk` z8Qf(9{*P@F#cKZgVZFXmM9peZu#vZMBl|VcL~ktcTnBbvLBeQ8WYVVVcuW+r41Lj| zwL3A)>X8hS@^qG$Zz~0*jJFLPUZ3j>ANuJ(9Jql^UNK&B|GB8f21v(EK;QxxwVf-U zN0@7aMpWL(LJkZF*Vi~*R3j>qZq#ybH;mRA7Anja%*Qq!1;rMIu32*(ee7Mvbw2#8 z#Kn}nks>dL$OG{f>-0na$v$p6^Xy(V8V+WZzj94H=T=%G4wL^@GIJW%o~tN8sB-TT zXl~Vy&c&nFufIHM%W*;Dkk-)bjpI}&jHw;bWWKP%e$D<`R+6RedQqd_?a*D>e0s}8 zJH*{)dup*to5>=_Rce{l+-q`O^0LoJuBi-ZU66aWUk55JEWsMvfG80?$}mWHcU%{_ zVMhSz?g#{dBe3E$YALj=6qQT*<+}(6wOtDOjhBT7n+q0}mcOJf0b!vJrAbhjc!R_|;H10qIe?mn}}dw%(qf_!@79$06S@9uM92 zO3r*c^ndy(F}jK2Na3d2ew?FCD3P4AZzXDHb^#=GMMtJEG0?>@ikqlOygae6`k7(u zkS=u&Q0B6)+n1!cd;z=NhbB!;kEqw)h-hzFjbsT}4tRpcxl>e(l~adIJnA~S-YfZj zFeh?NNsBT87&w;O`K&soDxK@+1-n=ZSJVnEB*Qb5D%dDJe_jYec}GpvRE1^pS+a^| zCOJn1vHom+7VHr>S^|G`{QS(pa!1TE0!J=h4y@uQd&0D*k6_U1j0xU;APe<gb9XKY{@>+<<3Rx{+wfrmslFo6285ol zn_+zTkm^F)+9i{WeefREgS?<#g<<>QM~HK7S;YkTU9VmMc;burU#BQBsWdt@{NVuJ zK$0Jf@>Nu}Cw0KF1WDTU&rQ^3(#wg2Ukh5>onr1iVtOYMm2=AAOO2+7WTbrnjSd`_ z$cMv){cA+>bD@FI{^A4AJR3);GGq7f)UK8Ca-4AUlX$T_`<$VcDndYLVy-l^xx6Ub zN}~62VNbuRiwxS|R2tSoZCFk=!Hmrlm>XGAz?y?lN zYaE%|u?ECJi84hcg<>1*!MjT$997ZT(cr?Bs46Q{iqZqOV<0u8a(UFS33d~>Cp2;M zJOkM_UDCVvcJ%V4qGFWB2QaWMQZ0T9NP)Z1N%I*@Q$EPc9UC2OKc`HXfmT+0!Mm1! z8W|HqDQXG$NzOc6uy5Mu9@0~4>W&ztHSI(=^^#5`co9Ac#* z+0=usdk)&&9)pL~Wbl~+y|y*WW3CD99|iYdY!k&x_+Wlw%;cKjhaE^mvSOO{B^o8) z3z!Vni~AH6HH`2bAbJ7xyU%g^r*6%WQ?h1qqSrkfZ?x~maxca7{|9Fq?7o$xJ(dCP6j?_R2pn%$Nd)BHV2(OK(e4@RS}k zIVx(Ta%uk;0K)sv5LT(=z{a4~xZ0cR%MhX$b*%966pKt1gv~VUnrPL8XrNrHvr|M$ zn=vX?MNN}Kz0f;)<0>;Wc4`k*w^l@`7Jr=>!wd`$7SpyuSayH^P-hT!DD^ zUbW2@98s;jfM?>{E|$O@3PGuh_og)Da0`;QOrndk$-Pn9sBjKqRe!O^Ssjk<@S6&4 z1jNd+CtA9@(Wz@?TX|OB6g74IHOfodM#d5h`h0$01+i$hqgm@LP~JCr7$N-ie$m>oM= zGUE%Ra52Unjy94)os}#*l0sqXe&2Q}Nf`fosTz}}g!}hVe6VI9y}j1@hwvb9c=J;R z`waat#7b3v5Cy}2!eS2MX0rj<@r0J@VAjz5{upZySA{9@Ux`?4g#aWZQB~E7=VoA| zi=SV$6*s>-IA#37ZRdv@6_!&FQ7cY=R70Dcr<)$$iiTVNPqX)~&xgJcTn-}F}?ADy!Oklp6z zY|PMSbWi3>9uMooHQK<}!_9kM#f2vguJiTyC!a(fl&%bmzJeX|xM_ny$ z6tvpV+iqIj{HG-dGqRL1*RF5tSAU>FIHdbBW86-jiIgu!ncUA=WwNS5mdV4>56mBR ztzLut1qiRF0L-gJIT_CVq&?g}>mte;u^33}|Akwggwo;|7=iK~N>2{N;9VM-fJ;c9 zNVvrrc&6$gEV_=f!KTJjL$FSDql|S)kS^bZ{raXDTdmYCi=Lj|?`#z)P4g==N;PW` ze!dwr79#43JvBrCIG6Z|WVJW~q-IWeN^GCq-QRz-wsrU)%Y%+U1(%gVE+r);)9mZ( zE2g1_Rt6P=zq;O6Lp1?u;q!CEmNr*sdcYwQA>Ydu1}PnA9{w2J*fZSYXZkX zVyR$lXG~{4YdswxWC_U#KnSjj0R-EeWJ(v1po8_k5R8O|anOF1s~=5y_ycT9#okBrJjz%=V*s?zJ{vB=hT&8)r!6wogTf;%@fNTdb|rI1^ZXM z6My~LUPIleNkC$7ws=e0axIhd7+e8xnRZO#=VCEYWdlB0~D?0a|dl7#`f zlJ(vw)2U)gFkBy9P*B%=vuB&Xnr`x4aUgMTanKr;$}R#p3VcGC;bx1E4Vkp41sNOS zX6zMn;E5AZkzQNfDdj6Y7OgwoaVGrs4LbHvDSQK7g9o_bkUY?)7%u{+Aq>d|fSBhM|?<1EiN)#bU5e;;0A zU&|C#RV5dzMuAvKqYtCF1@U0mj1%D>-SYJJ|KPsF#eMeNUCnB!s-h&9J#ri~Bpoui z2uoyXO8IWYVS?oT$5KXnOAz*9{eaSPG3GR)oy6$!89Foblw_V*z{!^W_H$g{8LVze zq_d;OpX3ER7D`bmIS1^7jCdYR=zt|=Fzo3t3d3xj(L^$*)6hz_?pXePhXGt*-GRm+ zF2km8e<1q)8#Cf3Y)PYYk~pXiCe-RMS2>{a4(PQQMyU(g15i?&am> zplAd+jh&xq;jbsITm2r zZnzKrO^!ufusJ|pYeM2cARfUJ71lh7%J=+-Ke*oq(>OH-KYZM|ypjKuvL=i`vglA< z+0QOaBmKGyvS$UO4@)=3bgiB3al3~HU-%A|pf5oMf2)WEX;vzfCSA?S2gO`u{u!G7 zE*VLo@IQahoy5iO#PVhm@JV;*|Blf#0OK3N*CjQmYEu40TDxn8BVokr-R9xRIR`i0 zz1Z44GhxZvAc>m3(@@mfA!nrCk^7oc+T_Xy-N8CifV!&!l-N$)TxTh}(SLt5yyXt+ z4vDT@_D8uvcBvl` zLG#COsu9J(d44A(0!&g2dXX6o_Ro}YaXn8v^Nd(HWgsEF@>;t(RBBL2PfcYIB^!m= zv9@=0X?f)R*@@LFxZ<^b-KJ3tuaW@7YriURJ8Nuz7QQ?oqHe33pNZT#R%JDC` z^5%;25G}UZVRJ|7Uw|wG;p+Y?dj@3-C3sB_D?~w88+s+IY}`vos>AQ+=*q$FSPbBM zthS#!h342Tytlx*aFMU*E-I4?fjnyA`zDETJh3z-7MtspBa?9`Nu|50sGl_fWro|{ zt)Hy>P-16HNY}&Qa>I+~eNk~B$GV}CW_a&BaI8)w)0w~=`#-V7V!eE?+)VXQiG;{csOmnW7-ww^wwX{?!_F@ zlDp-1w`t%L5r;W|X#iqD5A{0Is7po1>KoDi5pF0xoF{yR03TnYWZjFku;$djOUJcy z=fh-R$Fo8FoM$ALyQ{x8c`#Z@&yUTv674qZevF2{qQ{lZt^qqa01>bPTSa;?YHjY3 z%W}qj)I3Bd&znw%Z&c-^9GjC--ZGjY^)se#HtxBaUk)M4WK)F(mJ1z9sa3rP5B++j z1T<%CF7xi~@8DGM10H@Nj)CD`T4972Mj?A!vonA|t?}UD#Rk(aa%n?ddiWk8&&AdZ zXRQstu|n(WM?3L`V+N%qO%IDXiNDeoJHwiyzl8q5C`6=-6oXjah@L*NhUhO%vFktu zY3L;mAIsLsL|@!~r$&S1;H^Nf9A!B7{Fh*Km_~US#y7^iro5*-vk`SnXFINb0rxo) zRo}>x;Ca@{X0HNymTV`k<>O#XUr*XYvVIlHV3$bopp8kf(OrqsrC_1fb*5Ys7XY>%pgA|SyV zMiRCwsKNR7PO={gD2rYJ3&PZG;{FVmUc@t3=idibxx3b>TYH{HRi}IRC4@{pvmjYn zG|qSPp#YOV{7T62{xRj((!A(!si!PJDUqAas^z9oBw;fz4Y0|4qjM?`IMj^30B>n_ z@*XlIdGjH6|12?+vHwaqON-_bzT;bF4jmw)qTm!oa?_W8CPupH?rl$N*k59R31{+xa8H^7xG_E5k1%mn;mLXoHYVvT zFu3&vK#b~vyVvX!pu@&JjQT%}y=7cfZ`VCeN_R>t-O?!~N_P!i(jr|FXJ`QdY3ZS3 zND1jiltvu7JEWBq_#eMu0$^pQw)l8~VS;%exDJ zI>zKM%J-K)x=u`fuTSFH)|S>WpUiYvi~0U&a2YwhnZIS6X!S4#g|L617H#C{syU}m zy4AOni@rccw|Ttw5wxUl?p3Ms3F@X0d=OL0#4xO=`@94dR({=&Y<{0!TWM0AqQrU^ zP0=8jY{WJG2pUh17LH{e@X~#s z^hqr+3O!+3XNyhnm3V(D%D^7BhaQPmvhy76axIC1o4jQ359#)&r30QXuoPQRh2ERF z;$FG{owqD#PM=c~5`w|u4RP`?e}Ui^pbE=~5q84!*KdmumvUoZT z_6K%(pw)}hrry3GjJa#C4Za*+`uZ6h_fD9Puvn4Q)H;*2*X>){Hobv-SHcsrtJaSYZ#Jv017Q8P6?fhjEJ zJ6F_z;>!7VHj^GnFwf_`Ujl+VX-dtY<^3$t0k`QcP1oEB#|Zh8cBn@4HNl?=gr|JS(9joXn1R z`_wQ;t4wqs5r1XRIWhy`2qx~j+D$)Y>&&Rh!ZUuRhgrCue)O206;qO()_lkcn68HO z3ifV)28WCuBM0Taih={99{=b}7(vQcEQssXLK6{aTqJVvAtNK2+NEvcM5Yac#1;By zT6k)>EcfS!aD1edDpgnlxG5Q&6#}~{ZidWrtPMShexLI6bnO`nW|u}5*JnrH`v3K+ zLBL52Q+XGuzOfdoAu>bCp|q{2ZbqO=^>+1kk{U?96Fg56kJnQA`e)Vw|K*`Q_@=H^ z@BUHl?TwF;!?fJB6`H_K3RjRfws=va)-Wh{{?{Zxul7-MyIN-;CfF;VkkMqrQ$^_n z9SNLIL>VmC79w9TZfo-`U0<-&!a+RNexL#e?4FnVp1Avy zKVJ*WU&MFzAL4uY=VpU)_;4nDcjq)c->X^%<$_A=IL8-6M$c;JF0OrT<#;DaBAica zapx<=%h^RsgkF@gU|GAF#fg_|U6*Mfo#>SMDh>sdtQ!6eO+YAsgc6Xjgi0Q)^Y>8* zwgniwazkUEX)w#igG1VLVqlAugjM~O$&;lRtQpxqzRTXKDXn0+N$mQ`cX)AKjEo%P zA1W`NNruOXb=0i8g_cz&&a&B$Sx)qMtV#8nHRA-RGl@7??}eF_y^X@G6^jQ)O~Q;R z8o;T>N0cf`ZyTF)1;iD@UoL(F{w+Ozyc>V~KrFKTFR_jk2VLjiq-Qy$IfqOgb5@dg z&p#~m>`OHpMT4q)TBpX`uGyGyxh8V3YYwVH`L;4fDFsSN9{pG~-D+G({(O z9xlh1>#bDz@MJAx+); zB)(E=8}Msq2x_}Wk8LMZ02$GXbBvIuB`;UHsN?DKP_!>OMYU2urOm<6z2ja}3uguX z>M;JPeRQynpTE&CeK;EPPl}R-N@GxK_<_y{qV9dZ0`!CrA4W+Wwbh*<`)ROP{L#r= zg0lTs#+BD6A6f^TXvn}<_IJepPo`T*|Es4?@u#P*;pbN&kfwb6DL$yTzlZ-B0Xl{N zCr~75w;*RD!)(=HMGT)MELT!-)HZLWMX)_L1WuB*N6#gBhbAo*goFrvaK;baWN0_i zkC3v+{>`Pl3@a6<*q5--X;?@HxUNF_w>#j4?bT3qAviJunbXruV| zlfcvQcM9(k0Nd+oojf39a1VGbks6cMF&B}4$`Lg_T)Oa)OR|7lfEx73nT&t=^=cT@ zd9c5)s3_IVVmYvut!E)?GGupfoX}F^iO?xS@?rztKfzKApwtxU)x#VM=v=M%_nHul zhSu>^NpPwcuyjj+8DI1MQ`j%*?hnZXgfm*`ZF;!6DtBm3!}W?4=VqCof9%c$+L|Eq z25_k+fz;TNJoK?g1OHJW6sn^hGyti| zSaR#|#5du;C(tawZQNiTag6|Z`jmoX*8)462ZKe)Rp@-{!s5U9xqJJQ7j#J-^{&vUdU|h~C8y~}C&y}HseD=9f zvYw-sQBgS17)9Ypb@pI$8zT&Dmyo9P`G7F;PA1LUj<2$ssG4xSRC9*dhwuIc$1E!h z$abbCKKe8mlM0J_ZcuepQz3SdXk@%aBnBr(pfsN!7I%o&=NZn?NTFNlkLvk))ke+0 z0L%2N5^RICK0GowBy^mN22D^1&rXhc5&PlL zo`S8^{pUznQ&|&t)nwyCq3_BGbWwT9#jS0-8oIlZ_(r2_G?KTjY3f-;T=qOb(Eb~= znlDaHN=kB@R1@)=Bo#Fv_%j$tV9uaEW{vgt7cEIH$@(9C2i6ndF9i>%cK`EAcJpc> zBXOZ6`UU+y66%(_8aG--TIjS`4Wwaq*FzfMu+UpXG?R_Ya}jjQbVGIxhh}x=TFSCA z-RspKUN!&5jI3B`_P_6-0%uCcjGxF4tb`ZYE ztIzn#u>l|GmX)!9k?&UCPDrp5wkM(x?UcwtbFuIGK4gv&x#}A}%|-1c`NP$%V~Xyt zwbZSNVwWC9QX^%Pm%^b{#Wdqnaz)w*1Mw0l`0X=J27mD^wQ7-}&BQWQB`8H{)2Mi1 zCF%UtVMkfxnSm!Rn&YjyU*73wbpR6%5z$PiGF`L^H2p)j&V(=zZ}BA> zCvDmEr#DnL^OFYmpF*xM&kwg}xSD*0Nwg}7(b+pLl^Cw+&UnkRCtHm7n&qiu)j#-vSrFDw z>^gOm0=}#k!iFIXxuSE75aVZL3lA_cr(^2EWQJU+l<}?ePBg=TzGq~qXkjI9=~Hqs zX0c`^+?;7W_58;5OprNvkZiVLoDw+pu|j8_Yd+bH4@%k5Ie6p@+=#{<30*I+{1sM% zya{1Cuf%mq?hhvL#5lC-7}5KAlwb5zCBv~kS@aSxZw%H_4fRc*`?C?S)ojD+_}q*> zX{-mkw&x%ijhe}UrlXI$)!$e)N%t`VHeS>kB_(cVQxpE#FAKI!9p2o5M@qrSh$_hn zMUAD)s)2>^)d_Op<9`v!3omC{;RCT)USJ_oXZEVJyzH5;J>eTq)h_d5v(VwVsN$nr zuu%46T;(}>?k$f$YL95tvi<3rYsPjv-) zV|LZ&Mu7%V^FpFc;r{sOpan*K#!qcVvG{skRLN6Q`Sr!*-NE90&r2s*Ok#tapN)&K zQ_@kX)NS08A?0HfJyGSKQTF z+Xcpn!_)oYV(^`|E8L%A=;)>69ID~ML`YLAfg4!fG%oieGjU4Om|Krpp15mSnS{Um8(D@SYoOY;o z#?H4>@Q*ZA@_tD}xZjZ}<7RB^=HzbLI1GSR)n3(t14|0!9v40(kjlSj!|(WZW$qnD zvvI%~x&Mlp=06;jNzM|*!5}g0(*MjoJDSHaRrw7^GBPvvt_$iOn+KgOvn;Kw4P__~ z9Y)JJ&ek9mt#EF6cj~0j7xt;gnnY|)R&5a%9r+Iw$q7|WR= z1T~6zFT)^;M43nx?WFT;f>Da{5i&>EgHo^!1R6a`Nf?YRU93qMO_hKdr$Zsf4#aX$ zIF{)C?cl_Pk{!77$Ts4J%J` zSpplNro+yDU&R|0-2xvcr;R-9d9%{15m(vbORo^MpJB-Q@9h)2)C=Dz19fW*DDC#xlx=IumCuKbn-m#vU9qx$DO1HUr zE1I||5cBNo*MV=l<}Ag5xCFZ5rJmwwR}Mq~Mc(rlR!A*{jVM=-{9)%`x8n@J2;)Y& zRB+18Ebb+_HkNXIp-q&Yji7n4lOPdrvFOv}xtn-bU{d)RTxuH!jt6_~ufS#
d62o9O;EJ)%1>l?;a@M8J7)D4ivH&e5n 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

1dbs3KqD$>nGUk_y2izY9 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