From c6f6c004cead472b2a417ca68b23428729d77fb9 Mon Sep 17 00:00:00 2001 From: Ayoub KEBAILI Date: Thu, 16 Nov 2023 20:56:34 -0500 Subject: [PATCH] Support of Azd deployment model as option --- .gitignore | 3 + apps/backend/azuredeploy-backend.bicep | 5 + apps/frontend/azuredeploy-frontend.bicep | 4 + azure.yaml | 66 +++++++ azuredeploy.bicep | 21 ++ infra/README.md | 139 +++++++++++++ infra/core/ai/cognitiveservices.bicep | 44 +++++ infra/main.bicep | 240 +++++++++++++++++++++++ infra/main.parameters.json | 42 ++++ infra/scripts/CreateAppRegistration.ps1 | 29 +++ infra/scripts/CreatePrerequisites.ps1 | 8 + infra/scripts/UpdateSecretsInApps.ps1 | 26 +++ infra/scripts/loadenv.ps1 | 26 +++ 13 files changed, 653 insertions(+) create mode 100644 azure.yaml create mode 100644 infra/README.md create mode 100644 infra/core/ai/cognitiveservices.bicep create mode 100644 infra/main.bicep create mode 100644 infra/main.parameters.json create mode 100644 infra/scripts/CreateAppRegistration.ps1 create mode 100644 infra/scripts/CreatePrerequisites.ps1 create mode 100644 infra/scripts/UpdateSecretsInApps.ps1 create mode 100644 infra/scripts/loadenv.ps1 diff --git a/.gitignore b/.gitignore index 1ceec492..ee3dd32b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ common/__pycache__/ *.amltemp data/ credentials.env +.azure/ +.vscode/ +infra/target/ \ No newline at end of file diff --git a/apps/backend/azuredeploy-backend.bicep b/apps/backend/azuredeploy-backend.bicep index d3cdf403..c67c5a52 100644 --- a/apps/backend/azuredeploy-backend.bicep +++ b/apps/backend/azuredeploy-backend.bicep @@ -120,6 +120,7 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = { resource webApp 'Microsoft.Web/sites@2022-09-01' = { name: webAppName location: location + tags: { 'azd-service-name': 'backend' } kind: 'app,linux' properties: { enabled: true @@ -314,3 +315,7 @@ resource bot 'Microsoft.BotService/botServices@2022-09-15' = { webApp ] } + +output botServiceName string = bot.name +output webAppName string = webApp.name +output webAppUrl string = webApp.properties.defaultHostName diff --git a/apps/frontend/azuredeploy-frontend.bicep b/apps/frontend/azuredeploy-frontend.bicep index 07513e84..09ca487f 100644 --- a/apps/frontend/azuredeploy-frontend.bicep +++ b/apps/frontend/azuredeploy-frontend.bicep @@ -72,6 +72,7 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = { // Create a Web App using a Linux App Service Plan. resource webApp 'Microsoft.Web/sites@2022-09-01' = { name: webAppName + tags: { 'azd-service-name': 'frontend' } location: location properties: { serverFarmId: appServicePlan.id @@ -135,3 +136,6 @@ resource webAppConfig 'Microsoft.Web/sites/config@2022-09-01' = { appCommandLine: 'python -m streamlit run Home.py --server.port 8000 --server.address 0.0.0.0' } } + +output webAppURL string = webApp.properties.defaultHostName +output webAppName string = webApp.name diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 00000000..23e30ebf --- /dev/null +++ b/azure.yaml @@ -0,0 +1,66 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +# This is an example starter azure.yaml file containing several example services in comments below. +# Make changes as needed to describe your application setup. +# To learn more about the azure.yaml file, visit https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/azd-schema + +# Name of the application. +name: Azure-Cognitive-Search-Azure-OpenAI-Accelerator + +hooks: + preprovision: + windows: + shell: pwsh + run: ./infra/scripts/CreatePrerequisites.ps1 + interactive: true + continueOnError: false + posix: + shell: pwsh + run: ./infra/scripts/CreatePrerequisites.ps1 + interactive: true + continueOnError: false + postprovision: + windows: + shell: pwsh + run: ./infra/scripts/UpdateSecretsInApps.ps1 + interactive: true + continueOnError: false + posix: + shell: pwsh + run: ./infra/scripts/UpdateSecretsInApps.ps1 + interactive: true + continueOnError: false + +services: + backend: + project: ./infra/target/backend + language: py + host: appservice + hooks: + prepackage: + windows: + shell: pwsh + run: Remove-Item * -Recurse;Copy-Item -Path "../../../apps/backend/*" -Destination "./" -force;Copy-Item -Path "../../../common/*" -Destination "./" -Recurse -force;pip install -r requirements.txt + interactive: true + continueOnError: false + posix: + shell: pwsh + run: 'Remove-Item * -Recurse;Copy-Item -Path "../../../apps/backend/*" -Destination "./" -force;Copy-Item -Path "../../../common/*" -Destination "./" -Recurse -force;pip install -r requirements.txt' + interactive: true + continueOnError: false + frontend: + project: ./infra/target/frontend + language: py + host: appservice + hooks: + prepackage: + windows: + shell: pwsh + run: Remove-Item * -Recurse; Copy-Item -Path "../../../apps/frontend/*" -Destination "./" -Recurse -Force; Copy-Item -Path "../../../common/*" -Destination "./" -Recurse -force;pip install -r requirements.txt + interactive: true + continueOnError: false + posix: + shell: pwsh + run: 'Remove-Item * -Recurse; Copy-Item -Path "../../../apps/frontend/*" -Destination "./" -Recurse -Force; Copy-Item -Path "../../../common/*" -Destination "./" -Recurse -force;pip install -r requirements.txt' + interactive: true + continueOnError: false diff --git a/azuredeploy.bicep b/azuredeploy.bicep index c4619f3d..23d87b61 100644 --- a/azuredeploy.bicep +++ b/azuredeploy.bicep @@ -226,3 +226,24 @@ resource blobStorageContainer 'Microsoft.Storage/storageAccounts/blobServices/co parent: blobServices name: containerName }] + + +output azureSearchName string = azureSearchName +output azureSearchEndpoint string = 'https://${azureSearchName}.search.windows.net' +output SQLServerName string = SQLServerName +output SQLDatabaseName string = SQLDBName +output cosmosDBAccountName string = cosmosDBAccountName +output cosmosDBDatabaseName string = cosmosDBDatabaseName +output cosmosDBContainerName string = cosmosDBContainerName +output bingSearchAPIName string = bingSearchAPIName +output formRecognizerName string = formRecognizerName +output blobStorageAccountName string = blobStorageAccountName +output formrecognizerEndpoint string = 'https://${formRecognizerName}.cognitiveservices.azure.com' +output formRecognizerKey string = formRecognizerAccount.listKeys().key1 +output azureSearchKey string = azureSearch.listAdminKeys().primaryKey +output cosmosDBAccountEndpoint string = cosmosDBAccount.properties.documentEndpoint +output cosmosDBConnectionString string = 'AccountEndpoint=${cosmosDBAccount.properties.documentEndpoint};AccountKey=${cosmosDBAccount.listKeys().primaryMasterKey}' +output bingServiceSearchKey string = bingSearchAccount.listKeys().key1 +output cognitiveServiceName string = cognitiveServiceName +output cognitiveServiceKey string = cognitiveService.listKeys().key1 +output blobConnectionString string = 'DefaultEndpointsProtocol=https;AccountName=${blobStorageAccountName};AccountKey=${blobStorageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net' diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 00000000..da48e12a --- /dev/null +++ b/infra/README.md @@ -0,0 +1,139 @@ +## Table of Contents + +- [Setup Local environment](#setup-local-environment) + - [Installing PowerShell](#installing-powershell) + - [Installing Azure Developer CLI (azd)](#installing-azure-developer-cli) +- [Provision infrastructure](#provision-infrastructure) +- [Deploying from scratch](#deploying-from-scratch) +- [Deploying with existing Azure resources](#deploying-with-existing-azure-resources) +- [Troubleshooting](#troubleshooting) + +### Define environment variables for running services + +1. Modify or add environment variables to configure the running application. Environment variables can be configured by updating the `settings` node(s) for each service in [main.parameters.json](./infra/main.parameters.json). +2. For services using a database, environment variables have been pre-configured under the `env` node in the following files to allow connection to the database. Modify the name of these variables as needed to match your application. + - [app/common.bicep](./infra/app/common.bicep) +3. For services using Redis, environment variables will not show up under `env` explicitly, but are available as: `REDIS_ENDPOINT`, `REDIS_HOST`, `REDIS_PASSWORD`, and `REDIS_PORT`. + +### Setup Local environment + +First install the required tools: + +- [Azure Developer CLI](https://aka.ms/azure-dev/install) +- [Python 3.9, 3.10, or 3.11](https://www.python.org/downloads/) + - **Important**: Python and the pip package manager must be in the path in Windows for the setup scripts to work. + - **Important**: Ensure you can run `python --version` from console. On Ubuntu, you might need to run `sudo apt install python-is-python3` to link `python` to `python3`. +- [Git](https://git-scm.com/downloads) +- [Powershell 7+ (pwsh)](https://github.com/powershell/powershell) - For Windows users only. + - **Important**: Ensure you can run `pwsh.exe` from a PowerShell terminal. If this fails, you likely need to upgrade PowerShell. + +#### Installing PowerShell + +PowerShell is a cross-platform task automation solution consisting of a command-line shell, a scripting language, and a configuration management framework. PowerShell runs on Windows, Linux, and macOS. + +##### Windows + +- PowerShell comes pre-installed on Windows 10 and later. +- To update or install the latest version, visit [Microsoft's PowerShell GitHub page](https://github.com/PowerShell/PowerShell). + +##### Linux (PowerShell 7+) + +- For Ubuntu and other Linux distributions, follow the instructions in the [official guide](https://learn.microsoft.com/en-us/powershell/scripting/install/install-ubuntu?view=powershell-7.3). + +##### macOS (PowerShell 7+) + +- For macOS, use Homebrew to install PowerShell: + + ```bash + brew install --cask powershell + ``` + +#### Installing Azure Developer CLI + +The Azure Developer CLI (azd) is a command-line tool for building, deploying, and managing Azure resources in a repeatable and predictable manner. + +##### Windows OS + +- Install using winget: + + ```bash + winget install AzureDeveloperCLI + ``` + +##### macOS + +- Install using Homebrew: + + ```bash + brew install azure-developer-cli + ``` + +##### Linux + +- Install using the script method: + + ```bash + + curl -fsSL https://aka.ms/install-azd.sh | bash + ``` + +- For more details, refer to the [installation guide](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&pivots=os-linux). + +### Provision infrastructure + +1. Run `azd auth login` to conect to your azure tenant + +2. Run `azd up` to provision your infrastructure and deploy to Azure in one step (or run `azd provision` then `azd deploy` to accomplish the tasks separately). Visit the service endpoints listed to see your application up-and-running! + +To troubleshoot any issues, see [troubleshooting](#troubleshooting). + +### Deploying from scratch + +Execute the following command, if you don't have any pre-existing Azure services and want to start from a fresh deployment. + +1. Open the terminal. + +2. Run `azd auth login` to conect to your azure tenant + + * note : if your using Azure Machine Learning Compute to run the deployement you need to use `azd auth login --use-device-code` and follow the instruction to connect to Azure. + - **Important**: for Microsoft FTE using fdpo tenant and Azure Machine Learning Studio Compute. This will not work as fdpo policies don't allow --use-device-code option. + +3. Run `azd up` - This will provision Azure resources and deploy this sample to those resources. + - **Important**: Beware that the resources created by this command will incur immediate costs, primarily from the Cognitive Search resource. These resources may accrue costs even if you interrupt the command before it is fully executed. You can run `azd down` or delete the resources manually to avoid unnecessary spending. + - You will be prompted to select two locations, one for the majority of resources and one for the OpenAI resource, which is currently a short list. That location list is based on the [OpenAI model availability table](https://learn.microsoft.com/azure/cognitive-services/openai/concepts/models#model-summary-table-and-region-availability) and may become outdated as availability changes. + +4. After the application has been successfully deployed you will see a backend and frontend URL printed to the console. Click that frontend URL to interact with the application in your browser. + +> NOTE: It may take 5 minutes for the application to be fully deployed. If you see a "Python Developer" welcome screen or an error page, then wait a bit and refresh the page. + +### Deploying with existing Azure resources + +If you already have existing Azure resources, you can re-use those by setting `azd` environment values. + +#### Existing resource group + +1. Run `azd env set AZURE_RESOURCE_GROUP {Name of existing resource group}` +1. Run `azd env set AZURE_LOCATION {Location of existing resource group}` + +#### Existing Azure OpenAI resource + +1. Run `azd env set AZURE_OPENAI_SERVICE {Name of existing OpenAI service}` +1. Run `azd env set AZURE_OPENAI_RESOURCE_GROUP {Name of existing resource group that OpenAI service is provisioned to}` +1. Run `azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT {Name of existing ChatGPT deployment}`. Only needed if your ChatGPT deployment is not the default model name. +1. Run `azd env set AZURE_OPENAI_EMB_DEPLOYMENT {Name of existing GPT embedding deployment}`. Only needed if your embeddings deployment is not the default model name. + +When you run `azd up` after and are prompted to select a value for `openAiResourceGroupLocation`, make sure to select the same location as the existing OpenAI resource group. + +## Troubleshooting + +Q: I visited the service endpoint listed, and I'm seeing a blank or error page. + +A: Your service may have failed to start or misconfigured. To investigate further: + +1. Click on the resource group link shown to visit Azure Portal. +2. Navigate to the specific Azure Container App resource for the service. +3. Select _Monitoring -> Log stream_ under the navigation pane. +4. Observe the log output to identify any errors. +5. If logs are written to disk, examine the local logs or debug the application by using the _Console_ to connect to a shell within the running container. + +For additional information about setting up your `azd` project, visit our official [docs](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/make-azd-compatible?pivots=azd-convert). diff --git a/infra/core/ai/cognitiveservices.bicep b/infra/core/ai/cognitiveservices.bicep new file mode 100644 index 00000000..2295703d --- /dev/null +++ b/infra/core/ai/cognitiveservices.bicep @@ -0,0 +1,44 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name +var keys = account.listKeys() +output key string = keys.key1 diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 00000000..5c30e3d6 --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,240 @@ +targetScope = 'subscription' + +// The main bicep module to provision Azure resources. +// For a more complete walkthrough to understand how this file works with azd, +// see https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/make-azd-compatible?pivots=azd-create + +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources') +@metadata({ + azd: { + type: 'location' + } +}) +param location string + +param resourceGroupName string = '' + +@description('Optional, defaults to S3. The SKU of the App Service Plan. Acceptable values are B3, S3 and P2v3.') +@allowed([ + 'B3' + 'S3' + 'P2v3' +]) +param HostingPlanSku string = 'P2v3' + +@description('The location of the OpenAI resource group.') +@allowed([ 'canadaeast', 'eastus', 'eastus2', 'francecentral', 'switzerlandnorth', 'uksouth', 'japaneast' ]) +@metadata({ + azd: { + type: 'location' + } +}) +param openAiResourceGroupLocation string +param openAiServiceName string // Set in main.parameters.json +param openAiResourceGroupName string + +param openAiSkuName string = 'S0' + +@description('Optional. The API version of the Azure OpenAI.') +param azureOpenAIAPIVersion string = '2023-05-15' + +param chatGptDeploymentName string = '' // Set in main.parameters.json +param chatGptDeploymentCapacity int = 20 +param chatGptModelName string = (openAiHost == 'azure') ? 'gpt-4-32k' : 'gpt-3.5-turbo' +param chatGptModelVersion string = '0613' +param embeddingDeploymentName string = '' // Set in main.parameters.json +param embeddingDeploymentCapacity int = 30 +param embeddingModelName string = 'text-embedding-ada-002' + +@allowed([ 'azure', 'openai' ]) +param openAiHost string // Set in main.parameters.json + +// tags that should be applied to all resources. +var tags = { + // Tag all resources with the environment name. + 'azd-env-name': environmentName +} + +@description('Required. Active Directory App ID.') +param appId string + +@description('Required. Active Directory App Secret Value.') +@secure() +param appSecret string + +// infrastructure resources +@description('Required. The administrator username of the SQL logical server.') +param SQLAdministratorLogin string + +@description('Required. The administrator password of the SQL logical server.') +@secure() +param SQLAdministratorLoginPassword string + +// Generate a unique token to be used in naming resources. +// Remove linter suppression after using. +#disable-next-line no-unused-vars +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) + +// Organize resources in a resource group +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: !empty(resourceGroupName) ? resourceGroupName : 'rg-${environmentName}' + location: location + tags: tags +} + +resource openAiResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing = if (!empty(openAiResourceGroupName)) { + name: !empty(openAiResourceGroupName) ? openAiResourceGroupName : rg.name +} + +// create opan ai account if openAiHost is azure +module openAi 'core/ai/cognitiveservices.bicep' = if (openAiHost == 'azure') { + name: 'openai' + scope: openAiResourceGroup + params: { + name: !empty(openAiServiceName) ? openAiServiceName : 'openai-${resourceToken}' + location: openAiResourceGroupLocation + tags: tags + sku: { + name: openAiSkuName + } + deployments: [ + { + name: !empty(chatGptDeploymentName) ? chatGptDeploymentName : chatGptModelName + model: { + format: 'OpenAI' + name: chatGptModelName + version: chatGptModelVersion + } + sku: { + name: 'Standard' + capacity: chatGptDeploymentCapacity + } + } + { + name: !empty(embeddingDeploymentName) ? embeddingDeploymentName : embeddingModelName + model: { + format: 'OpenAI' + name: embeddingModelName + version: '2' + } + capacity: embeddingDeploymentCapacity + } + ] + } +} + +// Add resources to be provisioned below. +// A full example that leverages azd bicep modules can be seen in the todo-python-mongo template: +// https://github.com/Azure-Samples/todo-python-mongo/tree/main/infra +module infra '../azuredeploy.bicep' = { + name: 'infra' + scope: rg + params: { + location: location + SQLAdministratorLogin: SQLAdministratorLogin + SQLAdministratorLoginPassword: SQLAdministratorLoginPassword + } + dependsOn: [ + openAi + ] +} + +module backend '../apps/backend/azuredeploy-backend.bicep' = { + name: 'backend' + scope: rg + params: { + location: location + appId: appId + appPassword: appSecret + azureOpenAIAPIKey: openAi.outputs.key + azureOpenAIName: openAi.outputs.name + azureSearchName: infra.outputs.azureSearchName + bingSearchName: infra.outputs.bingSearchAPIName + blobSASToken: '' // TODO: add blobSASToken + cosmosDBAccountName: infra.outputs.cosmosDBAccountName + cosmosDBContainerName: infra.outputs.cosmosDBContainerName + SQLServerName: infra.outputs.SQLServerName + SQLServerDatabase: infra.outputs.SQLDatabaseName + SQLServerUsername: SQLAdministratorLogin + SQLServerPassword: SQLAdministratorLoginPassword + + } + dependsOn: [ + infra + openAi + ] +} + +module frontend '../apps/frontend/azuredeploy-frontend.bicep' = { + name: 'frontend' + scope: rg + params: { + appServicePlanSKU: HostingPlanSku + location: location + azureOpenAIName: openAi.outputs.name + azureOpenAIAPIKey: openAi.outputs.key + azureOpenAIModelName: chatGptModelName + azureOpenAIAPIVersion: azureOpenAIAPIVersion + azureSearchName: infra.outputs.azureSearchName + blobSASToken: '' // TODO: to be added in post deployment + botDirectLineChannelKey: '' // TODO: to be added in post deployment + botServiceName: backend.outputs.botServiceName + } + dependsOn: [ + openAi + infra + backend + ] +} + +// Outputs are automatically saved in the local azd environment .env file. +// To see these outputs, run `azd env get-values`, or `azd env get-values --output json` for json output. +#disable-next-line outputs-should-not-contain-secrets +output AZURE_APP_SECRET string = appSecret +output AZURE_APP_ID string = appId +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output AZURE_SUBSCRIPTION_ID string = subscription().subscriptionId +output AZURE_RESOURCE_GROUP string = rg.name +output AZURE_OPENAI_SERVICE string = openAi.outputs.name +output AZURE_OPENAI_RESOURCE_GROUP string = openAiResourceGroup.name +output AZURE_OPENAI_HOST string = openAiHost +output AZURE_OPENAI_MODEL_NAME string = !empty(chatGptDeploymentName) ? chatGptDeploymentName : chatGptModelName +output AZURE_OPENAI_EMB_DEPLOYMENT string = !empty(embeddingDeploymentName) ? embeddingDeploymentName : embeddingModelName +output AZURE_OPENAI_API_VERSION string = azureOpenAIAPIVersion + +output AZURE_BOT_SERVICE string = backend.outputs.botServiceName +output AZURE_FRONTEND_WEBAPP_NAME string = frontend.outputs.webAppName +output AZURE_BACKEND_WEBAPP_NAME string = backend.outputs.webAppName +output AZURE_BLOB_STORAGE_ACCOUNT_NAME string = infra.outputs.blobStorageAccountName +output AZURE_BACKEND_WEBAPP_URL string = backend.outputs.webAppUrl + + +output BLOB_CONNECTION_STRING string = infra.outputs.blobConnectionString +output BLOB_SAS_TOKEN string = '' // TODO: add blobSASToken + +// Key Variables to put in your Crenetials file +output AZURE_SEARCH_ENDPOINT string = infra.outputs.azureSearchEndpoint +output AZURE_SEARCH_KEY string = infra.outputs.azureSearchKey +output COG_SERVICES_NAME string = infra.outputs.cognitiveServiceName +output COG_SERVICES_KEY string = infra.outputs.cognitiveServiceKey +output FORM_RECOGNIZER_ENDPOINT string = infra.outputs.formrecognizerEndpoint +output FORM_RECOGNIZER_KEY string = infra.outputs.formRecognizerKey +output AZURE_OPENAI_ENDPOINT string = 'https://${openAi.outputs.name}.openai.azure.com/' +output AZURE_OPENAI_API_KEY string = openAi.outputs.key +output BING_SUBSCRIPTION_KEY string = infra.outputs.bingServiceSearchKey +output SQL_SERVER_NAME string = infra.outputs.SQLServerName +output SQL_SERVER_DATABASE string = infra.outputs.SQLDatabaseName +output SQL_SERVER_USERNAME string = SQLAdministratorLogin +#disable-next-line outputs-should-not-contain-secrets +output SQL_SERVER_PASSWORD string = SQLAdministratorLoginPassword +output AZURE_COSMOSDB_ENDPOINT string = infra.outputs.cosmosDBAccountEndpoint +output AZURE_COSMOSDB_NAME string = infra.outputs.cosmosDBAccountName +output AZURE_COSMOSDB_CONTAINER_NAME string = infra.outputs.cosmosDBContainerName +output AZURE_COMOSDB_CONNECTION_STRING string = infra.outputs.cosmosDBConnectionString diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 00000000..3d2c52d6 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "resourceGroupName": { + "value": "${AZURE_RESOURCE_GROUP}" + }, + "openAiResourceGroupLocation": { + "value": "${AZURE_OPENAI_LOCATION}" + }, + "openAiServiceName": { + "value": "${AZURE_OPENAI_SERVICE}" + }, + "openAiResourceGroupName": { + "value": "${AZURE_OPENAI_RESOURCE_GROUP}" + }, + "openAiHost": { + "value": "${OPENAI_HOST=azure}" + }, + "appId": { + "value": "${AZURE_APP_ID}" + }, + "appSecret": { + "value": "${AZURE_APP_SECRET}" + }, + "blobSASToken": { + "value": "${AZURE_BLOB_SAS_TOKEN}" + }, + "chatGptDeploymentName": { + "value": "${AZURE_OPENAI_CHATGPT_DEPLOYMENT}" + }, + "embeddingDeploymentName": { + "value": "${AZURE_OPENAI_EMB_DEPLOYMENT}" + } + } +} diff --git a/infra/scripts/CreateAppRegistration.ps1 b/infra/scripts/CreateAppRegistration.ps1 new file mode 100644 index 00000000..f2a7ed1a --- /dev/null +++ b/infra/scripts/CreateAppRegistration.ps1 @@ -0,0 +1,29 @@ +$ErrorActionPreference = "Stop" +& $PSScriptRoot\loadenv.ps1 + +$appName = $env:AZURE_ENV_NAME + '-app' +$appURI = "http://$env:AZURE_ENV_NAME.fdpo.onmicrosoft.com" +$appHomePageUrl = $appURI + +$myApp = az ad app list --filter "displayName eq '$appName'" --query "[0]" -o json | ConvertFrom-Json +if ($null -eq $myApp) { + $myApp = az ad app create --display-name $appName --query "{displayName: displayName, appId: appId}" -o json | ConvertFrom-Json +} +else { + Write-Host "Application already exists" +} + +$credential = az ad app credential reset --id $myApp.appId --display-name 'MSAPP_SECRET' --append --query "password" -o tsv + + +azd env set AZURE_APP_ID $myApp.appId +azd env set AZURE_APP_SECRET $credential +Write-Host "Application ID: $($myApp.appId) and Secret Saved in azd environment variables" -ForegroundColor Green + + + + + + + + diff --git a/infra/scripts/CreatePrerequisites.ps1 b/infra/scripts/CreatePrerequisites.ps1 new file mode 100644 index 00000000..49fd8ab1 --- /dev/null +++ b/infra/scripts/CreatePrerequisites.ps1 @@ -0,0 +1,8 @@ +$ErrorActionPreference = "Stop" + +& $PSScriptRoot\loadenv.ps1 + + +New-Item -ItemType "directory" -Path ".\infra\target" -ErrorAction SilentlyContinue +New-Item -ItemType "directory" -Path ".\infra\target\frontend" -ErrorAction SilentlyContinue +New-Item -ItemType "directory" -Path ".\infra\target\backend" -ErrorAction SilentlyContinue diff --git a/infra/scripts/UpdateSecretsInApps.ps1 b/infra/scripts/UpdateSecretsInApps.ps1 new file mode 100644 index 00000000..2fba49c8 --- /dev/null +++ b/infra/scripts/UpdateSecretsInApps.ps1 @@ -0,0 +1,26 @@ + +$ErrorActionPreference = "Stop" + +& $PSScriptRoot\loadenv.ps1 + +$jsonObject = az bot directline show --name $env:AZURE_BOT_SERVICE --resource-group $env:AZURE_RESOURCE_GROUP --with-secrets true | ConvertFrom-Json +$defaultSite = $jsonObject.properties.properties.sites | Where-Object { $_.siteName -eq "Default Site" } +$defaultSiteKey = $defaultSite.key.ToString() + +# Generate SAS for BlobStorage to access the container +$expiry=(Get-Date).AddDays(7).ToString('yyyy-MM-ddTHH:mm:ssZ') +$SAS_TOKEN=az storage container generate-sas --account-name $env:AZURE_BLOB_STORAGE_ACCOUNT_NAME --name 'cord19' --permissions dlrw --expiry $expiry --auth-mode login --as-user +azd env set BLOB_SAS_TOKEN $SAS_TOKEN + +# Write value to Azure App Service configuration +write-host "Update Frontend APP configuration" -ForegroundColor Gray +az webapp config appsettings set --name $env:AZURE_FRONTEND_WEBAPP_NAME --resource-group $env:AZURE_RESOURCE_GROUP --settings BOT_DIRECTLINE_SECRET_KEY=$defaultSiteKey BLOB_SAS_TOKEN=$SAS_TOKEN +# Write value to Azure App Service configuration +write-host "Update Backend APP configuration" -ForegroundColor Gray +az webapp config appsettings set --name $env:AZURE_BACKEND_WEBAPP_NAME --resource-group $env:AZURE_RESOURCE_GROUP --settings BLOB_SAS_TOKEN=$SAS_TOKEN + +Write-host "Generate Service Principal for Github actin deployment" -ForegroundColor Gray +az ad sp create-for-rbac --name $env:AZURE_ENV_NAME --role contributor --scopes "/subscriptions/$env:AZURE_SUBSCRIPTION_ID/resourceGroups/$env:AZURE_RESOURCE_GROUP" + + + diff --git a/infra/scripts/loadenv.ps1 b/infra/scripts/loadenv.ps1 new file mode 100644 index 00000000..81082f22 --- /dev/null +++ b/infra/scripts/loadenv.ps1 @@ -0,0 +1,26 @@ +Write-Host "Loading azd .env file from current environment" +foreach ($line in (& azd env get-values)) { + if ($line -match "([^=]+)=(.*)") { + $key = $matches[1] + $value = $matches[2] -replace '^"|"$' + [Environment]::SetEnvironmentVariable($key, $value) + } +} + +$pythonCmd = Get-Command python -ErrorAction SilentlyContinue +if (-not $pythonCmd) { + # fallback to python3 if python not found + $pythonCmd = Get-Command python3 -ErrorAction SilentlyContinue +} + +# Write-Host 'Creating python virtual environment "scripts/.venv"' +# Start-Process -FilePath ($pythonCmd).Source -ArgumentList "-m venv ./scripts/.venv" -Wait -NoNewWindow + +# $venvPythonPath = "./scripts/.venv/scripts/python.exe" +# if (Test-Path -Path "/usr") { +# # fallback to Linux venv path +# $venvPythonPath = "./scripts/.venv/bin/python" +# } + +# Write-Host 'Installing dependencies from "requirements.txt" into virtual environment' +# Start-Process -FilePath $venvPythonPath -ArgumentList "-m pip install -r scripts/requirements.txt" -Wait -NoNewWindow