diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml index 0976ea17..a65ed6cb 100644 --- a/.github/workflows/azure-dev.yml +++ b/.github/workflows/azure-dev.yml @@ -15,69 +15,19 @@ permissions: jobs: template_validation_job: runs-on: ubuntu-latest - name: Template validation + name: template validation steps: - # Step 1: Checkout the code from your repository - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - # Step 2: Set up Python - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.9" - - # Step 3: Create and populate the virtual environment - - name: Create virtual environment and install dependencies - run: | - python -m venv .venv - source .venv/bin/activate - python -m pip install --upgrade pip - pip install azure-mgmt-resource azure-identity azure-core azure-mgmt-subscription azure-cli-core - # Install any other dependencies that might be needed - pip freeze > requirements-installed.txt - echo "Virtual environment created with these packages:" - cat requirements-installed.txt - - # Step 4: Create azd directory if it doesn't exist - - name: Create azd directory - run: | - mkdir -p ./.azd || true - touch ./.azd/.env || true - - # Step 5: Validate the Azure template - - name: Validate Azure Template - uses: microsoft/template-validation-action@v0.3.5 + - uses: microsoft/template-validation-action@Latest id: validation env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }} - AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} + AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} + AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Step 6: Debug output in case of failure - - name: Debug on failure - if: failure() - run: | - echo "Validation failed. Checking environment:" - ls -la - if [ -d ".venv" ]; then - echo ".venv directory exists" - ls -la .venv/bin/ - else - echo ".venv directory does not exist" - fi - if [ -d "tva_*" ]; then - echo "TVA directory exists:" - find . -name "tva_*" -type d - ls -la $(find . -name "tva_*" -type d) - else - echo "No TVA directory found" - fi - - # Step 7: Print the result of the validation - - name: Print result - if: success() + - name: print result run: cat ${{ steps.validation.outputs.resultFile }} diff --git a/.github/workflows/docker-build-and-push.yml b/.github/workflows/docker-build-and-push.yml index 383ef2fc..a6d2c59a 100644 --- a/.github/workflows/docker-build-and-push.yml +++ b/.github/workflows/docker-build-and-push.yml @@ -18,7 +18,7 @@ on: - dev - demo - hotfix - workflow_dispatch: + workflow_dispatch: jobs: build-and-push: @@ -32,14 +32,19 @@ jobs: uses: docker/setup-buildx-action@v1 - name: Log in to Azure Container Registry - if: ${{ (github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'hotfix') }} + if: ${{ github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'hotfix' }} uses: azure/docker-login@v2 with: login-server: ${{ secrets.ACR_LOGIN_SERVER }} username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} - - name: Set Docker image tag + - name: Get current date + id: date + run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + + - name: Determine Tag Name Based on Branch + id: determine_tag run: | if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then echo "TAG=latest" >> $GITHUB_ENV @@ -52,24 +57,30 @@ jobs: else echo "TAG=pullrequest-ignore" >> $GITHUB_ENV fi - - - name: Build and push Docker images optionally + + - name: Set Historical Tag run: | - cd src/backend - docker build -t ${{ secrets.ACR_LOGIN_SERVER }}/macaebackend:${{ env.TAG }} -f Dockerfile . && \ - if [[ "${{ env.TAG }}" == "latest" || "${{ env.TAG }}" == "dev" || "${{ env.TAG }}" == "demo" || "${{ env.TAG }}" == "hotfix" ]]; then - docker push ${{ secrets.ACR_LOGIN_SERVER }}/macaebackend:${{ env.TAG }} && \ - echo "Backend image built and pushed successfully." - else - echo "Skipping Docker push for backend with tag: ${{ env.TAG }}" - fi - cd ../frontend - docker build -t ${{ secrets.ACR_LOGIN_SERVER }}/macaefrontend:${{ env.TAG }} -f Dockerfile . && \ - if [[ "${{ env.TAG }}" == "latest" || "${{ env.TAG }}" == "dev" || "${{ env.TAG }}" == "demo" || "${{ env.TAG }}" == "hotfix" ]]; then - docker push ${{ secrets.ACR_LOGIN_SERVER }}/macaefrontend:${{ env.TAG }} && \ - echo "Frontend image built and pushed successfully." - else - echo "Skipping Docker push for frontend with tag: ${{ env.TAG }}" - fi + DATE_TAG=$(date +'%Y-%m-%d') + RUN_ID=${{ github.run_number }} + # Create historical tag using TAG, DATE_TAG, and RUN_ID + echo "HISTORICAL_TAG=${{ env.TAG }}_${DATE_TAG}_${RUN_ID}" >> $GITHUB_ENV - + - name: Build and optionally push Backend Docker image + uses: docker/build-push-action@v6 + with: + context: ./src/backend + file: ./src/backend/Dockerfile + push: ${{ env.TAG != 'pullrequest-ignore' }} + tags: | + ${{ secrets.ACR_LOGIN_SERVER }}/macaebackend:${{ env.TAG }} + ${{ secrets.ACR_LOGIN_SERVER }}/macaebackend:${{ env.HISTORICAL_TAG }} + + - name: Build and optionally push Frontend Docker image + uses: docker/build-push-action@v6 + with: + context: ./src/frontend + file: ./src/frontend/Dockerfile + push: ${{ env.TAG != 'pullrequest-ignore' }} + tags: | + ${{ secrets.ACR_LOGIN_SERVER }}/macaefrontend:${{ env.TAG }} + ${{ secrets.ACR_LOGIN_SERVER }}/macaefrontend:${{ env.HISTORICAL_TAG }} \ No newline at end of file diff --git a/infra/deploy_ai_foundry.bicep b/infra/deploy_ai_foundry.bicep index ee9b3b37..328a37ea 100644 --- a/infra/deploy_ai_foundry.bicep +++ b/infra/deploy_ai_foundry.bicep @@ -6,7 +6,7 @@ param gptModelName string param gptModelVersion string param managedIdentityObjectId string param aiServicesEndpoint string -param aiServicesKey string +param aiServices object param aiServicesId string var storageName = '${solutionName}hubstorage' @@ -136,7 +136,7 @@ resource aiHub 'Microsoft.MachineLearningServices/workspaces@2023-08-01-preview' authType: 'ApiKey' isSharedToAll: true credentials: { - key: aiServicesKey + key: aiServices.Key.key1 } metadata: { ApiType: 'Azure' @@ -187,7 +187,7 @@ resource azureOpenAIApiKeyEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-pr parent: keyVault name: 'AZURE-OPENAI-KEY' properties: { - value: aiServicesKey //aiServices_m.listKeys().key1 + value: aiServices.Key.key1 //aiServices_m.listKeys().key1 } } @@ -251,7 +251,7 @@ resource cogServiceKeyEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-previe parent: keyVault name: 'COG-SERVICES-KEY' properties: { - value: aiServicesKey + value: aiServices.Key.key1 } } diff --git a/infra/main.bicep b/infra/main.bicep index bea37442..cdaf6ddd 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1,6 +1,6 @@ targetScope = 'resourceGroup' -@description('Location for all resources.') -param location string +// @description('Location for all resources.') +// param location string @allowed([ 'australiaeast' @@ -28,15 +28,12 @@ param location string 'westus3' ]) @description('Location for all Ai services resources. This location can be different from the resource group location.') -param azureOpenAILocation string = 'eastus2' // The location used for all deployed resources. This location must be in the same region as the resource group. +param azureOpenAILocation string //= 'eastus2' // The location used for all deployed resources. This location must be in the same region as the resource group. @minLength(3) @maxLength(20) -@description('A unique prefix for all resources in this deployment. This should be 3-20 characters long:') -param environmentName string - -var uniqueId = toLower(uniqueString(subscription().id, environmentName, resourceGroup().location)) -var solutionPrefix = 'ma${padLeft(take(uniqueId, 12), 12, '0')}' +@description('Prefix for all resources created by this template. This prefix will be used to create unique names for all resources. The prefix must be unique within the resource group.') +param prefix string //= 'macae' @description('Tags to apply to all deployed resources') param tags object = {} @@ -61,8 +58,9 @@ param resourceSize { } param capacity int = 140 +var location = resourceGroup().location var modelVersion = '2024-08-06' -var aiServicesName = '${solutionPrefix}-aiservices' +var aiServicesName = '${prefix}-aiservices' var deploymentType = 'GlobalStandard' var gptModelVersion = 'gpt-4o' var appVersion = 'fnd01' @@ -73,7 +71,7 @@ var dockerRegistryUrl = 'https://${resgistryName}.azurecr.io' var backendDockerImageURL = '${resgistryName}.azurecr.io/macaebackend:${appVersion}' var frontendDockerImageURL = '${resgistryName}.azurecr.io/macaefrontend:${appVersion}' -var uniqueNameFormat = '${solutionPrefix}-{0}-${uniqueString(resourceGroup().id, solutionPrefix)}' +var uniqueNameFormat = '${prefix}-{0}-${uniqueString(resourceGroup().id, prefix)}' var aoaiApiVersion = '2025-01-01-preview' resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { @@ -123,7 +121,7 @@ resource aiServices 'Microsoft.CognitiveServices/accounts@2024-04-01-preview' = apiProperties: { //statisticsEnabled: false } - //disableLocalAuth: true + disableLocalAuth: true } } @@ -149,7 +147,7 @@ resource aiServicesDeployments 'Microsoft.CognitiveServices/accounts/deployments module kvault 'deploy_keyvault.bicep' = { name: 'deploy_keyvault' params: { - solutionName: solutionPrefix + solutionName: prefix solutionLocation: location managedIdentityObjectId: managedIdentityModule.outputs.managedIdentityOutput.objectId } @@ -163,14 +161,14 @@ module kvault 'deploy_keyvault.bicep' = { module aifoundry 'deploy_ai_foundry.bicep' = { name: 'deploy_ai_foundry' params: { - solutionName: solutionPrefix + solutionName: prefix solutionLocation: azureOpenAILocation keyVaultName: kvault.outputs.keyvaultName gptModelName: gptModelVersion gptModelVersion: gptModelVersion managedIdentityObjectId: managedIdentityModule.outputs.managedIdentityOutput.objectId aiServicesEndpoint: aiServices.properties.endpoint - aiServicesKey: aiServices.listKeys().key1 + aiServices: aiServices aiServicesId: aiServices.id } scope: resourceGroup(resourceGroup().name) @@ -205,7 +203,7 @@ resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = { } ] capabilities: [{ name: 'EnableServerless' }] - //disableLocalAuth: true + disableLocalAuth: true } resource contributorRoleDefinition 'sqlRoleDefinitions' existing = { @@ -279,7 +277,7 @@ resource acaCosomsRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleA @description('') resource containerApp 'Microsoft.App/containerApps@2024-03-01' = { - name: '${solutionPrefix}-backend' + name: '${prefix}-backend' location: location tags: tags identity: { @@ -448,7 +446,7 @@ resource frontendAppService 'Microsoft.Web/sites@2021-02-01' = { } resource aiHubProject 'Microsoft.MachineLearningServices/workspaces@2024-01-01-preview' existing = { - name: '${solutionPrefix}-aiproject' // aiProjectName must be calculated - available at main start. + name: '${prefix}-aiproject' // aiProjectName must be calculated - available at main start. } resource aiDeveloper 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { @@ -469,7 +467,7 @@ var cosmosAssignCli = 'az cosmosdb sql role assignment create --resource-group " module managedIdentityModule 'deploy_managed_identity.bicep' = { name: 'deploy_managed_identity' params: { - solutionName: solutionPrefix + solutionName: prefix //solutionLocation: location managedIdentityId: pullIdentity.id managedIdentityPropPrin: pullIdentity.properties.principalId diff --git a/infra/main.json b/infra/main.json index 6c40552d..ccecd875 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,20 +5,13 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "7719893060553487435" + "version": "0.35.1.17967", + "templateHash": "18228555099764132241" } }, "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Location for all resources." - } - }, "azureOpenAILocation": { "type": "string", - "defaultValue": "eastus2", "allowedValues": [ "australiaeast", "brazilsouth", @@ -50,7 +43,6 @@ }, "prefix": { "type": "string", - "defaultValue": "macae", "minLength": 3, "maxLength": 20, "metadata": { @@ -107,6 +99,7 @@ } }, "variables": { + "location": "[resourceGroup().location]", "modelVersion": "2024-08-06", "aiServicesName": "[format('{0}-aiservices', parameters('prefix'))]", "deploymentType": "GlobalStandard", @@ -190,7 +183,7 @@ "type": "Microsoft.OperationalInsights/workspaces", "apiVersion": "2023-09-01", "name": "[format(variables('uniqueNameFormat'), 'logs')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "tags": "[parameters('tags')]", "properties": { "retentionInDays": 30, @@ -203,7 +196,7 @@ "type": "Microsoft.Insights/components", "apiVersion": "2020-02-02-preview", "name": "[format(variables('uniqueNameFormat'), 'appins')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "kind": "web", "properties": { "Application_Type": "web", @@ -217,7 +210,7 @@ "type": "Microsoft.CognitiveServices/accounts", "apiVersion": "2024-04-01-preview", "name": "[variables('aiServicesName')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "sku": { "name": "S0" }, @@ -277,7 +270,7 @@ "type": "Microsoft.DocumentDB/databaseAccounts", "apiVersion": "2024-05-15", "name": "[format(variables('uniqueNameFormat'), 'cosmos')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "tags": "[parameters('tags')]", "kind": "GlobalDocumentDB", "properties": { @@ -286,7 +279,7 @@ "locations": [ { "failoverPriority": 0, - "locationName": "[parameters('location')]" + "locationName": "[variables('location')]" } ], "capabilities": [ @@ -301,13 +294,13 @@ "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2023-07-31-preview", "name": "[format(variables('uniqueNameFormat'), 'containerapp-pull')]", - "location": "[parameters('location')]" + "location": "[variables('location')]" }, "containerAppEnv": { "type": "Microsoft.App/managedEnvironments", "apiVersion": "2024-03-01", "name": "[format(variables('uniqueNameFormat'), 'containerapp')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "tags": "[parameters('tags')]", "properties": { "daprAIConnectionString": "[reference('appInsights').ConnectionString]", @@ -315,7 +308,7 @@ "destination": "log-analytics", "logAnalyticsConfiguration": { "customerId": "[reference('logAnalytics').customerId]", - "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', format(variables('uniqueNameFormat'), 'logs')), '2023-09-01').primarySharedKey]" + "sharedKey": "[listKeys('logAnalytics', '2023-09-01').primarySharedKey]" } } }, @@ -342,7 +335,7 @@ "type": "Microsoft.App/containerApps", "apiVersion": "2024-03-01", "name": "[format('{0}-backend', parameters('prefix'))]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "tags": "[parameters('tags')]", "identity": { "type": "SystemAssigned, UserAssigned", @@ -468,7 +461,7 @@ "type": "Microsoft.Web/serverfarms", "apiVersion": "2021-02-01", "name": "[format(variables('uniqueNameFormat'), 'frontend-plan')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "tags": "[parameters('tags')]", "sku": { "name": "P1v2", @@ -484,7 +477,7 @@ "type": "Microsoft.Web/sites", "apiVersion": "2021-02-01", "name": "[format(variables('uniqueNameFormat'), 'frontend')]", - "location": "[parameters('location')]", + "location": "[variables('location')]", "tags": "[parameters('tags')]", "kind": "app,linux,container", "properties": { @@ -568,7 +561,7 @@ "value": "[parameters('prefix')]" }, "solutionLocation": { - "value": "[parameters('location')]" + "value": "[variables('location')]" }, "managedIdentityObjectId": { "value": "[reference('managedIdentityModule').outputs.managedIdentityOutput.value.objectId]" @@ -580,8 +573,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "10664495342911727649" + "version": "0.35.1.17967", + "templateHash": "5761607453167859573" } }, "parameters": { @@ -706,7 +699,7 @@ "value": "[reference('aiServices').endpoint]" }, "aiServicesKey": { - "value": "[listKeys(resourceId('Microsoft.CognitiveServices/accounts', variables('aiServicesName')), '2024-04-01-preview').key1]" + "value": "[listKeys('aiServices', '2024-04-01-preview').key1]" }, "aiServicesId": { "value": "[resourceId('Microsoft.CognitiveServices/accounts', variables('aiServicesName'))]" @@ -718,8 +711,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "8087543237770345715" + "version": "0.35.1.17967", + "templateHash": "9490638595753234802" } }, "parameters": { @@ -1112,8 +1105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.34.44.8038", - "templateHash": "11364190519186458619" + "version": "0.35.1.17967", + "templateHash": "12327197428621494853" } }, "parameters": { @@ -1199,7 +1192,7 @@ "value": "2.69.0" }, "location": { - "value": "[parameters('location')]" + "value": "[variables('location')]" }, "managedIdentities": { "value": {