From 1242dcbd4133a524871001d799bd22c13b662cf9 Mon Sep 17 00:00:00 2001 From: Vemarthula-Microsoft Date: Wed, 17 Sep 2025 20:34:37 +0530 Subject: [PATCH 01/40] Enhance deploy workflow with new branch and region logic Updated the deploy workflow to include a new branch for triggering the pipeline and modified the region selection logic for Azure resources based on quota availability. --- .github/workflows/deploy.yml | 64 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index be98168f..e0598d17 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,17 +1,18 @@ name: Deploy-Test-Cleanup Pipeline on: - workflow_run: - workflows: ["Build Docker and Optional Push"] - types: - - completed - branches: - - main - - dev - - demo - schedule: - - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT - workflow_dispatch: + workflow_run: + workflows: ["Build Docker and Optional Push"] + types: + - completed + branches: + - main + - dev + - demo + - vee-pipeline-fixes + schedule: + - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT + workflow_dispatch: env: GPT_MIN_CAPACITY: 150 @@ -36,22 +37,28 @@ jobs: run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - - name: Run Quota Check - id: quota-check + - name: Select Region with Sufficient OpenAI Quota + id: select_region run: | - export AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }} - export AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }} - export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }} - export AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}" - export GPT_MIN_CAPACITY="${{ env.GPT_MIN_CAPACITY }}" - export AZURE_REGIONS="${{ vars.AZURE_REGIONS }}" - chmod +x scripts/checkquota.sh - if ! scripts/checkquota.sh; then - # If quota check fails due to insufficient quota, set the flag - if grep -q "No region with sufficient quota found" scripts/checkquota.sh; then - echo "QUOTA_FAILED=true" >> $GITHUB_ENV + regions=("eastus2" "francecentral" "westus3" "southcentralus") + required_quota=150 + selected_region="" + for region in "${regions[@]}"; do + available_quota=$(az openai quota show --location "$region" --query "quotas[?name=='OpenAI.GlobalStandard.gpt-4o'].limit" -o tsv) + used_quota=$(az openai quota show --location "$region" --query "quotas[?name=='OpenAI.GlobalStandard.gpt-4o'].used" -o tsv) + if [[ $((available_quota - used_quota)) -ge $required_quota ]]; then + selected_region="$region" + break fi - exit 1 # Fail the pipeline if any other failure occurs + done + if [[ -z "$selected_region" ]]; then + echo "No region with sufficient quota found." + echo "QUOTA_FAILED=true" >> $GITHUB_ENV + exit 1 + else + echo "Selected region: $selected_region" + echo "SELECTED_REGION=$selected_region" >> $GITHUB_ENV + echo "selected_region=$selected_region" >> $GITHUB_OUTPUT fi - name: Send Notification on Quota Failure @@ -94,7 +101,7 @@ jobs: rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) if [ "$rg_exists" = "false" ]; then echo "Resource group does not exist. Creating..." - az group create --name ${{ env.RESOURCE_GROUP_NAME }} --location northcentralus || { echo "Error creating resource group"; exit 1; } + az group create --name ${{ env.RESOURCE_GROUP_NAME }} --location ${{ env.SELECTED_REGION }} || { echo "Error creating resource group"; exit 1; } else echo "Resource group already exists." fi @@ -132,11 +139,12 @@ jobs: --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="eastus" \ + aiDeploymentsLocation="${{ env.SELECTED_REGION }}" \ useWafAlignedArchitecture=false \ capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" + - name: Assign Contributor role to Service Principal if: always() run: | @@ -356,7 +364,7 @@ jobs: # Purge OpenAI Resource echo "Purging the OpenAI Resource..." - if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/northcentralus/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then + if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.SELECTED_REGION }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" else echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" From 97750c9a69a549a0dbd805c1ffe397f4fc46a72b Mon Sep 17 00:00:00 2001 From: Vemarthula-Microsoft Date: Thu, 18 Sep 2025 06:36:21 +0530 Subject: [PATCH 02/40] Update deploy workflow for Azure OpenAI integration --- .github/workflows/deploy.yml | 67 ++++-------------------------------- 1 file changed, 6 insertions(+), 61 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e0598d17..c7abbba8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,9 +6,6 @@ on: types: - completed branches: - - main - - dev - - demo - vee-pipeline-fixes schedule: - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT @@ -37,6 +34,10 @@ jobs: run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} + - name: Install Azure OpenAI Extension + run: | + az extension add --upgrade -n openai + - name: Select Region with Sufficient OpenAI Quota id: select_region run: | @@ -154,7 +155,6 @@ jobs: --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} - - name: Get Deployment Output and extract Values id: get_output run: | @@ -202,22 +202,15 @@ jobs: set -e echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." - - # Run the az monitor log-analytics workspace list command to get the workspace name log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) - if [ -z "$log_analytics_workspace_name" ]; then echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." else echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV echo "Log Analytics workspace name: ${log_analytics_workspace_name}" fi - echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." - - # Run the az resource list command to get the OpenAI resource name openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv) - if [ -z "$openai_resource_name" ]; then echo "No OpenAI resource found in resource group ${{ env.RESOURCE_GROUP_NAME }}." exit 1 @@ -233,17 +226,12 @@ jobs: set -e echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." - - # Get the list of KeyVaults in the specified resource group keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv) - if [ -z "$keyvaults" ]; then echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." echo "KEYVAULTS=[]" >> $GITHUB_ENV # If no KeyVaults found, set an empty array else echo "KeyVaults found: $keyvaults" - - # Format the list into an array with proper formatting (no trailing comma) keyvault_array="[" first=true for kv in $keyvaults; do @@ -255,8 +243,6 @@ jobs: fi done keyvault_array="$keyvault_array]" - - # Output the formatted array and save it to the environment variable echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV fi @@ -283,50 +269,28 @@ jobs: run: | set -e - # Purge Log Analytics Workspace echo "Purging the Log Analytics Workspace..." if ! az monitor log-analytics workspace delete --force --resource-group ${{ env.RESOURCE_GROUP_NAME }} --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --yes --verbose; then echo "Failed to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" else echo "Purged the Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" fi - echo "Log analytics workspace resource purging completed successfully" - - name: Wait for resource deletion to complete if: always() run: | - - # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" - - # Remove the surrounding square brackets, if they exist stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - - # Convert the comma-separated string into an array IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults" - - # Append new resources to the array resources_to_check+=("${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" "${{ env.OPENAI_RESOURCE_NAME }}") - echo "List of resources to check: ${resources_to_check[@]}" - - # Maximum number of retries max_retries=3 - - # Retry intervals in seconds (30, 60, 120) retry_intervals=(30 60 120) - - # Retry mechanism to check resources retries=0 while true; do resource_found=false - - # Get the list of resources in YAML format again on each retry resource_list=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --output yaml) - - # Iterate through the resources to check for resource in "${resources_to_check[@]}"; do echo "Checking resource: $resource" if echo "$resource_list" | grep -q "name: $resource"; then @@ -336,15 +300,12 @@ jobs: echo "Resource '$resource' does not exist in the resource group." fi done - - # If any resource exists, retry if [ "$resource_found" = true ]; then retries=$((retries + 1)) if [ "$retries" -gt "$max_retries" ]; then echo "Maximum retry attempts reached. Exiting." break else - # Wait for the appropriate interval for the current retry echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..." sleep ${retry_intervals[$retries-1]} fi @@ -353,44 +314,30 @@ jobs: break fi done - + - name: Purging the Resources if: always() run: | set -e - echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" - - # Purge OpenAI Resource echo "Purging the OpenAI Resource..." if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.SELECTED_REGION }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" else echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" fi - - # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" - - # Remove the surrounding square brackets, if they exist stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - - # Convert the comma-separated string into an array IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" - echo "Using KeyVaults Array..." for keyvault_name in "${keyvault_array[@]}"; do echo "Processing KeyVault: $keyvault_name" - # Check if the KeyVault is soft-deleted deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}) - - # If the KeyVault is found in the soft-deleted state, purge it if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..." - # Purge the KeyVault if az keyvault purge --name "$keyvault_name" --no-wait; then - echo "Successfully purged KeyVault '$keyvault_name'." + echo "Successfully purged KeyVault '$keyvault_name'." else echo "Failed to purge KeyVault '$keyvault_name'." fi @@ -404,14 +351,12 @@ jobs: if: failure() || needs.deploy.result == 'failure' run: | RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the DocGen Deployment Automation process has encountered an issue and has failed to complete successfully.

Build URL: ${RUN_URL}

Please investigate the matter at your earliest convenience.

Best regards,
Your Automation Team

" } EOF ) - curl -X POST "${{ secrets.LOGIC_APP_URL }}" \ -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" From 292587579324e7211a504d92ac86f658a7a5bfb3 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 07:52:44 +0530 Subject: [PATCH 03/40] pipeline changes --- .github/workflows/build-docker-images.yml | 4 +- .github/workflows/deploy.yml | 125 +++++++++++++++------- .github/workflows/test.yml | 8 +- 3 files changed, 94 insertions(+), 43 deletions(-) diff --git a/.github/workflows/build-docker-images.yml b/.github/workflows/build-docker-images.yml index 7519d620..df6213c6 100644 --- a/.github/workflows/build-docker-images.yml +++ b/.github/workflows/build-docker-images.yml @@ -7,12 +7,14 @@ on: - dev - demo - hotfix + - vee-pipeline-fixes pull_request: branches: - main - dev - demo - hotfix + - vee-pipeline-fixes types: - opened - ready_for_review @@ -39,5 +41,5 @@ jobs: password_secret: ${{ matrix.password_secret }} app_name: ${{ matrix.app_name }} dockerfile: ${{ matrix.dockerfile }} - push: ${{ github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'hotfix' }} + push: ${{ github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'hotfix' || github.ref_name == 'vee-pipeline-fixes' }} secrets: inherit \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c7abbba8..dac5b994 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,15 +1,15 @@ name: Deploy-Test-Cleanup Pipeline on: - workflow_run: - workflows: ["Build Docker and Optional Push"] - types: - - completed - branches: - - vee-pipeline-fixes - schedule: - - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT - workflow_dispatch: + workflow_run: + workflows: ["Build Docker and Optional Push"] + types: + - completed + branches: + - vee-pipeline-fixes + schedule: + - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT + workflow_dispatch: env: GPT_MIN_CAPACITY: 150 @@ -34,32 +34,22 @@ jobs: run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - - name: Install Azure OpenAI Extension + - name: Run Quota Check + id: quota-check run: | - az extension add --upgrade -n openai - - - name: Select Region with Sufficient OpenAI Quota - id: select_region - run: | - regions=("eastus2" "francecentral" "westus3" "southcentralus") - required_quota=150 - selected_region="" - for region in "${regions[@]}"; do - available_quota=$(az openai quota show --location "$region" --query "quotas[?name=='OpenAI.GlobalStandard.gpt-4o'].limit" -o tsv) - used_quota=$(az openai quota show --location "$region" --query "quotas[?name=='OpenAI.GlobalStandard.gpt-4o'].used" -o tsv) - if [[ $((available_quota - used_quota)) -ge $required_quota ]]; then - selected_region="$region" - break + export AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }} + export AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }} + export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }} + export AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}" + export GPT_MIN_CAPACITY="${{ env.GPT_MIN_CAPACITY }}" + export AZURE_REGIONS="${{ vars.AZURE_REGIONS }}" + chmod +x scripts/checkquota.sh + if ! scripts/checkquota.sh; then + # If quota check fails due to insufficient quota, set the flag + if grep -q "No region with sufficient quota found" scripts/checkquota.sh; then + echo "QUOTA_FAILED=true" >> $GITHUB_ENV fi - done - if [[ -z "$selected_region" ]]; then - echo "No region with sufficient quota found." - echo "QUOTA_FAILED=true" >> $GITHUB_ENV - exit 1 - else - echo "Selected region: $selected_region" - echo "SELECTED_REGION=$selected_region" >> $GITHUB_ENV - echo "selected_region=$selected_region" >> $GITHUB_OUTPUT + exit 1 # Fail the pipeline if any other failure occurs fi - name: Send Notification on Quota Failure @@ -102,7 +92,7 @@ jobs: rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) if [ "$rg_exists" = "false" ]; then echo "Resource group does not exist. Creating..." - az group create --name ${{ env.RESOURCE_GROUP_NAME }} --location ${{ env.SELECTED_REGION }} || { echo "Error creating resource group"; exit 1; } + az group create --name ${{ env.RESOURCE_GROUP_NAME }} --location northcentralus || { echo "Error creating resource group"; exit 1; } else echo "Resource group already exists." fi @@ -140,12 +130,11 @@ jobs: --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="${{ env.SELECTED_REGION }}" \ + aiDeploymentsLocation="eastus" \ useWafAlignedArchitecture=false \ capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" - - name: Assign Contributor role to Service Principal if: always() run: | @@ -155,6 +144,7 @@ jobs: --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} + - name: Get Deployment Output and extract Values id: get_output run: | @@ -202,15 +192,22 @@ jobs: set -e echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + + # Run the az monitor log-analytics workspace list command to get the workspace name log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) + if [ -z "$log_analytics_workspace_name" ]; then echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." else echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV echo "Log Analytics workspace name: ${log_analytics_workspace_name}" fi + echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + + # Run the az resource list command to get the OpenAI resource name openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv) + if [ -z "$openai_resource_name" ]; then echo "No OpenAI resource found in resource group ${{ env.RESOURCE_GROUP_NAME }}." exit 1 @@ -226,12 +223,17 @@ jobs: set -e echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." + + # Get the list of KeyVaults in the specified resource group keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv) + if [ -z "$keyvaults" ]; then echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." echo "KEYVAULTS=[]" >> $GITHUB_ENV # If no KeyVaults found, set an empty array else echo "KeyVaults found: $keyvaults" + + # Format the list into an array with proper formatting (no trailing comma) keyvault_array="[" first=true for kv in $keyvaults; do @@ -243,6 +245,8 @@ jobs: fi done keyvault_array="$keyvault_array]" + + # Output the formatted array and save it to the environment variable echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV fi @@ -269,28 +273,50 @@ jobs: run: | set -e + # Purge Log Analytics Workspace echo "Purging the Log Analytics Workspace..." if ! az monitor log-analytics workspace delete --force --resource-group ${{ env.RESOURCE_GROUP_NAME }} --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --yes --verbose; then echo "Failed to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" else echo "Purged the Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" fi + echo "Log analytics workspace resource purging completed successfully" + - name: Wait for resource deletion to complete if: always() run: | + + # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" + + # Remove the surrounding square brackets, if they exist stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + + # Convert the comma-separated string into an array IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults" + + # Append new resources to the array resources_to_check+=("${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" "${{ env.OPENAI_RESOURCE_NAME }}") + echo "List of resources to check: ${resources_to_check[@]}" + + # Maximum number of retries max_retries=3 + + # Retry intervals in seconds (30, 60, 120) retry_intervals=(30 60 120) + + # Retry mechanism to check resources retries=0 while true; do resource_found=false + + # Get the list of resources in YAML format again on each retry resource_list=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --output yaml) + + # Iterate through the resources to check for resource in "${resources_to_check[@]}"; do echo "Checking resource: $resource" if echo "$resource_list" | grep -q "name: $resource"; then @@ -300,12 +326,15 @@ jobs: echo "Resource '$resource' does not exist in the resource group." fi done + + # If any resource exists, retry if [ "$resource_found" = true ]; then retries=$((retries + 1)) if [ "$retries" -gt "$max_retries" ]; then echo "Maximum retry attempts reached. Exiting." break else + # Wait for the appropriate interval for the current retry echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..." sleep ${retry_intervals[$retries-1]} fi @@ -314,30 +343,44 @@ jobs: break fi done - + - name: Purging the Resources if: always() run: | set -e + echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" + + # Purge OpenAI Resource echo "Purging the OpenAI Resource..." - if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.SELECTED_REGION }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then + if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/northcentralus/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" else echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" fi + + # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" + + # Remove the surrounding square brackets, if they exist stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + + # Convert the comma-separated string into an array IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" + echo "Using KeyVaults Array..." for keyvault_name in "${keyvault_array[@]}"; do echo "Processing KeyVault: $keyvault_name" + # Check if the KeyVault is soft-deleted deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}) + + # If the KeyVault is found in the soft-deleted state, purge it if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..." + # Purge the KeyVault if az keyvault purge --name "$keyvault_name" --no-wait; then - echo "Successfully purged KeyVault '$keyvault_name'." + echo "Successfully purged KeyVault '$keyvault_name'." else echo "Failed to purge KeyVault '$keyvault_name'." fi @@ -351,12 +394,14 @@ jobs: if: failure() || needs.deploy.result == 'failure' run: | RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the DocGen Deployment Automation process has encountered an issue and has failed to complete successfully.

Build URL: ${RUN_URL}

Please investigate the matter at your earliest convenience.

Best regards,
Your Automation Team

" } EOF ) + curl -X POST "${{ secrets.LOGIC_APP_URL }}" \ -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" @@ -365,4 +410,4 @@ jobs: if: always() run: | az logout - echo "Logged out from Azure." + echo "Logged out from Azure." \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34a2f24d..4870ae15 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,7 @@ on: - main - dev - demo + - vee-pipeline-fixes pull_request: types: - opened @@ -16,6 +17,7 @@ on: - main - dev - demo + - vee-pipeline-fixes jobs: # frontend_tests: @@ -87,13 +89,15 @@ jobs: if [ -z "$(find src/tests/backend -type f -name '*_test.py')" ]; then echo "No backend test files found, skipping backend tests." echo "skip_backend_tests=true" >> $GITHUB_ENV + echo "skip_backend_tests=true" >> $GITHUB_OUTPUT else echo "Backend test files found, running tests." echo "skip_backend_tests=false" >> $GITHUB_ENV + echo "skip_backend_tests=false" >> $GITHUB_OUTPUT fi - name: Run Backend Tests with Coverage - if: env.skip_backend_tests == 'false' + if: steps.check_backend_tests.outputs.skip_backend_tests == 'false' run: | cd src pytest --cov=. --cov-report=term-missing --cov-report=xml @@ -101,6 +105,6 @@ jobs: - name: Skip Backend Tests - if: env.skip_backend_tests == 'true' + if: steps.check_backend_tests.outputs.skip_backend_tests == 'true' run: | echo "Skipping backend tests because no test files were found." From e8582e2c256dd21643263b9568e820749f891d53 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 08:37:42 +0530 Subject: [PATCH 04/40] latest changes adding branach name --- .github/workflows/build-docker.yml | 2 ++ .github/workflows/deploy.yml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 55da1c59..943107a7 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -59,6 +59,8 @@ jobs: echo "tagname=demo" >> $GITHUB_OUTPUT elif [[ "${{ github.ref_name }}" == "hotfix" ]]; then echo "tagname=hotfix" >> $GITHUB_OUTPUT + elif [[ "${{ github.ref_name }}" == "vee-pipeline-fixes" ]]; then + echo "tagname=vee-pipeline-fixes" >> $GITHUB_OUTPUT else echo "tagname=default" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dac5b994..13409fa7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -120,6 +120,8 @@ jobs: IMAGE_TAG="dev" elif [[ "${{ env.BRANCH_NAME }}" == "demo" ]]; then IMAGE_TAG="demo" + elif [[ "${{ env.BRANCH_NAME }}" == "vee-pipeline-fixes" ]]; then + IMAGE_TAG="vee-pipeline-fixes" else IMAGE_TAG="latest" fi @@ -194,7 +196,7 @@ jobs: echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." # Run the az monitor log-analytics workspace list command to get the workspace name - log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) + log_analytics_workspace_name=$(az monitor.log-analytics.workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) if [ -z "$log_analytics_workspace_name" ]; then echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." From fe0119bedb3c9a3de01b1953db7e9c7d76368cdc Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 08:56:56 +0530 Subject: [PATCH 05/40] pipeline changes --- .github/workflows/deploy.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 13409fa7..57eb6362 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -126,13 +126,17 @@ jobs: IMAGE_TAG="latest" fi + # Prefer quota-selected region if available + EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" + echo "Using AI Deployments Region: $EFFECTIVE_AI_REGION" + az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="eastus" \ + aiDeploymentsLocation="$EFFECTIVE_AI_REGION" \ useWafAlignedArchitecture=false \ capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ @@ -356,7 +360,7 @@ jobs: # Purge OpenAI Resource echo "Purging the OpenAI Resource..." - if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/northcentralus/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then + if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.VALID_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" else echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" From 91665a2edc7d9f9eb7369adab6d7f868220eccc9 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 09:18:18 +0530 Subject: [PATCH 06/40] fixing of deploy cleanup failure --- .github/workflows/deploy.yml | 103 +++++++++++++---------------------- 1 file changed, 38 insertions(+), 65 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 57eb6362..715266ab 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,6 +21,7 @@ jobs: outputs: RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }} WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }} + SELECTED_AI_REGION: ${{ steps.deploy.outputs.selected_ai_region || env.VALID_REGION }} steps: - name: Checkout Code uses: actions/checkout@v3 @@ -126,9 +127,9 @@ jobs: IMAGE_TAG="latest" fi - # Prefer quota-selected region if available EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" echo "Using AI Deployments Region: $EFFECTIVE_AI_REGION" + echo "selected_ai_region=$EFFECTIVE_AI_REGION" >> $GITHUB_OUTPUT az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ @@ -181,6 +182,7 @@ jobs: runs-on: ubuntu-latest env: RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }} + AI_REGION: ${{ needs.deploy.outputs.SELECTED_AI_REGION }} steps: - name: Setup Azure CLI run: | @@ -195,31 +197,22 @@ jobs: if: always() id: get_azure_resources run: | - set -e echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." - - # Run the az monitor log-analytics workspace list command to get the workspace name - log_analytics_workspace_name=$(az monitor.log-analytics.workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) - - if [ -z "$log_analytics_workspace_name" ]; then - echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." - else + log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv || true) + if [ -n "$log_analytics_workspace_name" ]; then echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV - echo "Log Analytics workspace name: ${log_analytics_workspace_name}" + echo "Log Analytics workspace name: ${log_analytics_workspace_name}" + else + echo "No Log Analytics workspace found." fi - echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." - - # Run the az resource list command to get the OpenAI resource name - openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv) - - if [ -z "$openai_resource_name" ]; then - echo "No OpenAI resource found in resource group ${{ env.RESOURCE_GROUP_NAME }}." - exit 1 - else + openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv || true) + if [ -n "$openai_resource_name" ]; then echo "OPENAI_RESOURCE_NAME=${openai_resource_name}" >> $GITHUB_ENV - echo "OpenAI resource name: ${openai_resource_name}" + echo "OpenAI resource name: ${openai_resource_name}" + else + echo "No OpenAI resource found." fi - name: List KeyVaults and Store in Array @@ -256,22 +249,10 @@ jobs: echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV fi - - name: Delete Bicep Deployment + - name: Delete Bicep Deployment (defer RG deletion to end) if: always() run: | - set -e - echo "Checking if resource group exists..." - rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) - if [ "$rg_exists" = "true" ]; then - echo "Resource group exist. Cleaning..." - az group delete \ - --name ${{ env.RESOURCE_GROUP_NAME }} \ - --yes \ - --no-wait - echo "Resource group deleted... ${{ env.RESOURCE_GROUP_NAME }}" - else - echo "Resource group does not exists." - fi + echo "Skipping early RG deletion to allow purges to run first." - name: Purge log analytics workspace if: always() @@ -353,48 +334,28 @@ jobs: - name: Purging the Resources if: always() run: | - set -e - echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" - - # Purge OpenAI Resource - echo "Purging the OpenAI Resource..." - if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.VALID_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then - echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" - else - echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" + if [ -n "${{ env.OPENAI_RESOURCE_NAME }}" ]; then + echo "Purging the OpenAI Resource (soft-delete) in region ${{ env.AI_REGION }}..." + az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.AI_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose || echo "OpenAI purge skipped or failed." fi - - # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" - - # Remove the surrounding square brackets, if they exist stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - - # Convert the comma-separated string into an array IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" - - echo "Using KeyVaults Array..." for keyvault_name in "${keyvault_array[@]}"; do - echo "Processing KeyVault: $keyvault_name" - # Check if the KeyVault is soft-deleted - deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}) - - # If the KeyVault is found in the soft-deleted state, purge it + kv_trim=$(echo "$keyvault_name" | sed 's/\"//g' | xargs) + [ -z "$kv_trim" ] && continue + echo "Processing KeyVault: $kv_trim" + deleted_vaults=$(az keyvault list-deleted --query "[?name=='$kv_trim']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo '[]') if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then - echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..." - # Purge the KeyVault - if az keyvault purge --name "$keyvault_name" --no-wait; then - echo "Successfully purged KeyVault '$keyvault_name'." - else - echo "Failed to purge KeyVault '$keyvault_name'." - fi + echo "KeyVault '$kv_trim' soft-deleted. Purging..." + az keyvault purge --name "$kv_trim" --no-wait || echo "Failed to purge KeyVault '$kv_trim'" else - echo "KeyVault '$keyvault_name' is not soft-deleted. No action taken." + echo "KeyVault '$kv_trim' not soft-deleted. Skipping." fi done - echo "Resource purging completed successfully" + echo "Resource purging completed" - name: Send Notification on Failure if: failure() || needs.deploy.result == 'failure' @@ -412,6 +373,18 @@ jobs: -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" + - name: Final Resource Group Deletion + if: always() + run: | + set -e + rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) + if [ "$rg_exists" = true ]; then + echo "Deleting resource group ${{ env.RESOURCE_GROUP_NAME }}..." + az group delete --name ${{ env.RESOURCE_GROUP_NAME }} --yes --no-wait || echo "RG delete command issued." + else + echo "Resource group already gone." + fi + - name: Logout from Azure if: always() run: | From f2e029524d56ee07a99db07c422e439178560129 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 09:33:35 +0530 Subject: [PATCH 07/40] fixing cleanup deployment failure --- .github/workflows/deploy.yml | 182 +++++++++-------------------------- 1 file changed, 48 insertions(+), 134 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 715266ab..417ed1ad 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -198,164 +198,85 @@ jobs: id: get_azure_resources run: | set -e - echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + echo "Collecting resource identifiers from RG ${{ env.RESOURCE_GROUP_NAME }} before deletion..." log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv || true) if [ -n "$log_analytics_workspace_name" ]; then echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV - echo "Log Analytics workspace name: ${log_analytics_workspace_name}" - else - echo "No Log Analytics workspace found." fi - echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv || true) if [ -n "$openai_resource_name" ]; then echo "OPENAI_RESOURCE_NAME=${openai_resource_name}" >> $GITHUB_ENV - echo "OpenAI resource name: ${openai_resource_name}" - else - echo "No OpenAI resource found." fi + keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv || true) + if [ -n "$keyvaults" ]; then + keyvault_array="["; first=true; for kv in $keyvaults; do if [ "$first" = true ]; then keyvault_array="$keyvault_array\"$kv\""; first=false; else keyvault_array="$keyvault_array,\"$kv\""; fi; done; keyvault_array="$keyvault_array]"; echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV; fi + echo "Snapshot complete." - - name: List KeyVaults and Store in Array + - name: Delete Resource Group (primary deletion) if: always() - id: list_keyvaults run: | - set -e - echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." - - # Get the list of KeyVaults in the specified resource group - keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv) - - if [ -z "$keyvaults" ]; then - echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." - echo "KEYVAULTS=[]" >> $GITHUB_ENV # If no KeyVaults found, set an empty array - else - echo "KeyVaults found: $keyvaults" - - # Format the list into an array with proper formatting (no trailing comma) - keyvault_array="[" - first=true - for kv in $keyvaults; do - if [ "$first" = true ]; then - keyvault_array="$keyvault_array\"$kv\"" - first=false - else - keyvault_array="$keyvault_array,\"$kv\"" - fi - done - keyvault_array="$keyvault_array]" - - # Output the formatted array and save it to the environment variable - echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV - fi + echo "Initiating deletion of RG ${{ env.RESOURCE_GROUP_NAME }}..." + az group delete --name ${{ env.RESOURCE_GROUP_NAME }} --yes --no-wait || echo "Delete command issued." - - name: Delete Bicep Deployment (defer RG deletion to end) + - name: Wait for RG deletion propagation if: always() run: | - echo "Skipping early RG deletion to allow purges to run first." + set -e + echo "Polling for RG deletion..." + max_retries=15 + sleep_interval=20 + for i in $(seq 1 $max_retries); do + exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }} || echo true) + if [ "$exists" = false ]; then + echo "Resource group deleted (confirmed)." + break + fi + echo "RG still exists (attempt $i). Waiting $sleep_interval s..." + sleep $sleep_interval + done - - name: Purge log analytics workspace + - name: Purge soft-deleted OpenAI (if any) if: always() - id: log_analytics_workspace run: | - set -e - # Purge Log Analytics Workspace - echo "Purging the Log Analytics Workspace..." - if ! az monitor log-analytics workspace delete --force --resource-group ${{ env.RESOURCE_GROUP_NAME }} --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --yes --verbose; then - echo "Failed to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" + if [ -n "${{ env.OPENAI_RESOURCE_NAME }}" ]; then + echo "Attempting purge of soft-deleted OpenAI: ${{ env.OPENAI_RESOURCE_NAME }} in region ${{ env.AI_REGION }}" + az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.AI_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose || echo "OpenAI purge not available yet or failed (non-fatal)." else - echo "Purged the Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" + echo "No OpenAI resource recorded to purge." fi - echo "Log analytics workspace resource purging completed successfully" - - - - name: Wait for resource deletion to complete + - name: Purge soft-deleted KeyVaults (if any) if: always() run: | - - # List of keyvaults + set -e KEYVAULTS="${{ env.KEYVAULTS }}" - - # Remove the surrounding square brackets, if they exist - stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - - # Convert the comma-separated string into an array - IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults" - - # Append new resources to the array - resources_to_check+=("${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" "${{ env.OPENAI_RESOURCE_NAME }}") - - echo "List of resources to check: ${resources_to_check[@]}" - - # Maximum number of retries - max_retries=3 - - # Retry intervals in seconds (30, 60, 120) - retry_intervals=(30 60 120) - - # Retry mechanism to check resources - retries=0 - while true; do - resource_found=false - - # Get the list of resources in YAML format again on each retry - resource_list=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --output yaml) - - # Iterate through the resources to check - for resource in "${resources_to_check[@]}"; do - echo "Checking resource: $resource" - if echo "$resource_list" | grep -q "name: $resource"; then - echo "Resource '$resource' exists in the resource group." - resource_found=true - else - echo "Resource '$resource' does not exist in the resource group." - fi - done - - # If any resource exists, retry - if [ "$resource_found" = true ]; then - retries=$((retries + 1)) - if [ "$retries" -gt "$max_retries" ]; then - echo "Maximum retry attempts reached. Exiting." - break - else - # Wait for the appropriate interval for the current retry - echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..." - sleep ${retry_intervals[$retries-1]} - fi + stripped=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + IFS=',' read -r -a kvs <<< "$stripped" + for raw in "${kvs[@]}"; do + kv=$(echo "$raw" | sed 's/\"//g' | xargs) + [ -z "$kv" ] && continue + echo "Checking soft-delete state for KeyVault: $kv" + deleted=$(az keyvault list-deleted --query "[?name=='$kv']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo '[]') + if [ "$(echo "$deleted" | jq length)" -gt 0 ]; then + echo "Purging KeyVault $kv ..." + az keyvault purge --name "$kv" --no-wait || echo "Failed to purge $kv (non-fatal)" else - echo "No resources found. Exiting." - break + echo "KeyVault $kv not soft-deleted; skipping." fi done - - - name: Purging the Resources + + - name: Purge Log Analytics Workspace (best-effort) if: always() run: | set -e - echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" - if [ -n "${{ env.OPENAI_RESOURCE_NAME }}" ]; then - echo "Purging the OpenAI Resource (soft-delete) in region ${{ env.AI_REGION }}..." - az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.AI_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose || echo "OpenAI purge skipped or failed." + if [ -n "${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" ]; then + echo "Attempting to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} (best-effort)" + az monitor log-analytics workspace delete --force --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --resource-group ${{ env.RESOURCE_GROUP_NAME }} --yes --verbose || echo "Workspace purge not applicable or already gone." + else + echo "No Log Analytics workspace recorded." fi - KEYVAULTS="${{ env.KEYVAULTS }}" - stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" - for keyvault_name in "${keyvault_array[@]}"; do - kv_trim=$(echo "$keyvault_name" | sed 's/\"//g' | xargs) - [ -z "$kv_trim" ] && continue - echo "Processing KeyVault: $kv_trim" - deleted_vaults=$(az keyvault list-deleted --query "[?name=='$kv_trim']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo '[]') - if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then - echo "KeyVault '$kv_trim' soft-deleted. Purging..." - az keyvault purge --name "$kv_trim" --no-wait || echo "Failed to purge KeyVault '$kv_trim'" - else - echo "KeyVault '$kv_trim' not soft-deleted. Skipping." - fi - done - echo "Resource purging completed" - name: Send Notification on Failure if: failure() || needs.deploy.result == 'failure' @@ -376,14 +297,7 @@ jobs: - name: Final Resource Group Deletion if: always() run: | - set -e - rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) - if [ "$rg_exists" = true ]; then - echo "Deleting resource group ${{ env.RESOURCE_GROUP_NAME }}..." - az group delete --name ${{ env.RESOURCE_GROUP_NAME }} --yes --no-wait || echo "RG delete command issued." - else - echo "Resource group already gone." - fi + echo "Final RG delete step retained for idempotency; primary deletion already initiated." - name: Logout from Azure if: always() From ebf397f8e5f7cd17e45022afdabb53f765a576b9 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 10:15:47 +0530 Subject: [PATCH 08/40] azure template trigger --- .github/workflows/azure-dev-validation.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index d5378b5b..38c1ac64 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - vee-pipeline-fixes permissions: contents: read id-token: write @@ -31,4 +32,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Step 3: Print the result of the validation - name: Print result - run: cat ${{ steps.validation.outputs.resultFile }} + run: cat ${{ steps.validation.outputs.resultFile }} From f3144548ecb8efcb8f91b04ac17c5ff9c33023ce Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 10:25:18 +0530 Subject: [PATCH 09/40] azure template validation fix --- .github/workflows/azure-dev-validation.yml | 89 ++++++++++++++-------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 38c1ac64..e5d1413d 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -1,35 +1,64 @@ -name: Azure Template Validation -on: -# workflow_dispatch: - push: - branches: - - main - - vee-pipeline-fixes +name: Azure Template Validation +on: + workflow_dispatch: + push: + branches: + - main + - vee-pipeline-fixes permissions: contents: read id-token: write pull-requests: write -jobs: - template_validation_job: - runs-on: ubuntu-latest +jobs: + template_validation_job: + runs-on: ubuntu-latest + # Using an environment named 'production' may require approvals; adjust if that caused prior failures. environment: production - name: Template validation - steps: - # Step 1: Checkout the code from your repository - - name: Checkout code - uses: actions/checkout@v4 - # Step 2: Validate the Azure template using microsoft/template-validation-action - - name: Validate Azure Template - uses: microsoft/template-validation-action@Latest - id: validation - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - 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 }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Step 3: Print the result of the validation - - name: Print result - run: cat ${{ steps.validation.outputs.resultFile }} + name: Template validation + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Validate Azure Template + id: validation + uses: microsoft/template-validation-action@main + env: + # These env vars are optional for the action but retained in case the action consumes them. + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + 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 }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Print validation result + if: always() + run: | + if [ -n "${{ steps.validation.outputs.resultFile }}" ] && [ -f "${{ steps.validation.outputs.resultFile }}" ]; then + echo "--- Validation Result File ---" + cat "${{ steps.validation.outputs.resultFile }}" + else + echo "Result file not found (output: '${{ steps.validation.outputs.resultFile }}')." >&2 + fi + + - name: Fail if validation errors detected + run: | + file='${{ steps.validation.outputs.resultFile }}' + if [ ! -f "$file" ]; then + echo "No validation result file produced; failing." >&2 + exit 1 + fi + # Heuristic: look for common error markers. + if grep -Ei '"(status|level)" *: *"error"' "$file" || grep -Ei '\b(error|failed)\b' "$file"; then + echo "Errors detected in template validation output." >&2 + exit 1 + fi + echo "No blocking errors detected in validation output." From 6302ffc0a8eb4ac609f7bba98d2f30c60dbff134 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 10:45:45 +0530 Subject: [PATCH 10/40] test template validation --- .github/workflows/azure-dev-validation.yml | 68 +++++++++++++++++----- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index e5d1413d..3c810b3c 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -12,53 +12,93 @@ permissions: jobs: template_validation_job: runs-on: ubuntu-latest - # Using an environment named 'production' may require approvals; adjust if that caused prior failures. - environment: production + environment: validation name: Template validation steps: - name: Checkout code uses: actions/checkout@v4 - - name: Azure Login + - name: Pre-flight secret check + id: secret_check + run: | + missing=0 + for var in AZURE_CLIENT_ID AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID; do + if [ -z "${{ secrets[format('{0}', var)] }}" ]; then + echo "::error::Required secret $var is missing." >&2 + missing=1 + fi + done + if [ "$missing" -eq 1 ]; then + echo "Missing required secrets. Failing early." >&2 + exit 1 + fi + echo "All required auth secrets present (client secret not required for OIDC)." + + - name: Azure Login (OIDC) uses: azure/login@v1 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Debug Azure context + run: | + az account show || echo "Could not show account (ensure privileges)" >&2 + echo "Listing bicep version (if installed):"; az bicep version || true + echo "Listing repo root:"; ls -1 . || true + echo "Infra directory content:"; ls -1 infra || true + - name: Validate Azure Template id: validation uses: microsoft/template-validation-action@main env: - # These env vars are optional for the action but retained in case the action consumes them. AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} 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 }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + + - name: Capture validation output + id: capture + run: | + out="${{ steps.validation.outputs.resultFile }}" + if [ -n "$out" ] && [ -f "$out" ]; then + cp "$out" validation-result.json + else + echo '{"warning":"No resultFile produced by action"}' > validation-result.json + fi + echo "result_path=validation-result.json" >> $GITHUB_OUTPUT - name: Print validation result if: always() run: | - if [ -n "${{ steps.validation.outputs.resultFile }}" ] && [ -f "${{ steps.validation.outputs.resultFile }}" ]; then - echo "--- Validation Result File ---" - cat "${{ steps.validation.outputs.resultFile }}" - else - echo "Result file not found (output: '${{ steps.validation.outputs.resultFile }}')." >&2 - fi + echo "--- validation-result.json ---" + cat validation-result.json || echo "No validation-result.json present" >&2 + + - name: Upload validation result artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: validation-result + path: validation-result.json + retention-days: 7 - name: Fail if validation errors detected run: | - file='${{ steps.validation.outputs.resultFile }}' + file='validation-result.json' if [ ! -f "$file" ]; then echo "No validation result file produced; failing." >&2 exit 1 fi - # Heuristic: look for common error markers. if grep -Ei '"(status|level)" *: *"error"' "$file" || grep -Ei '\b(error|failed)\b' "$file"; then echo "Errors detected in template validation output." >&2 + cat "$file" + exit 1 + fi + # Also treat underlying action non-zero exit as failure even if heuristic passes. + if [ "${{ steps.validation.outcome }}" = "failure" ]; then + echo "Underlying validation action reported failure (steps.validation.outcome)." >&2 exit 1 fi echo "No blocking errors detected in validation output." From 4e412588d339239970be70926e35070e41b445ef Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 11:02:52 +0530 Subject: [PATCH 11/40] deploy yml file changes for template validation --- .github/workflows/deploy.yml | 73 ++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 417ed1ad..b16954f2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,7 @@ jobs: SELECTED_AI_REGION: ${{ steps.deploy.outputs.selected_ai_region || env.VALID_REGION }} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Azure CLI run: | @@ -38,19 +38,33 @@ jobs: - name: Run Quota Check id: quota-check run: | + set -e export AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }} export AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }} - export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }} + export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }} export AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}" export GPT_MIN_CAPACITY="${{ env.GPT_MIN_CAPACITY }}" export AZURE_REGIONS="${{ vars.AZURE_REGIONS }}" chmod +x scripts/checkquota.sh - if ! scripts/checkquota.sh; then - # If quota check fails due to insufficient quota, set the flag - if grep -q "No region with sufficient quota found" scripts/checkquota.sh; then - echo "QUOTA_FAILED=true" >> $GITHUB_ENV - fi - exit 1 # Fail the pipeline if any other failure occurs + echo "Running quota script..." + # Capture stdout & stderr + if ! bash -c './scripts/checkquota.sh' | tee quota.out; then + echo "Quota script exited non-zero." >&2 + fi + echo "--- Quota Script Output (truncated) ---" + head -100 quota.out || true + # Parse VALID_REGION if present + REGION_LINE=$(grep -E '^VALID_REGION=' quota.out || true) + if [ -n "$REGION_LINE" ]; then + echo "$REGION_LINE" >> $GITHUB_ENV + echo "Captured $REGION_LINE" + fi + if grep -qi 'No region with sufficient quota found' quota.out; then + echo "QUOTA_FAILED=true" >> $GITHUB_ENV + echo "Quota failure detected: no region with sufficient quota." >&2 + fi + if ! grep -q '^VALID_REGION=' quota.out; then + echo "WARNING: VALID_REGION not found; will fallback later." >&2 fi - name: Send Notification on Quota Failure @@ -75,6 +89,14 @@ jobs: - name: Install Bicep CLI run: az bicep install + - name: Install jq + run: | + if ! command -v jq >/dev/null 2>&1; then + sudo apt-get update -y + sudo apt-get install -y jq + fi + jq --version + - name: Generate Resource Group Name id: generate_rg_name run: | @@ -127,10 +149,14 @@ jobs: IMAGE_TAG="latest" fi - EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" + EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" # VALID_REGION exported earlier if quota script succeeded echo "Using AI Deployments Region: $EFFECTIVE_AI_REGION" echo "selected_ai_region=$EFFECTIVE_AI_REGION" >> $GITHUB_OUTPUT + echo "Resource Group: ${{ env.RESOURCE_GROUP_NAME }} (Created in northcentralus)" + echo "Solution Prefix: ${{ env.SOLUTION_PREFIX }}" + echo "Image Tag: ${IMAGE_TAG}" + az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ @@ -142,26 +168,35 @@ jobs: capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" - - name: Assign Contributor role to Service Principal + - name: Assign Contributor role to Service Principal (Idempotent) if: always() run: | - echo "Assigning Contributor role to SPN for RG: ${{ env.RESOURCE_GROUP_NAME }}" - az role assignment create \ - --assignee ${{ secrets.AZURE_CLIENT_ID }} \ - --role "Contributor" \ - --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} + scope=/subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} + echo "Ensuring Contributor role on scope: $scope" + existing=$(az role assignment list --assignee ${{ secrets.AZURE_CLIENT_ID }} --scope "$scope" --query '[0].id' -o tsv || true) + if [ -n "$existing" ]; then + echo "Role assignment already exists: $existing" + else + az role assignment create --assignee ${{ secrets.AZURE_CLIENT_ID }} --role "Contributor" --scope "$scope" || echo "Non-fatal: role assignment create failed (possibly permission or propagation delay)." + fi - name: Get Deployment Output and extract Values id: get_output run: | set -e - echo "Fetching deployment output..." + echo "Fetching deployment outputs..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) - echo "Extracting deployment output..." - WEBAPP_URL=$(echo $BICEP_OUTPUT | jq -r '.weB_APP_URL.value') + echo "Raw outputs JSON length: $(echo "$BICEP_OUTPUT" | wc -c)" + # Correct output key is WEB_APP_URL (verified in infra/main.bicep) + WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r '.WEB_APP_URL.value // empty') + if [ -z "$WEBAPP_URL" ]; then + echo "::error::WEB_APP_URL output missing or empty. Full outputs below:" >&2 + echo "$BICEP_OUTPUT" >&2 + exit 1 + fi echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Deployment output: $BICEP_OUTPUT" + echo "Resolved WEB_APP_URL: $WEBAPP_URL" - name: Logout from Azure if: always() From ca177febe270e26a9ac6a359f0e2d69a703f90c2 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 12:00:05 +0530 Subject: [PATCH 12/40] revert deploy yml file --- .github/workflows/deploy.yml | 67 ++++++++++-------------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b16954f2..d7211388 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,7 @@ jobs: SELECTED_AI_REGION: ${{ steps.deploy.outputs.selected_ai_region || env.VALID_REGION }} steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v3 - name: Setup Azure CLI run: | @@ -38,33 +38,19 @@ jobs: - name: Run Quota Check id: quota-check run: | - set -e export AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }} export AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }} - export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }} + export AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }} export AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}" export GPT_MIN_CAPACITY="${{ env.GPT_MIN_CAPACITY }}" export AZURE_REGIONS="${{ vars.AZURE_REGIONS }}" chmod +x scripts/checkquota.sh - echo "Running quota script..." - # Capture stdout & stderr - if ! bash -c './scripts/checkquota.sh' | tee quota.out; then - echo "Quota script exited non-zero." >&2 - fi - echo "--- Quota Script Output (truncated) ---" - head -100 quota.out || true - # Parse VALID_REGION if present - REGION_LINE=$(grep -E '^VALID_REGION=' quota.out || true) - if [ -n "$REGION_LINE" ]; then - echo "$REGION_LINE" >> $GITHUB_ENV - echo "Captured $REGION_LINE" - fi - if grep -qi 'No region with sufficient quota found' quota.out; then - echo "QUOTA_FAILED=true" >> $GITHUB_ENV - echo "Quota failure detected: no region with sufficient quota." >&2 - fi - if ! grep -q '^VALID_REGION=' quota.out; then - echo "WARNING: VALID_REGION not found; will fallback later." >&2 + if ! scripts/checkquota.sh; then + # If quota check fails due to insufficient quota, set the flag + if grep -q "No region with sufficient quota found" scripts/checkquota.sh; then + echo "QUOTA_FAILED=true" >> $GITHUB_ENV + fi + exit 1 # Fail the pipeline if any other failure occurs fi - name: Send Notification on Quota Failure @@ -89,13 +75,7 @@ jobs: - name: Install Bicep CLI run: az bicep install - - name: Install jq - run: | - if ! command -v jq >/dev/null 2>&1; then - sudo apt-get update -y - sudo apt-get install -y jq - fi - jq --version + # reverted: jq explicit installation step removed - name: Generate Resource Group Name id: generate_rg_name @@ -168,35 +148,26 @@ jobs: capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" - - name: Assign Contributor role to Service Principal (Idempotent) + - name: Assign Contributor role to Service Principal if: always() run: | - scope=/subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} - echo "Ensuring Contributor role on scope: $scope" - existing=$(az role assignment list --assignee ${{ secrets.AZURE_CLIENT_ID }} --scope "$scope" --query '[0].id' -o tsv || true) - if [ -n "$existing" ]; then - echo "Role assignment already exists: $existing" - else - az role assignment create --assignee ${{ secrets.AZURE_CLIENT_ID }} --role "Contributor" --scope "$scope" || echo "Non-fatal: role assignment create failed (possibly permission or propagation delay)." - fi + echo "Assigning Contributor role to SPN for RG: ${{ env.RESOURCE_GROUP_NAME }}" + az role assignment create \ + --assignee ${{ secrets.AZURE_CLIENT_ID }} \ + --role "Contributor" \ + --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} - name: Get Deployment Output and extract Values id: get_output run: | set -e - echo "Fetching deployment outputs..." + echo "Fetching deployment output..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) - echo "Raw outputs JSON length: $(echo "$BICEP_OUTPUT" | wc -c)" - # Correct output key is WEB_APP_URL (verified in infra/main.bicep) - WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r '.WEB_APP_URL.value // empty') - if [ -z "$WEBAPP_URL" ]; then - echo "::error::WEB_APP_URL output missing or empty. Full outputs below:" >&2 - echo "$BICEP_OUTPUT" >&2 - exit 1 - fi + echo "Extracting deployment output..." + WEBAPP_URL=$(echo $BICEP_OUTPUT | jq -r '.weB_APP_URL.value') echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Resolved WEB_APP_URL: $WEBAPP_URL" + echo "Deployment output: $BICEP_OUTPUT" - name: Logout from Azure if: always() From 4c72c2540fe45eb751278b974bf0acf703cfb2a9 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 13:35:16 +0530 Subject: [PATCH 13/40] added list key val step --- .github/workflows/deploy.yml | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d7211388..582f9e0d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -165,9 +165,15 @@ jobs: echo "Fetching deployment output..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) echo "Extracting deployment output..." - WEBAPP_URL=$(echo $BICEP_OUTPUT | jq -r '.weB_APP_URL.value') + WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r '.WEB_APP_URL.value') + if [ -z "$WEBAPP_URL" ] || [ "$WEBAPP_URL" = "null" ]; then + echo "WEB_APP_URL not found in deployment outputs" >&2 + echo "Full outputs: $BICEP_OUTPUT" >&2 + exit 1 + fi echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Deployment output: $BICEP_OUTPUT" + echo "Deployment output (truncated for log clarity):" + echo "$BICEP_OUTPUT" | head -c 2000 - name: Logout from Azure if: always() @@ -213,11 +219,35 @@ jobs: if [ -n "$openai_resource_name" ]; then echo "OPENAI_RESOURCE_NAME=${openai_resource_name}" >> $GITHUB_ENV fi - keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv || true) - if [ -n "$keyvaults" ]; then - keyvault_array="["; first=true; for kv in $keyvaults; do if [ "$first" = true ]; then keyvault_array="$keyvault_array\"$kv\""; first=false; else keyvault_array="$keyvault_array,\"$kv\""; fi; done; keyvault_array="$keyvault_array]"; echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV; fi echo "Snapshot complete." + - name: List KeyVaults and Store in Array + if: always() + id: list_keyvaults + run: | + set -e + echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." + keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv || true) + if [ -z "$keyvaults" ]; then + echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." + echo "KEYVAULTS=[]" >> $GITHUB_ENV + else + echo "KeyVaults found: $keyvaults" + keyvault_array="[" + first=true + for kv in $keyvaults; do + if [ "$first" = true ]; then + keyvault_array="$keyvault_array\"$kv\"" + first=false + else + keyvault_array="$keyvault_array,\"$kv\"" + fi + done + keyvault_array="$keyvault_array]" + echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV + fi + echo "Stored KeyVault array in KEYVAULTS environment variable." + - name: Delete Resource Group (primary deletion) if: always() run: | From 30223e72682dcac56084517652181a51b3986b19 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 14:37:03 +0530 Subject: [PATCH 14/40] deploy failed fix --- .github/workflows/deploy.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 582f9e0d..acc4774f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -165,14 +165,19 @@ jobs: echo "Fetching deployment output..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) echo "Extracting deployment output..." - WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r '.WEB_APP_URL.value') + echo "Available output keys:" + echo "$BICEP_OUTPUT" | jq -r 'keys | join(", ")' + # Case-insensitive lookup for WEB_APP_URL because deployment outputs returned inconsistent casing previously + WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r 'to_entries | map(select((.key|ascii_upcase)=="WEB_APP_URL")) | .[0].value.value // empty') if [ -z "$WEBAPP_URL" ] || [ "$WEBAPP_URL" = "null" ]; then - echo "WEB_APP_URL not found in deployment outputs" >&2 - echo "Full outputs: $BICEP_OUTPUT" >&2 + echo "WEB_APP_URL not found in deployment outputs (case-insensitive scan)" >&2 + echo "Full outputs JSON follows for troubleshooting:" >&2 + echo "$BICEP_OUTPUT" >&2 exit 1 fi + echo "Resolved WEBAPP_URL: $WEBAPP_URL" echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Deployment output (truncated for log clarity):" + echo "Deployment outputs (truncated):" echo "$BICEP_OUTPUT" | head -c 2000 - name: Logout from Azure From d6ea884b70e4a38da59597bd5544eeefa528e4ab Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 16:08:16 +0530 Subject: [PATCH 15/40] validation template fix --- .github/workflows/azure-dev-validation.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 3c810b3c..8704f5de 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -21,13 +21,12 @@ jobs: - name: Pre-flight secret check id: secret_check run: | + # GitHub Actions expressions are evaluated before the shell loop runs, so we cannot + # index into secrets dynamically like secrets[format('{0}', var)]. Instead, check each explicitly. missing=0 - for var in AZURE_CLIENT_ID AZURE_TENANT_ID AZURE_SUBSCRIPTION_ID; do - if [ -z "${{ secrets[format('{0}', var)] }}" ]; then - echo "::error::Required secret $var is missing." >&2 - missing=1 - fi - done + if [ -z "${{ secrets.AZURE_CLIENT_ID }}" ]; then echo "::error::Required secret AZURE_CLIENT_ID is missing." >&2; missing=1; fi + if [ -z "${{ secrets.AZURE_TENANT_ID }}" ]; then echo "::error::Required secret AZURE_TENANT_ID is missing." >&2; missing=1; fi + if [ -z "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]; then echo "::error::Required secret AZURE_SUBSCRIPTION_ID is missing." >&2; missing=1; fi if [ "$missing" -eq 1 ]; then echo "Missing required secrets. Failing early." >&2 exit 1 From 9f1296eda7712ea2e887fe04407bc62b886bb7d1 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 16:12:38 +0530 Subject: [PATCH 16/40] template fix --- .github/workflows/azure-dev-validation.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 8704f5de..01213484 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -27,12 +27,18 @@ jobs: if [ -z "${{ secrets.AZURE_CLIENT_ID }}" ]; then echo "::error::Required secret AZURE_CLIENT_ID is missing." >&2; missing=1; fi if [ -z "${{ secrets.AZURE_TENANT_ID }}" ]; then echo "::error::Required secret AZURE_TENANT_ID is missing." >&2; missing=1; fi if [ -z "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]; then echo "::error::Required secret AZURE_SUBSCRIPTION_ID is missing." >&2; missing=1; fi + if [ -z "${{ secrets.AZURE_LOCATION }}" ]; then echo "::warning::AZURE_LOCATION not set; defaulting to eastus for validation context."; echo "AZURE_LOCATION=eastus" >> $GITHUB_ENV; else echo "AZURE_LOCATION=${{ secrets.AZURE_LOCATION }}" >> $GITHUB_ENV; fi if [ "$missing" -eq 1 ]; then echo "Missing required secrets. Failing early." >&2 exit 1 fi echo "All required auth secrets present (client secret not required for OIDC)." + - name: Ensure Bicep CLI installed + run: | + az bicep install || true + echo "Bicep version:"; az bicep version || echo "Bicep not available" >&2 + - name: Azure Login (OIDC) uses: azure/login@v1 with: @@ -43,9 +49,10 @@ jobs: - name: Debug Azure context run: | az account show || echo "Could not show account (ensure privileges)" >&2 - echo "Listing bicep version (if installed):"; az bicep version || true + echo "Listing bicep version:"; az bicep version || true echo "Listing repo root:"; ls -1 . || true echo "Infra directory content:"; ls -1 infra || true + echo "Resolved AZURE_LOCATION: ${AZURE_LOCATION:-unset}" - name: Validate Azure Template id: validation From bfa33aac78b30b07e077ab0c2cf21992078c5a66 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 16:18:05 +0530 Subject: [PATCH 17/40] login fix --- .github/workflows/azure-dev-validation.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 01213484..20f7651c 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -46,6 +46,23 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Fallback Azure Login (Client Secret) + if: failure() && secrets.AZURE_CLIENT_SECRET != '' + run: | + echo "OIDC login failed; attempting client secret login fallback." >&2 + az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} || { + echo "Client secret fallback login also failed." >&2; exit 1; } + az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || true + + - name: Post-login diagnostics + if: always() + run: | + echo "Login diagnostics:" + az account show || echo "Account show failed" >&2 + echo "Listing subscriptions (top 5):"; az account list --query '[].{name:name,id:id}' -o table | head -n 7 || true + echo "Active tenant: ${{ secrets.AZURE_TENANT_ID }}" + echo "Client ID suffix: $(echo '${{ secrets.AZURE_CLIENT_ID }}' | tail -c 6)" + - name: Debug Azure context run: | az account show || echo "Could not show account (ensure privileges)" >&2 From a9201fa3c9a5f269707921b6b9b90ce911e80bdb Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 16:22:30 +0530 Subject: [PATCH 18/40] login fix template --- .github/workflows/azure-dev-validation.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 20f7651c..681196a0 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -47,8 +47,14 @@ jobs: subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Fallback Azure Login (Client Secret) - if: failure() && secrets.AZURE_CLIENT_SECRET != '' + if: failure() + env: + HAS_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET != '' }} run: | + if [ "${HAS_CLIENT_SECRET}" != "true" ]; then + echo "OIDC login failed and no client secret available for fallback." >&2 + exit 1 + fi echo "OIDC login failed; attempting client secret login fallback." >&2 az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} || { echo "Client secret fallback login also failed." >&2; exit 1; } From 427d54a1fda86873df8d41c8c3a68bfe58905811 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 17:05:57 +0530 Subject: [PATCH 19/40] template fix login --- .github/workflows/azure-dev-validation.yml | 40 +++++++++++++--------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 681196a0..d2025138 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest environment: validation name: Template validation + env: + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} steps: - name: Checkout code uses: actions/checkout@v4 @@ -37,28 +39,32 @@ jobs: - name: Ensure Bicep CLI installed run: | az bicep install || true - echo "Bicep version:"; az bicep version || echo "Bicep not available" >&2 - - - name: Azure Login (OIDC) + - name: Azure Login (Service Principal Client Secret) + if: ${{ env.AZURE_CLIENT_SECRET != '' }} + run: | + echo "Using service principal client secret authentication path." + az login --service-principal \ + -u ${{ secrets.AZURE_CLIENT_ID }} \ + -p ${{ secrets.AZURE_CLIENT_SECRET }} \ + --tenant ${{ secrets.AZURE_TENANT_ID }} + az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} + --tenant ${{ secrets.AZURE_TENANT_ID }} + - name: Azure Login (OIDC) (no client secret present) + if: ${{ env.AZURE_CLIENT_SECRET == '' }} uses: azure/login@v1 with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + continue-on-error: false - - name: Fallback Azure Login (Client Secret) - if: failure() - env: - HAS_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET != '' }} + - name: Auth Mode Announcement run: | - if [ "${HAS_CLIENT_SECRET}" != "true" ]; then - echo "OIDC login failed and no client secret available for fallback." >&2 - exit 1 + if [ -n "${{ secrets.AZURE_CLIENT_SECRET }}" ]; then + echo "Auth mode: client secret (SP). OIDC skipped." + else + echo "Auth mode: OIDC (no client secret set). Ensure federated credential exists for environment 'validation'." fi - echo "OIDC login failed; attempting client secret login fallback." >&2 - az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} || { - echo "Client secret fallback login also failed." >&2; exit 1; } - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || true - name: Post-login diagnostics if: always() From d01abcfd1c1977838295e0c8ebb39f289141334f Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 17:14:22 +0530 Subject: [PATCH 20/40] latest fix --- .github/workflows/azure-dev-validation.yml | 165 ++++----------------- 1 file changed, 30 insertions(+), 135 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index d2025138..8f95e7c7 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -1,139 +1,34 @@ -name: Azure Template Validation -on: - workflow_dispatch: - push: - branches: - - main - - vee-pipeline-fixes +name: Azure Template Validation +on: +# workflow_dispatch: + push: + branches: + - main permissions: contents: read id-token: write pull-requests: write -jobs: - template_validation_job: - runs-on: ubuntu-latest - environment: validation - name: Template validation - env: - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Pre-flight secret check - id: secret_check - run: | - # GitHub Actions expressions are evaluated before the shell loop runs, so we cannot - # index into secrets dynamically like secrets[format('{0}', var)]. Instead, check each explicitly. - missing=0 - if [ -z "${{ secrets.AZURE_CLIENT_ID }}" ]; then echo "::error::Required secret AZURE_CLIENT_ID is missing." >&2; missing=1; fi - if [ -z "${{ secrets.AZURE_TENANT_ID }}" ]; then echo "::error::Required secret AZURE_TENANT_ID is missing." >&2; missing=1; fi - if [ -z "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]; then echo "::error::Required secret AZURE_SUBSCRIPTION_ID is missing." >&2; missing=1; fi - if [ -z "${{ secrets.AZURE_LOCATION }}" ]; then echo "::warning::AZURE_LOCATION not set; defaulting to eastus for validation context."; echo "AZURE_LOCATION=eastus" >> $GITHUB_ENV; else echo "AZURE_LOCATION=${{ secrets.AZURE_LOCATION }}" >> $GITHUB_ENV; fi - if [ "$missing" -eq 1 ]; then - echo "Missing required secrets. Failing early." >&2 - exit 1 - fi - echo "All required auth secrets present (client secret not required for OIDC)." - - - name: Ensure Bicep CLI installed - run: | - az bicep install || true - - name: Azure Login (Service Principal Client Secret) - if: ${{ env.AZURE_CLIENT_SECRET != '' }} - run: | - echo "Using service principal client secret authentication path." - az login --service-principal \ - -u ${{ secrets.AZURE_CLIENT_ID }} \ - -p ${{ secrets.AZURE_CLIENT_SECRET }} \ - --tenant ${{ secrets.AZURE_TENANT_ID }} - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} - --tenant ${{ secrets.AZURE_TENANT_ID }} - - name: Azure Login (OIDC) (no client secret present) - if: ${{ env.AZURE_CLIENT_SECRET == '' }} - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - continue-on-error: false - - - name: Auth Mode Announcement - run: | - if [ -n "${{ secrets.AZURE_CLIENT_SECRET }}" ]; then - echo "Auth mode: client secret (SP). OIDC skipped." - else - echo "Auth mode: OIDC (no client secret set). Ensure federated credential exists for environment 'validation'." - fi - - - name: Post-login diagnostics - if: always() - run: | - echo "Login diagnostics:" - az account show || echo "Account show failed" >&2 - echo "Listing subscriptions (top 5):"; az account list --query '[].{name:name,id:id}' -o table | head -n 7 || true - echo "Active tenant: ${{ secrets.AZURE_TENANT_ID }}" - echo "Client ID suffix: $(echo '${{ secrets.AZURE_CLIENT_ID }}' | tail -c 6)" - - - name: Debug Azure context - run: | - az account show || echo "Could not show account (ensure privileges)" >&2 - echo "Listing bicep version:"; az bicep version || true - echo "Listing repo root:"; ls -1 . || true - echo "Infra directory content:"; ls -1 infra || true - echo "Resolved AZURE_LOCATION: ${AZURE_LOCATION:-unset}" - - - name: Validate Azure Template - id: validation - uses: microsoft/template-validation-action@main - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: true - - - name: Capture validation output - id: capture - run: | - out="${{ steps.validation.outputs.resultFile }}" - if [ -n "$out" ] && [ -f "$out" ]; then - cp "$out" validation-result.json - else - echo '{"warning":"No resultFile produced by action"}' > validation-result.json - fi - echo "result_path=validation-result.json" >> $GITHUB_OUTPUT - - - name: Print validation result - if: always() - run: | - echo "--- validation-result.json ---" - cat validation-result.json || echo "No validation-result.json present" >&2 - - - name: Upload validation result artifact - if: always() - uses: actions/upload-artifact@v4 - with: - name: validation-result - path: validation-result.json - retention-days: 7 - - - name: Fail if validation errors detected - run: | - file='validation-result.json' - if [ ! -f "$file" ]; then - echo "No validation result file produced; failing." >&2 - exit 1 - fi - if grep -Ei '"(status|level)" *: *"error"' "$file" || grep -Ei '\b(error|failed)\b' "$file"; then - echo "Errors detected in template validation output." >&2 - cat "$file" - exit 1 - fi - # Also treat underlying action non-zero exit as failure even if heuristic passes. - if [ "${{ steps.validation.outcome }}" = "failure" ]; then - echo "Underlying validation action reported failure (steps.validation.outcome)." >&2 - exit 1 - fi - echo "No blocking errors detected in validation output." +jobs: + template_validation_job: + runs-on: ubuntu-latest + environment: dev + name: Template validation + steps: + # Step 1: Checkout the code from your repository + - name: Checkout code + uses: actions/checkout@v4 + # Step 2: Validate the Azure template using microsoft/template-validation-action + - name: Validate Azure Template + uses: microsoft/template-validation-action@v0.3.5 + id: validation + env: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + 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 }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Step 3: Print the result of the validation + - name: Print result + run: cat ${{ steps.validation.outputs.resultFile }} \ No newline at end of file From de9cc7955e834f8c562c0e410a9d18fe33d412a2 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 18 Sep 2025 17:20:00 +0530 Subject: [PATCH 21/40] add branach name for trigger --- .github/workflows/azure-dev-validation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 8f95e7c7..7e558f48 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - vee-pipeline-fixes permissions: contents: read id-token: write From 7e235fa6d31ecb3099780ff59ac947d08901be4b Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 23 Sep 2025 10:34:03 +0530 Subject: [PATCH 22/40] Removing Local Branch Name in All Pipelines --- .github/workflows/azure-dev-validation.yml | 1 - .github/workflows/build-docker-images.yml | 4 +--- .github/workflows/build-docker.yml | 2 -- .github/workflows/deploy.yml | 6 +++--- .github/workflows/test.yml | 2 -- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 7e558f48..8f95e7c7 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - vee-pipeline-fixes permissions: contents: read id-token: write diff --git a/.github/workflows/build-docker-images.yml b/.github/workflows/build-docker-images.yml index df6213c6..7519d620 100644 --- a/.github/workflows/build-docker-images.yml +++ b/.github/workflows/build-docker-images.yml @@ -7,14 +7,12 @@ on: - dev - demo - hotfix - - vee-pipeline-fixes pull_request: branches: - main - dev - demo - hotfix - - vee-pipeline-fixes types: - opened - ready_for_review @@ -41,5 +39,5 @@ jobs: password_secret: ${{ matrix.password_secret }} app_name: ${{ matrix.app_name }} dockerfile: ${{ matrix.dockerfile }} - push: ${{ github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'hotfix' || github.ref_name == 'vee-pipeline-fixes' }} + push: ${{ github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'hotfix' }} secrets: inherit \ No newline at end of file diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 943107a7..55da1c59 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -59,8 +59,6 @@ jobs: echo "tagname=demo" >> $GITHUB_OUTPUT elif [[ "${{ github.ref_name }}" == "hotfix" ]]; then echo "tagname=hotfix" >> $GITHUB_OUTPUT - elif [[ "${{ github.ref_name }}" == "vee-pipeline-fixes" ]]; then - echo "tagname=vee-pipeline-fixes" >> $GITHUB_OUTPUT else echo "tagname=default" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index acc4774f..89bef742 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,9 @@ on: types: - completed branches: - - vee-pipeline-fixes + - main + - dev + - demo schedule: - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT workflow_dispatch: @@ -123,8 +125,6 @@ jobs: IMAGE_TAG="dev" elif [[ "${{ env.BRANCH_NAME }}" == "demo" ]]; then IMAGE_TAG="demo" - elif [[ "${{ env.BRANCH_NAME }}" == "vee-pipeline-fixes" ]]; then - IMAGE_TAG="vee-pipeline-fixes" else IMAGE_TAG="latest" fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4870ae15..1c3216e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,6 @@ on: - main - dev - demo - - vee-pipeline-fixes pull_request: types: - opened @@ -17,7 +16,6 @@ on: - main - dev - demo - - vee-pipeline-fixes jobs: # frontend_tests: From 94d25da6c7c2d6ec4c6e371e3fbb5e72e9958bc3 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 23 Sep 2025 10:44:46 +0530 Subject: [PATCH 23/40] dev to production change --- .github/workflows/azure-dev-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 8f95e7c7..2619e449 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -11,7 +11,7 @@ permissions: jobs: template_validation_job: runs-on: ubuntu-latest - environment: dev + environment: production name: Template validation steps: # Step 1: Checkout the code from your repository From 332ea2b87c9ec53b0c984dbdc14f5e590cf982bb Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Wed, 24 Sep 2025 16:57:29 +0530 Subject: [PATCH 24/40] removing waf param --- .github/workflows/azure-dev-validation.yml | 3 +- .github/workflows/deploy.yml | 239 +++++++++++---------- .github/workflows/test.yml | 10 +- 3 files changed, 138 insertions(+), 114 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 2619e449..fc98fd7d 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - vee-pipeline-fixes permissions: contents: read id-token: write @@ -19,7 +20,7 @@ jobs: uses: actions/checkout@v4 # Step 2: Validate the Azure template using microsoft/template-validation-action - 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 }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 89bef742..50c1b378 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,6 +9,7 @@ on: - main - dev - demo + - vee-pipeline-fixes schedule: - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT workflow_dispatch: @@ -23,7 +24,6 @@ jobs: outputs: RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }} WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }} - SELECTED_AI_REGION: ${{ steps.deploy.outputs.selected_ai_region || env.VALID_REGION }} steps: - name: Checkout Code uses: actions/checkout@v3 @@ -32,11 +32,9 @@ jobs: run: | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az --version # Verify installation - - name: Login to Azure run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - - name: Run Quota Check id: quota-check run: | @@ -54,7 +52,6 @@ jobs: fi exit 1 # Fail the pipeline if any other failure occurs fi - - name: Send Notification on Quota Failure if: env.QUOTA_FAILED == 'true' run: | @@ -65,11 +62,9 @@ jobs: } EOF ) - curl -X POST "${{ secrets.LOGIC_APP_URL }}" \ -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" - - name: Fail Pipeline if Quota Check Fails if: env.QUOTA_FAILED == 'true' run: exit 1 @@ -77,8 +72,6 @@ jobs: - name: Install Bicep CLI run: az bicep install - # reverted: jq explicit installation step removed - - name: Generate Resource Group Name id: generate_rg_name run: | @@ -88,7 +81,6 @@ jobs: UNIQUE_RG_NAME="arg-${ACCL_NAME}-${SHORT_UUID}" echo "RESOURCE_GROUP_NAME=${UNIQUE_RG_NAME}" >> $GITHUB_ENV echo "Generated RESOURCE_GROUP_NAME: ${UNIQUE_RG_NAME}" - - name: Check and Create Resource Group id: check_create_rg run: | @@ -102,7 +94,6 @@ jobs: echo "Resource group already exists." fi echo "RESOURCE_GROUP_NAME=${{ env.RESOURCE_GROUP_NAME }}" >> $GITHUB_OUTPUT - - name: Generate Unique Solution Prefix id: generate_solution_prefix run: | @@ -113,7 +104,6 @@ jobs: UNIQUE_SOLUTION_PREFIX="${COMMON_PART}${UPDATED_TIMESTAMP}" echo "SOLUTION_PREFIX=${UNIQUE_SOLUTION_PREFIX}" >> $GITHUB_ENV echo "Generated SOLUTION_PREFIX: ${UNIQUE_SOLUTION_PREFIX}" - - name: Deploy Bicep Template id: deploy run: | @@ -128,25 +118,15 @@ jobs: else IMAGE_TAG="latest" fi - - EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" # VALID_REGION exported earlier if quota script succeeded - echo "Using AI Deployments Region: $EFFECTIVE_AI_REGION" - echo "selected_ai_region=$EFFECTIVE_AI_REGION" >> $GITHUB_OUTPUT - - echo "Resource Group: ${{ env.RESOURCE_GROUP_NAME }} (Created in northcentralus)" - echo "Solution Prefix: ${{ env.SOLUTION_PREFIX }}" - echo "Image Tag: ${IMAGE_TAG}" - az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="$EFFECTIVE_AI_REGION" \ - useWafAlignedArchitecture=false \ + aiDeploymentsLocation="eastus" \ capacity=${{ env.GPT_MIN_CAPACITY }} \ - imageVersion="${IMAGE_TAG}" \ + imageVersion="${IMAGE_TAG}"\ createdBy="Pipeline" - name: Assign Contributor role to Service Principal if: always() @@ -156,8 +136,6 @@ jobs: --assignee ${{ secrets.AZURE_CLIENT_ID }} \ --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} - - - name: Get Deployment Output and extract Values id: get_output run: | @@ -165,27 +143,14 @@ jobs: echo "Fetching deployment output..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) echo "Extracting deployment output..." - echo "Available output keys:" - echo "$BICEP_OUTPUT" | jq -r 'keys | join(", ")' - # Case-insensitive lookup for WEB_APP_URL because deployment outputs returned inconsistent casing previously - WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r 'to_entries | map(select((.key|ascii_upcase)=="WEB_APP_URL")) | .[0].value.value // empty') - if [ -z "$WEBAPP_URL" ] || [ "$WEBAPP_URL" = "null" ]; then - echo "WEB_APP_URL not found in deployment outputs (case-insensitive scan)" >&2 - echo "Full outputs JSON follows for troubleshooting:" >&2 - echo "$BICEP_OUTPUT" >&2 - exit 1 - fi - echo "Resolved WEBAPP_URL: $WEBAPP_URL" + WEBAPP_URL=$(echo $BICEP_OUTPUT | jq -r '.weB_APP_URL.value') echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Deployment outputs (truncated):" - echo "$BICEP_OUTPUT" | head -c 2000 - + echo "Deployment output: $BICEP_OUTPUT" - name: Logout from Azure if: always() run: | az logout echo "Logged out from Azure." - e2e-test: needs: deploy uses: ./.github/workflows/test-automation.yml @@ -199,32 +164,43 @@ jobs: runs-on: ubuntu-latest env: RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }} - AI_REGION: ${{ needs.deploy.outputs.SELECTED_AI_REGION }} steps: - name: Setup Azure CLI run: | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az --version # Verify installation - - name: Login to Azure run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - - name: Get Log Analytics Workspace and OpenAI from Resource Group if: always() id: get_azure_resources run: | set -e - echo "Collecting resource identifiers from RG ${{ env.RESOURCE_GROUP_NAME }} before deletion..." - log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv || true) - if [ -n "$log_analytics_workspace_name" ]; then + echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + + # Run the az monitor log-analytics workspace list command to get the workspace name + log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) + + if [ -z "$log_analytics_workspace_name" ]; then + echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." + else echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV + echo "Log Analytics workspace name: ${log_analytics_workspace_name}" fi - openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv || true) - if [ -n "$openai_resource_name" ]; then + + echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + + # Run the az resource list command to get the OpenAI resource name + openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv) + + if [ -z "$openai_resource_name" ]; then + echo "No OpenAI resource found in resource group ${{ env.RESOURCE_GROUP_NAME }}." + exit 1 + else echo "OPENAI_RESOURCE_NAME=${openai_resource_name}" >> $GITHUB_ENV + echo "OpenAI resource name: ${openai_resource_name}" fi - echo "Snapshot complete." - name: List KeyVaults and Store in Array if: always() @@ -232,12 +208,15 @@ jobs: run: | set -e echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." - keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv || true) + + # Get the list of KeyVaults in the specified resource group + keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv) if [ -z "$keyvaults" ]; then echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." - echo "KEYVAULTS=[]" >> $GITHUB_ENV + echo "KEYVAULTS=[]" >> $GITHUB_ENV # If no KeyVaults found, set an empty array else echo "KeyVaults found: $keyvaults" + # Format the list into an array with proper formatting (no trailing comma) keyvault_array="[" first=true for kv in $keyvaults; do @@ -249,97 +228,141 @@ jobs: fi done keyvault_array="$keyvault_array]" + # Output the formatted array and save it to the environment variable echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV fi - echo "Stored KeyVault array in KEYVAULTS environment variable." - - - name: Delete Resource Group (primary deletion) + - name: Delete Bicep Deployment if: always() run: | - set -e - echo "Initiating deletion of RG ${{ env.RESOURCE_GROUP_NAME }}..." - az group delete --name ${{ env.RESOURCE_GROUP_NAME }} --yes --no-wait || echo "Delete command issued." - - - name: Wait for RG deletion propagation - if: always() - run: | - set -e - echo "Polling for RG deletion..." - max_retries=15 - sleep_interval=20 - for i in $(seq 1 $max_retries); do - exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }} || echo true) - if [ "$exists" = false ]; then - echo "Resource group deleted (confirmed)." - break - fi - echo "RG still exists (attempt $i). Waiting $sleep_interval s..." - sleep $sleep_interval - done - - - name: Purge soft-deleted OpenAI (if any) + set -e + echo "Checking if resource group exists..." + rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) + if [ "$rg_exists" = "true" ]; then + echo "Resource group exist. Cleaning..." + az group delete \ + --name ${{ env.RESOURCE_GROUP_NAME }} \ + --yes \ + --no-wait + echo "Resource group deleted... ${{ env.RESOURCE_GROUP_NAME }}" + else + echo "Resource group does not exists." + fi + - name: Purge log analytics workspace if: always() + id: log_analytics_workspace run: | set -e - if [ -n "${{ env.OPENAI_RESOURCE_NAME }}" ]; then - echo "Attempting purge of soft-deleted OpenAI: ${{ env.OPENAI_RESOURCE_NAME }} in region ${{ env.AI_REGION }}" - az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.AI_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose || echo "OpenAI purge not available yet or failed (non-fatal)." + # Purge Log Analytics Workspace + echo "Purging the Log Analytics Workspace..." + if ! az monitor log-analytics workspace delete --force --resource-group ${{ env.RESOURCE_GROUP_NAME }} --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --yes --verbose; then + echo "Failed to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" else - echo "No OpenAI resource recorded to purge." + echo "Purged the Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" fi + echo "Log analytics workspace resource purging completed successfully" + - - name: Purge soft-deleted KeyVaults (if any) + - name: Wait for resource deletion to complete if: always() run: | - set -e + # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" - stripped=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - IFS=',' read -r -a kvs <<< "$stripped" - for raw in "${kvs[@]}"; do - kv=$(echo "$raw" | sed 's/\"//g' | xargs) - [ -z "$kv" ] && continue - echo "Checking soft-delete state for KeyVault: $kv" - deleted=$(az keyvault list-deleted --query "[?name=='$kv']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo '[]') - if [ "$(echo "$deleted" | jq length)" -gt 0 ]; then - echo "Purging KeyVault $kv ..." - az keyvault purge --name "$kv" --no-wait || echo "Failed to purge $kv (non-fatal)" + # Remove the surrounding square brackets, if they exist + stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + + # Convert the comma-separated string into an array + IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults" + # Append new resources to the array + resources_to_check+=("${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" "${{ env.OPENAI_RESOURCE_NAME }}") + echo "List of resources to check: ${resources_to_check[@]}" + # Maximum number of retries + max_retries=3 + # Retry intervals in seconds (30, 60, 120) + retry_intervals=(30 60 120) + # Retry mechanism to check resources + retries=0 + while true; do + resource_found=false + # Get the list of resources in YAML format again on each retry + resource_list=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --output yaml) + # Iterate through the resources to check + for resource in "${resources_to_check[@]}"; do + echo "Checking resource: $resource" + if echo "$resource_list" | grep -q "name: $resource"; then + echo "Resource '$resource' exists in the resource group." + resource_found=true + else + echo "Resource '$resource' does not exist in the resource group." + fi + done + # If any resource exists, retry + if [ "$resource_found" = true ]; then + retries=$((retries + 1)) + if [ "$retries" -gt "$max_retries" ]; then + echo "Maximum retry attempts reached. Exiting." + break + else + # Wait for the appropriate interval for the current retry + echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..." + sleep ${retry_intervals[$retries-1]} + fi else - echo "KeyVault $kv not soft-deleted; skipping." + echo "No resources found. Exiting." + break fi done - - - name: Purge Log Analytics Workspace (best-effort) + + - name: Purging the Resources if: always() run: | set -e - if [ -n "${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" ]; then - echo "Attempting to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} (best-effort)" - az monitor log-analytics workspace delete --force --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --resource-group ${{ env.RESOURCE_GROUP_NAME }} --yes --verbose || echo "Workspace purge not applicable or already gone." + echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" + # Purge OpenAI Resource + echo "Purging the OpenAI Resource..." + if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/northcentralus/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then + echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" else - echo "No Log Analytics workspace recorded." + echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" fi - + # List of keyvaults + KEYVAULTS="${{ env.KEYVAULTS }}" + # Remove the surrounding square brackets, if they exist + stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + + # Convert the comma-separated string into an array + IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" + echo "Using KeyVaults Array..." + for keyvault_name in "${keyvault_array[@]}"; do + echo "Processing KeyVault: $keyvault_name" + # Check if the KeyVault is soft-deleted + deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}) + # If the KeyVault is found in the soft-deleted state, purge it + if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then + echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..." + # Purge the KeyVault + if az keyvault purge --name "$keyvault_name" --no-wait; then + echo "Successfully purged KeyVault '$keyvault_name'." + else + echo "Failed to purge KeyVault '$keyvault_name'." + fi + else + echo "KeyVault '$keyvault_name' is not soft-deleted. No action taken." + fi + done + echo "Resource purging completed successfully" - name: Send Notification on Failure if: failure() || needs.deploy.result == 'failure' run: | RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the DocGen Deployment Automation process has encountered an issue and has failed to complete successfully.

Build URL: ${RUN_URL}

Please investigate the matter at your earliest convenience.

Best regards,
Your Automation Team

" } EOF ) - curl -X POST "${{ secrets.LOGIC_APP_URL }}" \ -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" - - - name: Final Resource Group Deletion - if: always() - run: | - echo "Final RG delete step retained for idempotency; primary deletion already initiated." - - name: Logout from Azure if: always() run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1c3216e7..99a148bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,7 @@ on: - main - dev - demo + - vee-pipeline-fixes pull_request: types: - opened @@ -16,6 +17,7 @@ on: - main - dev - demo + - vee-pipeline-fixes jobs: # frontend_tests: @@ -87,15 +89,13 @@ jobs: if [ -z "$(find src/tests/backend -type f -name '*_test.py')" ]; then echo "No backend test files found, skipping backend tests." echo "skip_backend_tests=true" >> $GITHUB_ENV - echo "skip_backend_tests=true" >> $GITHUB_OUTPUT else echo "Backend test files found, running tests." echo "skip_backend_tests=false" >> $GITHUB_ENV - echo "skip_backend_tests=false" >> $GITHUB_OUTPUT fi - name: Run Backend Tests with Coverage - if: steps.check_backend_tests.outputs.skip_backend_tests == 'false' + if: env.skip_backend_tests == 'false' run: | cd src pytest --cov=. --cov-report=term-missing --cov-report=xml @@ -103,6 +103,6 @@ jobs: - name: Skip Backend Tests - if: steps.check_backend_tests.outputs.skip_backend_tests == 'true' + if: env.skip_backend_tests == 'true' run: | - echo "Skipping backend tests because no test files were found." + echo "Skipping backend tests because no test files were found." \ No newline at end of file From 53001c5a88a843db11c2be7f428840b4147ca9fa Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Wed, 24 Sep 2025 17:16:05 +0530 Subject: [PATCH 25/40] waf removal --- infra/main.bicep | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index f3c9f956..e9fa93b8 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -2,8 +2,8 @@ metadata name = 'Modernize Your Code Solution Accelerator' metadata description = '''CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. ''' -@description('Set to true if you want to deploy WAF-aligned infrastructure.') -param useWafAlignedArchitecture bool +// @description('Set to true if you want to deploy WAF-aligned infrastructure.') +// param useWafAlignedArchitecture bool @minLength(3) @maxLength(16) @@ -46,11 +46,11 @@ param aiDeploymentsLocation string @description('Optional. AI model deployment token capacity. Defaults to 150K tokens per minute.') param capacity int = 150 -@description('Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false.') -param enableMonitoring bool = useWafAlignedArchitecture? true : false +// @description('Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false.') +// param enableMonitoring bool = useWafAlignedArchitecture? true : false -@description('Optional. Enable scaling for the container apps. Defaults to false.') -param enableScaling bool = useWafAlignedArchitecture? true : false +// @description('Optional. Enable scaling for the container apps. Defaults to false.') +// param enableScaling bool = useWafAlignedArchitecture? true : false @description('Optional. Enable redundancy for applicable resources. Defaults to false.') param enableRedundancy bool = false @@ -58,8 +58,8 @@ param enableRedundancy bool = false @description('Optional. The secondary location for the Cosmos DB account if redundancy is enabled.') param secondaryLocation string? -@description('Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false.') -param enablePrivateNetworking bool = useWafAlignedArchitecture? true : false +// @description('Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false.') +// param enablePrivateNetworking bool = useWafAlignedArchitecture? true : false @description('Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true.') param vmSize string? From 2c184ef37d92d21a850a40ef53d6a96a11a962d7 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Wed, 24 Sep 2025 17:23:48 +0530 Subject: [PATCH 26/40] usewaf set and declartion --- infra/main.bicep | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index e9fa93b8..b00f9267 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -2,8 +2,8 @@ metadata name = 'Modernize Your Code Solution Accelerator' metadata description = '''CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. ''' -// @description('Set to true if you want to deploy WAF-aligned infrastructure.') -// param useWafAlignedArchitecture bool +@description('Set to true if you want to deploy WAF-aligned infrastructure.') +param useWafAlignedArchitecture bool = false @minLength(3) @maxLength(16) @@ -46,11 +46,11 @@ param aiDeploymentsLocation string @description('Optional. AI model deployment token capacity. Defaults to 150K tokens per minute.') param capacity int = 150 -// @description('Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false.') -// param enableMonitoring bool = useWafAlignedArchitecture? true : false +@description('Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false.') +param enableMonitoring bool = useWafAlignedArchitecture? true : false -// @description('Optional. Enable scaling for the container apps. Defaults to false.') -// param enableScaling bool = useWafAlignedArchitecture? true : false +@description('Optional. Enable scaling for the container apps. Defaults to false.') +param enableScaling bool = useWafAlignedArchitecture? true : false @description('Optional. Enable redundancy for applicable resources. Defaults to false.') param enableRedundancy bool = false @@ -58,8 +58,8 @@ param enableRedundancy bool = false @description('Optional. The secondary location for the Cosmos DB account if redundancy is enabled.') param secondaryLocation string? -// @description('Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false.') -// param enablePrivateNetworking bool = useWafAlignedArchitecture? true : false +@description('Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false.') +param enablePrivateNetworking bool = useWafAlignedArchitecture? true : false @description('Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true.') param vmSize string? From 29ff906452046c4e176c0d7fc8789f768db8a1cf Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 25 Sep 2025 10:10:18 +0530 Subject: [PATCH 27/40] pipeline changes add auth --- .github/workflows/deploy.yml | 246 +++++++++++++++++------------------ .github/workflows/test.yml | 1 - 2 files changed, 116 insertions(+), 131 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 50c1b378..0dc8449b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,6 +24,7 @@ jobs: outputs: RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }} WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }} + SELECTED_AI_REGION: ${{ steps.deploy.outputs.selected_ai_region || env.VALID_REGION }} steps: - name: Checkout Code uses: actions/checkout@v3 @@ -32,9 +33,11 @@ jobs: run: | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az --version # Verify installation + - name: Login to Azure run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} + - name: Run Quota Check id: quota-check run: | @@ -52,6 +55,7 @@ jobs: fi exit 1 # Fail the pipeline if any other failure occurs fi + - name: Send Notification on Quota Failure if: env.QUOTA_FAILED == 'true' run: | @@ -62,9 +66,11 @@ jobs: } EOF ) + curl -X POST "${{ secrets.LOGIC_APP_URL }}" \ -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" + - name: Fail Pipeline if Quota Check Fails if: env.QUOTA_FAILED == 'true' run: exit 1 @@ -72,6 +78,8 @@ jobs: - name: Install Bicep CLI run: az bicep install + # reverted: jq explicit installation step removed + - name: Generate Resource Group Name id: generate_rg_name run: | @@ -81,6 +89,7 @@ jobs: UNIQUE_RG_NAME="arg-${ACCL_NAME}-${SHORT_UUID}" echo "RESOURCE_GROUP_NAME=${UNIQUE_RG_NAME}" >> $GITHUB_ENV echo "Generated RESOURCE_GROUP_NAME: ${UNIQUE_RG_NAME}" + - name: Check and Create Resource Group id: check_create_rg run: | @@ -94,6 +103,7 @@ jobs: echo "Resource group already exists." fi echo "RESOURCE_GROUP_NAME=${{ env.RESOURCE_GROUP_NAME }}" >> $GITHUB_OUTPUT + - name: Generate Unique Solution Prefix id: generate_solution_prefix run: | @@ -104,6 +114,7 @@ jobs: UNIQUE_SOLUTION_PREFIX="${COMMON_PART}${UPDATED_TIMESTAMP}" echo "SOLUTION_PREFIX=${UNIQUE_SOLUTION_PREFIX}" >> $GITHUB_ENV echo "Generated SOLUTION_PREFIX: ${UNIQUE_SOLUTION_PREFIX}" + - name: Deploy Bicep Template id: deploy run: | @@ -118,15 +129,24 @@ jobs: else IMAGE_TAG="latest" fi + + EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" # VALID_REGION exported earlier if quota script succeeded + echo "Using AI Deployments Region: $EFFECTIVE_AI_REGION" + echo "selected_ai_region=$EFFECTIVE_AI_REGION" >> $GITHUB_OUTPUT + + echo "Resource Group: ${{ env.RESOURCE_GROUP_NAME }} (Created in northcentralus)" + echo "Solution Prefix: ${{ env.SOLUTION_PREFIX }}" + echo "Image Tag: ${IMAGE_TAG}" + az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="eastus" \ + aiDeploymentsLocation="$EFFECTIVE_AI_REGION" \ capacity=${{ env.GPT_MIN_CAPACITY }} \ - imageVersion="${IMAGE_TAG}"\ + imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" - name: Assign Contributor role to Service Principal if: always() @@ -136,6 +156,8 @@ jobs: --assignee ${{ secrets.AZURE_CLIENT_ID }} \ --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} + + - name: Get Deployment Output and extract Values id: get_output run: | @@ -143,14 +165,27 @@ jobs: echo "Fetching deployment output..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) echo "Extracting deployment output..." - WEBAPP_URL=$(echo $BICEP_OUTPUT | jq -r '.weB_APP_URL.value') + echo "Available output keys:" + echo "$BICEP_OUTPUT" | jq -r 'keys | join(", ")' + # Case-insensitive lookup for WEB_APP_URL because deployment outputs returned inconsistent casing previously + WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r 'to_entries | map(select((.key|ascii_upcase)=="WEB_APP_URL")) | .[0].value.value // empty') + if [ -z "$WEBAPP_URL" ] || [ "$WEBAPP_URL" = "null" ]; then + echo "WEB_APP_URL not found in deployment outputs (case-insensitive scan)" >&2 + echo "Full outputs JSON follows for troubleshooting:" >&2 + echo "$BICEP_OUTPUT" >&2 + exit 1 + fi + echo "Resolved WEBAPP_URL: $WEBAPP_URL" echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Deployment output: $BICEP_OUTPUT" + echo "Deployment outputs (truncated):" + echo "$BICEP_OUTPUT" | head -c 2000 + - name: Logout from Azure if: always() run: | az logout echo "Logged out from Azure." + e2e-test: needs: deploy uses: ./.github/workflows/test-automation.yml @@ -164,43 +199,41 @@ jobs: runs-on: ubuntu-latest env: RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }} + AI_REGION: ${{ needs.deploy.outputs.SELECTED_AI_REGION }} steps: - name: Setup Azure CLI run: | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az --version # Verify installation + - name: Login to Azure run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} + + - name: Assign Contributor role to Service Principal + if: always() + run: | + echo "Assigning Contributor role to SPN for RG: ${{ env.RESOURCE_GROUP_NAME }}" + az role assignment create \ + --assignee ${{ secrets.AZURE_CLIENT_ID }} \ + --role "Contributor" \ + --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} + - name: Get Log Analytics Workspace and OpenAI from Resource Group if: always() id: get_azure_resources run: | set -e - echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." - - # Run the az monitor log-analytics workspace list command to get the workspace name - log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) - - if [ -z "$log_analytics_workspace_name" ]; then - echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." - else + echo "Collecting resource identifiers from RG ${{ env.RESOURCE_GROUP_NAME }} before deletion..." + log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv || true) + if [ -n "$log_analytics_workspace_name" ]; then echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV - echo "Log Analytics workspace name: ${log_analytics_workspace_name}" fi - - echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." - - # Run the az resource list command to get the OpenAI resource name - openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv) - - if [ -z "$openai_resource_name" ]; then - echo "No OpenAI resource found in resource group ${{ env.RESOURCE_GROUP_NAME }}." - exit 1 - else + openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv || true) + if [ -n "$openai_resource_name" ]; then echo "OPENAI_RESOURCE_NAME=${openai_resource_name}" >> $GITHUB_ENV - echo "OpenAI resource name: ${openai_resource_name}" fi + echo "Snapshot complete." - name: List KeyVaults and Store in Array if: always() @@ -208,15 +241,12 @@ jobs: run: | set -e echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." - - # Get the list of KeyVaults in the specified resource group - keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv) + keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv || true) if [ -z "$keyvaults" ]; then echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." - echo "KEYVAULTS=[]" >> $GITHUB_ENV # If no KeyVaults found, set an empty array + echo "KEYVAULTS=[]" >> $GITHUB_ENV else echo "KeyVaults found: $keyvaults" - # Format the list into an array with proper formatting (no trailing comma) keyvault_array="[" first=true for kv in $keyvaults; do @@ -228,141 +258,97 @@ jobs: fi done keyvault_array="$keyvault_array]" - # Output the formatted array and save it to the environment variable echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV fi - - name: Delete Bicep Deployment + echo "Stored KeyVault array in KEYVAULTS environment variable." + + - name: Delete Resource Group (primary deletion) if: always() run: | - set -e - echo "Checking if resource group exists..." - rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) - if [ "$rg_exists" = "true" ]; then - echo "Resource group exist. Cleaning..." - az group delete \ - --name ${{ env.RESOURCE_GROUP_NAME }} \ - --yes \ - --no-wait - echo "Resource group deleted... ${{ env.RESOURCE_GROUP_NAME }}" - else - echo "Resource group does not exists." - fi - - name: Purge log analytics workspace + set -e + echo "Initiating deletion of RG ${{ env.RESOURCE_GROUP_NAME }}..." + az group delete --name ${{ env.RESOURCE_GROUP_NAME }} --yes --no-wait || echo "Delete command issued." + + - name: Wait for RG deletion propagation + if: always() + run: | + set -e + echo "Polling for RG deletion..." + max_retries=15 + sleep_interval=20 + for i in $(seq 1 $max_retries); do + exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }} || echo true) + if [ "$exists" = false ]; then + echo "Resource group deleted (confirmed)." + break + fi + echo "RG still exists (attempt $i). Waiting $sleep_interval s..." + sleep $sleep_interval + done + + - name: Purge soft-deleted OpenAI (if any) if: always() - id: log_analytics_workspace run: | set -e - # Purge Log Analytics Workspace - echo "Purging the Log Analytics Workspace..." - if ! az monitor log-analytics workspace delete --force --resource-group ${{ env.RESOURCE_GROUP_NAME }} --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --yes --verbose; then - echo "Failed to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" + if [ -n "${{ env.OPENAI_RESOURCE_NAME }}" ]; then + echo "Attempting purge of soft-deleted OpenAI: ${{ env.OPENAI_RESOURCE_NAME }} in region ${{ env.AI_REGION }}" + az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.AI_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose || echo "OpenAI purge not available yet or failed (non-fatal)." else - echo "Purged the Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" + echo "No OpenAI resource recorded to purge." fi - echo "Log analytics workspace resource purging completed successfully" - - - name: Wait for resource deletion to complete + - name: Purge soft-deleted KeyVaults (if any) if: always() run: | - # List of keyvaults + set -e KEYVAULTS="${{ env.KEYVAULTS }}" - # Remove the surrounding square brackets, if they exist - stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - - # Convert the comma-separated string into an array - IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults" - # Append new resources to the array - resources_to_check+=("${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" "${{ env.OPENAI_RESOURCE_NAME }}") - echo "List of resources to check: ${resources_to_check[@]}" - # Maximum number of retries - max_retries=3 - # Retry intervals in seconds (30, 60, 120) - retry_intervals=(30 60 120) - # Retry mechanism to check resources - retries=0 - while true; do - resource_found=false - # Get the list of resources in YAML format again on each retry - resource_list=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --output yaml) - # Iterate through the resources to check - for resource in "${resources_to_check[@]}"; do - echo "Checking resource: $resource" - if echo "$resource_list" | grep -q "name: $resource"; then - echo "Resource '$resource' exists in the resource group." - resource_found=true - else - echo "Resource '$resource' does not exist in the resource group." - fi - done - # If any resource exists, retry - if [ "$resource_found" = true ]; then - retries=$((retries + 1)) - if [ "$retries" -gt "$max_retries" ]; then - echo "Maximum retry attempts reached. Exiting." - break - else - # Wait for the appropriate interval for the current retry - echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..." - sleep ${retry_intervals[$retries-1]} - fi + stripped=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + IFS=',' read -r -a kvs <<< "$stripped" + for raw in "${kvs[@]}"; do + kv=$(echo "$raw" | sed 's/\"//g' | xargs) + [ -z "$kv" ] && continue + echo "Checking soft-delete state for KeyVault: $kv" + deleted=$(az keyvault list-deleted --query "[?name=='$kv']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo '[]') + if [ "$(echo "$deleted" | jq length)" -gt 0 ]; then + echo "Purging KeyVault $kv ..." + az keyvault purge --name "$kv" --no-wait || echo "Failed to purge $kv (non-fatal)" else - echo "No resources found. Exiting." - break + echo "KeyVault $kv not soft-deleted; skipping." fi done - - - name: Purging the Resources + + - name: Purge Log Analytics Workspace (best-effort) if: always() run: | set -e - echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" - # Purge OpenAI Resource - echo "Purging the OpenAI Resource..." - if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/northcentralus/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then - echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" + if [ -n "${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" ]; then + echo "Attempting to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} (best-effort)" + az monitor log-analytics workspace delete --force --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --resource-group ${{ env.RESOURCE_GROUP_NAME }} --yes --verbose || echo "Workspace purge not applicable or already gone." else - echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" + echo "No Log Analytics workspace recorded." fi - # List of keyvaults - KEYVAULTS="${{ env.KEYVAULTS }}" - # Remove the surrounding square brackets, if they exist - stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - - # Convert the comma-separated string into an array - IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" - echo "Using KeyVaults Array..." - for keyvault_name in "${keyvault_array[@]}"; do - echo "Processing KeyVault: $keyvault_name" - # Check if the KeyVault is soft-deleted - deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}) - # If the KeyVault is found in the soft-deleted state, purge it - if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then - echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..." - # Purge the KeyVault - if az keyvault purge --name "$keyvault_name" --no-wait; then - echo "Successfully purged KeyVault '$keyvault_name'." - else - echo "Failed to purge KeyVault '$keyvault_name'." - fi - else - echo "KeyVault '$keyvault_name' is not soft-deleted. No action taken." - fi - done - echo "Resource purging completed successfully" + - name: Send Notification on Failure if: failure() || needs.deploy.result == 'failure' run: | RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + EMAIL_BODY=$(cat <Dear Team,

We would like to inform you that the DocGen Deployment Automation process has encountered an issue and has failed to complete successfully.

Build URL: ${RUN_URL}

Please investigate the matter at your earliest convenience.

Best regards,
Your Automation Team

" } EOF ) + curl -X POST "${{ secrets.LOGIC_APP_URL }}" \ -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" + + - name: Final Resource Group Deletion + if: always() + run: | + echo "Final RG delete step retained for idempotency; primary deletion already initiated." + - name: Logout from Azure if: always() run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 99a148bd..f472104d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,6 @@ on: - main - dev - demo - - vee-pipeline-fixes jobs: # frontend_tests: From 5aab02fe2aad02f9ab59bae8d60eec49b814581a Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 25 Sep 2025 10:40:03 +0530 Subject: [PATCH 28/40] add auth and reverted changes and removed waf --- .github/workflows/deploy.yml | 247 +++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 87 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0dc8449b..67c7bb5b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,6 @@ on: - main - dev - demo - - vee-pipeline-fixes schedule: - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT workflow_dispatch: @@ -24,7 +23,6 @@ jobs: outputs: RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }} WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }} - SELECTED_AI_REGION: ${{ steps.deploy.outputs.selected_ai_region || env.VALID_REGION }} steps: - name: Checkout Code uses: actions/checkout@v3 @@ -78,8 +76,6 @@ jobs: - name: Install Bicep CLI run: az bicep install - # reverted: jq explicit installation step removed - - name: Generate Resource Group Name id: generate_rg_name run: | @@ -130,24 +126,21 @@ jobs: IMAGE_TAG="latest" fi - EFFECTIVE_AI_REGION="${VALID_REGION:-eastus}" # VALID_REGION exported earlier if quota script succeeded - echo "Using AI Deployments Region: $EFFECTIVE_AI_REGION" - echo "selected_ai_region=$EFFECTIVE_AI_REGION" >> $GITHUB_OUTPUT - - echo "Resource Group: ${{ env.RESOURCE_GROUP_NAME }} (Created in northcentralus)" - echo "Solution Prefix: ${{ env.SOLUTION_PREFIX }}" - echo "Image Tag: ${IMAGE_TAG}" - + # Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ + current_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%7NZ") + az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="$EFFECTIVE_AI_REGION" \ + aiDeploymentsLocation="eastus" \ capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ - createdBy="Pipeline" + createdBy="Pipeline" \ + tags="{'SecurityControl':'Ignore','Purpose':'Deploying and Cleaning Up Resources for Validation','CreatedDate':'$current_date'}" + - name: Assign Contributor role to Service Principal if: always() run: | @@ -165,20 +158,9 @@ jobs: echo "Fetching deployment output..." BICEP_OUTPUT=$(az deployment group show --name ${{ env.SOLUTION_PREFIX }}-deployment --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "properties.outputs" -o json) echo "Extracting deployment output..." - echo "Available output keys:" - echo "$BICEP_OUTPUT" | jq -r 'keys | join(", ")' - # Case-insensitive lookup for WEB_APP_URL because deployment outputs returned inconsistent casing previously - WEBAPP_URL=$(echo "$BICEP_OUTPUT" | jq -r 'to_entries | map(select((.key|ascii_upcase)=="WEB_APP_URL")) | .[0].value.value // empty') - if [ -z "$WEBAPP_URL" ] || [ "$WEBAPP_URL" = "null" ]; then - echo "WEB_APP_URL not found in deployment outputs (case-insensitive scan)" >&2 - echo "Full outputs JSON follows for troubleshooting:" >&2 - echo "$BICEP_OUTPUT" >&2 - exit 1 - fi - echo "Resolved WEBAPP_URL: $WEBAPP_URL" + WEBAPP_URL=$(echo $BICEP_OUTPUT | jq -r '.weB_APP_URL.value') echo "WEBAPP_URL=$WEBAPP_URL" >> $GITHUB_OUTPUT - echo "Deployment outputs (truncated):" - echo "$BICEP_OUTPUT" | head -c 2000 + echo "Deployment output: $BICEP_OUTPUT" - name: Logout from Azure if: always() @@ -199,7 +181,6 @@ jobs: runs-on: ubuntu-latest env: RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }} - AI_REGION: ${{ needs.deploy.outputs.SELECTED_AI_REGION }} steps: - name: Setup Azure CLI run: | @@ -209,7 +190,7 @@ jobs: - name: Login to Azure run: | az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - + - name: Assign Contributor role to Service Principal if: always() run: | @@ -219,34 +200,56 @@ jobs: --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} + - name: Get Log Analytics Workspace and OpenAI from Resource Group if: always() id: get_azure_resources run: | + set -e - echo "Collecting resource identifiers from RG ${{ env.RESOURCE_GROUP_NAME }} before deletion..." - log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv || true) - if [ -n "$log_analytics_workspace_name" ]; then + echo "Fetching Log Analytics workspace from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + + # Run the az monitor log-analytics workspace list command to get the workspace name + log_analytics_workspace_name=$(az monitor log-analytics workspace list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[0].name" -o tsv) + + if [ -z "$log_analytics_workspace_name" ]; then + echo "No Log Analytics workspace found in resource group ${{ env.RESOURCE_GROUP_NAME }}." + else echo "LOG_ANALYTICS_WORKSPACE_NAME=${log_analytics_workspace_name}" >> $GITHUB_ENV + echo "Log Analytics workspace name: ${log_analytics_workspace_name}" fi - openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv || true) - if [ -n "$openai_resource_name" ]; then + + echo "Fetching OpenAI resource from resource group ${{ env.RESOURCE_GROUP_NAME }}..." + + # Run the az resource list command to get the OpenAI resource name + openai_resource_name=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv) + + if [ -z "$openai_resource_name" ]; then + echo "No OpenAI resource found in resource group ${{ env.RESOURCE_GROUP_NAME }}." + exit 1 + else echo "OPENAI_RESOURCE_NAME=${openai_resource_name}" >> $GITHUB_ENV + echo "OpenAI resource name: ${openai_resource_name}" fi - echo "Snapshot complete." - name: List KeyVaults and Store in Array if: always() id: list_keyvaults run: | + set -e echo "Listing all KeyVaults in the resource group ${RESOURCE_GROUP_NAME}..." - keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv || true) + + # Get the list of KeyVaults in the specified resource group + keyvaults=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --query "[?type=='Microsoft.KeyVault/vaults'].name" -o tsv) + if [ -z "$keyvaults" ]; then echo "No KeyVaults found in resource group ${RESOURCE_GROUP_NAME}." - echo "KEYVAULTS=[]" >> $GITHUB_ENV + echo "KEYVAULTS=[]" >> $GITHUB_ENV # If no KeyVaults found, set an empty array else echo "KeyVaults found: $keyvaults" + + # Format the list into an array with proper formatting (no trailing comma) keyvault_array="[" first=true for kv in $keyvaults; do @@ -258,76 +261,151 @@ jobs: fi done keyvault_array="$keyvault_array]" + + # Output the formatted array and save it to the environment variable echo "KEYVAULTS=$keyvault_array" >> $GITHUB_ENV fi - echo "Stored KeyVault array in KEYVAULTS environment variable." - - name: Delete Resource Group (primary deletion) + - name: Delete Bicep Deployment if: always() run: | - set -e - echo "Initiating deletion of RG ${{ env.RESOURCE_GROUP_NAME }}..." - az group delete --name ${{ env.RESOURCE_GROUP_NAME }} --yes --no-wait || echo "Delete command issued." + set -e + echo "Checking if resource group exists..." + rg_exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }}) + if [ "$rg_exists" = "true" ]; then + echo "Resource group exist. Cleaning..." + az group delete \ + --name ${{ env.RESOURCE_GROUP_NAME }} \ + --yes \ + --no-wait + echo "Resource group deleted... ${{ env.RESOURCE_GROUP_NAME }}" + else + echo "Resource group does not exists." + fi - - name: Wait for RG deletion propagation + - name: Purge log analytics workspace if: always() + id: log_analytics_workspace run: | - set -e - echo "Polling for RG deletion..." - max_retries=15 - sleep_interval=20 - for i in $(seq 1 $max_retries); do - exists=$(az group exists --name ${{ env.RESOURCE_GROUP_NAME }} || echo true) - if [ "$exists" = false ]; then - echo "Resource group deleted (confirmed)." - break - fi - echo "RG still exists (attempt $i). Waiting $sleep_interval s..." - sleep $sleep_interval - done - - name: Purge soft-deleted OpenAI (if any) - if: always() - run: | set -e - if [ -n "${{ env.OPENAI_RESOURCE_NAME }}" ]; then - echo "Attempting purge of soft-deleted OpenAI: ${{ env.OPENAI_RESOURCE_NAME }} in region ${{ env.AI_REGION }}" - az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/${{ env.AI_REGION || 'eastus' }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose || echo "OpenAI purge not available yet or failed (non-fatal)." + # Purge Log Analytics Workspace + echo "Purging the Log Analytics Workspace..." + if ! az monitor log-analytics workspace delete --force --resource-group ${{ env.RESOURCE_GROUP_NAME }} --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --yes --verbose; then + echo "Failed to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" else - echo "No OpenAI resource recorded to purge." + echo "Purged the Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" fi - - name: Purge soft-deleted KeyVaults (if any) + echo "Log analytics workspace resource purging completed successfully" + + + - name: Wait for resource deletion to complete if: always() run: | - set -e + + # List of keyvaults KEYVAULTS="${{ env.KEYVAULTS }}" - stripped=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') - IFS=',' read -r -a kvs <<< "$stripped" - for raw in "${kvs[@]}"; do - kv=$(echo "$raw" | sed 's/\"//g' | xargs) - [ -z "$kv" ] && continue - echo "Checking soft-delete state for KeyVault: $kv" - deleted=$(az keyvault list-deleted --query "[?name=='$kv']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo '[]') - if [ "$(echo "$deleted" | jq length)" -gt 0 ]; then - echo "Purging KeyVault $kv ..." - az keyvault purge --name "$kv" --no-wait || echo "Failed to purge $kv (non-fatal)" + + # Remove the surrounding square brackets, if they exist + stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + + # Convert the comma-separated string into an array + IFS=',' read -r -a resources_to_check <<< "$stripped_keyvaults" + + # Append new resources to the array + resources_to_check+=("${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" "${{ env.OPENAI_RESOURCE_NAME }}") + + echo "List of resources to check: ${resources_to_check[@]}" + + # Maximum number of retries + max_retries=3 + + # Retry intervals in seconds (30, 60, 120) + retry_intervals=(30 60 120) + + # Retry mechanism to check resources + retries=0 + while true; do + resource_found=false + + # Get the list of resources in YAML format again on each retry + resource_list=$(az resource list --resource-group ${{ env.RESOURCE_GROUP_NAME }} --output yaml) + + # Iterate through the resources to check + for resource in "${resources_to_check[@]}"; do + echo "Checking resource: $resource" + if echo "$resource_list" | grep -q "name: $resource"; then + echo "Resource '$resource' exists in the resource group." + resource_found=true + else + echo "Resource '$resource' does not exist in the resource group." + fi + done + + # If any resource exists, retry + if [ "$resource_found" = true ]; then + retries=$((retries + 1)) + if [ "$retries" -gt "$max_retries" ]; then + echo "Maximum retry attempts reached. Exiting." + break + else + # Wait for the appropriate interval for the current retry + echo "Waiting for ${retry_intervals[$retries-1]} seconds before retrying..." + sleep ${retry_intervals[$retries-1]} + fi else - echo "KeyVault $kv not soft-deleted; skipping." + echo "No resources found. Exiting." + break fi done - - - name: Purge Log Analytics Workspace (best-effort) + + - name: Purging the Resources if: always() run: | + set -e - if [ -n "${{ env.LOG_ANALYTICS_WORKSPACE_NAME }}" ]; then - echo "Attempting to purge Log Analytics workspace: ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} (best-effort)" - az monitor log-analytics workspace delete --force --workspace-name ${{ env.LOG_ANALYTICS_WORKSPACE_NAME }} --resource-group ${{ env.RESOURCE_GROUP_NAME }} --yes --verbose || echo "Workspace purge not applicable or already gone." + + echo "Azure OpenAI: ${{ env.OPENAI_RESOURCE_NAME }}" + + # Purge OpenAI Resource + echo "Purging the OpenAI Resource..." + if ! az resource delete --ids /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/providers/Microsoft.CognitiveServices/locations/northcentralus/resourceGroups/${{ env.RESOURCE_GROUP_NAME }}/deletedAccounts/${{ env.OPENAI_RESOURCE_NAME }} --verbose; then + echo "Failed to purge openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" else - echo "No Log Analytics workspace recorded." + echo "Purged the openai resource: ${{ env.OPENAI_RESOURCE_NAME }}" fi + # List of keyvaults + KEYVAULTS="${{ env.KEYVAULTS }}" + + # Remove the surrounding square brackets, if they exist + stripped_keyvaults=$(echo "$KEYVAULTS" | sed 's/\[\|\]//g') + + # Convert the comma-separated string into an array + IFS=',' read -r -a keyvault_array <<< "$stripped_keyvaults" + + echo "Using KeyVaults Array..." + for keyvault_name in "${keyvault_array[@]}"; do + echo "Processing KeyVault: $keyvault_name" + # Check if the KeyVault is soft-deleted + deleted_vaults=$(az keyvault list-deleted --query "[?name=='$keyvault_name']" -o json --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}) + + # If the KeyVault is found in the soft-deleted state, purge it + if [ "$(echo "$deleted_vaults" | jq length)" -gt 0 ]; then + echo "KeyVault '$keyvault_name' is soft-deleted. Proceeding to purge..." + # Purge the KeyVault + if az keyvault purge --name "$keyvault_name" --no-wait; then + echo "Successfully purged KeyVault '$keyvault_name'." + else + echo "Failed to purge KeyVault '$keyvault_name'." + fi + else + echo "KeyVault '$keyvault_name' is not soft-deleted. No action taken." + fi + done + echo "Resource purging completed successfully" + - name: Send Notification on Failure if: failure() || needs.deploy.result == 'failure' run: | @@ -344,11 +422,6 @@ jobs: -H "Content-Type: application/json" \ -d "$EMAIL_BODY" || echo "Failed to send notification" - - name: Final Resource Group Deletion - if: always() - run: | - echo "Final RG delete step retained for idempotency; primary deletion already initiated." - - name: Logout from Azure if: always() run: | From ba917a040b93b4b9ca4d40ff07e56d41af42f54c Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Thu, 25 Sep 2025 13:25:18 +0530 Subject: [PATCH 29/40] template validation version upgrade --- .github/workflows/azure-dev-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index fc98fd7d..199c0cc5 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 # Step 2: Validate the Azure template using microsoft/template-validation-action - name: Validate Azure Template - uses: microsoft/template-validation-action@Latest + uses: microsoft/template-validation-action@v0.3.5 id: validation env: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} From f5039bf5e3fe503937a61620f9a5fa4e0671201c Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Fri, 26 Sep 2025 10:54:54 +0530 Subject: [PATCH 30/40] latest version update --- .github/workflows/azure-dev-validation.yml | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 199c0cc5..aba6eb11 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -1,14 +1,15 @@ -name: Azure Template Validation +name: Azure Template Validation on: -# workflow_dispatch: - push: - branches: - - main - - vee-pipeline-fixes + push: + branches: + - main + - vee-pipeline-fixes + permissions: contents: read id-token: write pull-requests: write + jobs: template_validation_job: runs-on: ubuntu-latest @@ -18,10 +19,14 @@ jobs: # Step 1: Checkout the code from your repository - name: Checkout code uses: actions/checkout@v4 + # Step 2: Validate the Azure template using microsoft/template-validation-action - name: Validate Azure Template - uses: microsoft/template-validation-action@v0.3.5 + uses: microsoft/template-validation-action@Latest id: validation + with: + # disable auto test generation that breaks validation + validateTests: none env: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} @@ -30,6 +35,8 @@ jobs: AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }} AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Step 3: Print the result of the validation - name: Print result - run: cat ${{ steps.validation.outputs.resultFile }} \ No newline at end of file + run: cat ${{ steps.validation.outputs.resultFile }} + \ No newline at end of file From 1f44e159108f2a6b2778d5fbd9c3d9f0ae38ae55 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Fri, 26 Sep 2025 12:22:56 +0530 Subject: [PATCH 31/40] fixing all related changes and removed Waf --- .github/workflows/azure-dev-validation.yml | 25 ++++++++------------ infra/main.bicep | 12 ---------- infra/main.json | 27 ---------------------- 3 files changed, 9 insertions(+), 55 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index aba6eb11..7e558f48 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -1,32 +1,27 @@ -name: Azure Template Validation +name: Azure Template Validation on: - push: - branches: - - main - - vee-pipeline-fixes - +# workflow_dispatch: + push: + branches: + - main + - vee-pipeline-fixes permissions: contents: read id-token: write pull-requests: write - jobs: template_validation_job: runs-on: ubuntu-latest - environment: production + environment: dev name: Template validation steps: # Step 1: Checkout the code from your repository - name: Checkout code uses: actions/checkout@v4 - # Step 2: Validate the Azure template using microsoft/template-validation-action - name: Validate Azure Template - uses: microsoft/template-validation-action@Latest + uses: microsoft/template-validation-action@v0.3.5 id: validation - with: - # disable auto test generation that breaks validation - validateTests: none env: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} @@ -35,8 +30,6 @@ jobs: AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }} AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Step 3: Print the result of the validation - name: Print result - run: cat ${{ steps.validation.outputs.resultFile }} - \ No newline at end of file + run: cat ${{ steps.validation.outputs.resultFile }} \ No newline at end of file diff --git a/infra/main.bicep b/infra/main.bicep index b00f9267..9c280330 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -2,9 +2,6 @@ metadata name = 'Modernize Your Code Solution Accelerator' metadata description = '''CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. ''' -@description('Set to true if you want to deploy WAF-aligned infrastructure.') -param useWafAlignedArchitecture bool = false - @minLength(3) @maxLength(16) @description('Required. A unique application/solution name for all resources in this deployment. This should be 3-16 characters long.') @@ -46,21 +43,12 @@ param aiDeploymentsLocation string @description('Optional. AI model deployment token capacity. Defaults to 150K tokens per minute.') param capacity int = 150 -@description('Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false.') -param enableMonitoring bool = useWafAlignedArchitecture? true : false - -@description('Optional. Enable scaling for the container apps. Defaults to false.') -param enableScaling bool = useWafAlignedArchitecture? true : false - @description('Optional. Enable redundancy for applicable resources. Defaults to false.') param enableRedundancy bool = false @description('Optional. The secondary location for the Cosmos DB account if redundancy is enabled.') param secondaryLocation string? -@description('Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false.') -param enablePrivateNetworking bool = useWafAlignedArchitecture? true : false - @description('Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true.') param vmSize string? diff --git a/infra/main.json b/infra/main.json index 72245536..4b1c7d16 100644 --- a/infra/main.json +++ b/infra/main.json @@ -12,12 +12,6 @@ "description": "CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. \r\n" }, "parameters": { - "useWafAlignedArchitecture": { - "type": "bool", - "metadata": { - "description": "Set to true if you want to deploy WAF-aligned infrastructure." - } - }, "solutionName": { "type": "string", "minLength": 3, @@ -77,20 +71,6 @@ "description": "Optional. AI model deployment token capacity. Defaults to 150K tokens per minute." } }, - "enableMonitoring": { - "type": "bool", - "defaultValue": "[if(parameters('useWafAlignedArchitecture'), true(), false())]", - "metadata": { - "description": "Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false." - } - }, - "enableScaling": { - "type": "bool", - "defaultValue": "[if(parameters('useWafAlignedArchitecture'), true(), false())]", - "metadata": { - "description": "Optional. Enable scaling for the container apps. Defaults to false." - } - }, "enableRedundancy": { "type": "bool", "defaultValue": false, @@ -105,13 +85,6 @@ "description": "Optional. The secondary location for the Cosmos DB account if redundancy is enabled." } }, - "enablePrivateNetworking": { - "type": "bool", - "defaultValue": "[if(parameters('useWafAlignedArchitecture'), true(), false())]", - "metadata": { - "description": "Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false." - } - }, "vmSize": { "type": "string", "nullable": true, From b6b90b749c430520bded9860814c17580c8d8460 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 09:53:51 +0530 Subject: [PATCH 32/40] latest code from dev removed WAF --- infra/main.bicep | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 9c280330..4e73a7bf 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -2,6 +2,7 @@ metadata name = 'Modernize Your Code Solution Accelerator' metadata description = '''CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. ''' + @minLength(3) @maxLength(16) @description('Required. A unique application/solution name for all resources in this deployment. This should be 3-16 characters long.') @@ -43,12 +44,21 @@ param aiDeploymentsLocation string @description('Optional. AI model deployment token capacity. Defaults to 150K tokens per minute.') param capacity int = 150 +@description('Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false.') +param enableMonitoring bool = false + +@description('Optional. Enable scaling for the container apps. Defaults to false.') +param enableScaling bool = false + @description('Optional. Enable redundancy for applicable resources. Defaults to false.') param enableRedundancy bool = false @description('Optional. The secondary location for the Cosmos DB account if redundancy is enabled.') param secondaryLocation string? +@description('Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false.') +param enablePrivateNetworking bool = false + @description('Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true.') param vmSize string? @@ -78,7 +88,7 @@ param llmModel string = 'gpt-4o' @minLength(1) @description('Set the Image tag:') -param imageVersion string = 'latest' +param imageVersion string = 'latest_2025-09-22_455' @minLength(1) @description('Version of the GPT model to deploy:') @@ -122,8 +132,9 @@ var modelDeployment = { var abbrs = loadJsonContent('./abbreviations.json') -@description('Optional created by user name') -param createdBy string = empty(deployer().userPrincipalName) ? '' : split(deployer().userPrincipalName, '@')[0] +@description('Tag, Created by user name') +param createdBy string = contains(deployer(), 'userPrincipalName')? split(deployer().userPrincipalName, '@')[0]: deployer().objectId + // ========== Resource Group Tag ========== // resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = { @@ -132,6 +143,7 @@ resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = { tags: { ...allTags TemplateName: 'Code Modernization' + Type: enablePrivateNetworking ? 'WAF' : 'Non-WAF' CreatedBy: createdBy } } From 47c8fd74c3a9ec013052fe18bc468e8c348202d6 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 10:17:43 +0530 Subject: [PATCH 33/40] adding branch name --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 67c7bb5b..55090b48 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,6 +9,7 @@ on: - main - dev - demo + - vee-pipeline-fixes schedule: - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT workflow_dispatch: From 8693cb2c8e181c2c0dc25606dc68d454ec71b23c Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 10:58:39 +0530 Subject: [PATCH 34/40] add auth for log analytics --- .github/workflows/deploy.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 55090b48..740c7be7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -200,7 +200,12 @@ jobs: --assignee ${{ secrets.AZURE_CLIENT_ID }} \ --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} - + + echo "Assigning Log Analytics Contributor role for Log Analytics workspace access..." + az role assignment create \ + --assignee ${{ secrets.AZURE_CLIENT_ID }} \ + --role "Log Analytics Contributor" \ + --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo "Log Analytics Contributor role assignment failed (may already exist)" - name: Get Log Analytics Workspace and OpenAI from Resource Group if: always() From 2e3ea265e40f6a93a1b095cd4c4c33229b2b4c82 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 11:19:48 +0530 Subject: [PATCH 35/40] auth modify in auth step --- .github/workflows/deploy.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 740c7be7..45e494d8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -201,11 +201,14 @@ jobs: --role "Contributor" \ --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} - echo "Assigning Log Analytics Contributor role for Log Analytics workspace access..." + echo "Assigning Log Analytics Contributor role for Log Analytics workspace access at RG level..." az role assignment create \ --assignee ${{ secrets.AZURE_CLIENT_ID }} \ --role "Log Analytics Contributor" \ - --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }} || echo "Log Analytics Contributor role assignment failed (may already exist)" + --scope /subscriptions/${{ secrets.AZURE_SUBSCRIPTION_ID }}/resourceGroups/${{ env.RESOURCE_GROUP_NAME }} || echo "Log Analytics Contributor role assignment failed (may already exist)" + + echo "Waiting for role assignment propagation..." + sleep 30 - name: Get Log Analytics Workspace and OpenAI from Resource Group if: always() From 547b5ebf5210ff18d1aba9d1337dc2bcbf907b67 Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 16:53:53 +0530 Subject: [PATCH 36/40] dynamic region update --- .github/workflows/deploy.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 45e494d8..5f52885c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -130,13 +130,20 @@ jobs: # Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ current_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%7NZ") + EFFECTIVE_AI_REGION="${{ env.VALID_REGION }}" + if [ -z "$EFFECTIVE_AI_REGION" ]; then + EFFECTIVE_AI_REGION="eastus" + echo "⚠️ WARNING: VALID_REGION not set, falling back to eastus" + fi + echo "✅ Using AI Deployments Region: $EFFECTIVE_AI_REGION" + az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="eastus" \ + aiDeploymentsLocation="$EFFECTIVE_AI_REGION" \ capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" \ From 66b51a256d7ccce1045a21bb3ab535225710906b Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 18:56:27 +0530 Subject: [PATCH 37/40] dev to production fix --- .github/workflows/azure-dev-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 7e558f48..dda61a04 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -12,7 +12,7 @@ permissions: jobs: template_validation_job: runs-on: ubuntu-latest - environment: dev + environment: production name: Template validation steps: # Step 1: Checkout the code from your repository From e11048503ecab16e722bec38612ebff283c68afe Mon Sep 17 00:00:00 2001 From: Venkateswarlu Marthula Date: Tue, 30 Sep 2025 19:18:02 +0530 Subject: [PATCH 38/40] removing local brnach name in all the places --- .github/workflows/azure-dev-validation.yml | 1 - .github/workflows/deploy.yml | 1 - .github/workflows/test.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index dda61a04..2619e449 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - vee-pipeline-fixes permissions: contents: read id-token: write diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5f52885c..b63944eb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,6 @@ on: - main - dev - demo - - vee-pipeline-fixes schedule: - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT workflow_dispatch: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f472104d..f1103949 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,6 @@ on: - main - dev - demo - - vee-pipeline-fixes pull_request: types: - opened From de4800768c9303fc69b2d1ca5a5a7175e112f169 Mon Sep 17 00:00:00 2001 From: Roopan P M Date: Tue, 30 Sep 2025 22:09:25 +0530 Subject: [PATCH 39/40] main json updated --- infra/main.json | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/infra/main.json b/infra/main.json index 8299020e..9e1a8ac8 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.37.4.10188", - "templateHash": "4708808767614470236" + "templateHash": "10857935115773987076" }, "name": "Modernize Your Code Solution Accelerator", - "description": "CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. \n" + "description": "CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. \r\n" }, "parameters": { "solutionName": { @@ -71,6 +71,20 @@ "description": "Optional. AI model deployment token capacity. Defaults to 150K tokens per minute." } }, + "enableMonitoring": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable monitoring for the resources. This will enable Application Insights and Log Analytics. Defaults to false." + } + }, + "enableScaling": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable scaling for the container apps. Defaults to false." + } + }, "enableRedundancy": { "type": "bool", "defaultValue": false, @@ -85,6 +99,13 @@ "description": "Optional. The secondary location for the Cosmos DB account if redundancy is enabled." } }, + "enablePrivateNetworking": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable private networking for the resources. Set to true to enable private networking. Defaults to false." + } + }, "vmSize": { "type": "string", "nullable": true, @@ -165,9 +186,9 @@ }, "createdBy": { "type": "string", - "defaultValue": "[if(empty(deployer().userPrincipalName), '', split(deployer().userPrincipalName, '@')[0])]", + "defaultValue": "[if(contains(deployer(), 'userPrincipalName'), split(deployer().userPrincipalName, '@')[0], deployer().objectId)]", "metadata": { - "description": "Optional created by user name" + "description": "Tag, Created by user name" } } }, @@ -430,7 +451,7 @@ "apiVersion": "2021-04-01", "name": "default", "properties": { - "tags": "[shallowMerge(createArray(variables('allTags'), createObject('TemplateName', 'Code Modernization', 'CreatedBy', parameters('createdBy'))))]" + "tags": "[shallowMerge(createArray(variables('allTags'), createObject('TemplateName', 'Code Modernization', 'Type', if(parameters('enablePrivateNetworking'), 'WAF', 'Non-WAF'), 'CreatedBy', parameters('createdBy'))))]" } }, "avmTelemetry": { From 9bb82c98c4bae539445f309cdadf374ff5a054a2 Mon Sep 17 00:00:00 2001 From: Roopan P M Date: Tue, 30 Sep 2025 22:20:12 +0530 Subject: [PATCH 40/40] updated the code for valida region --- .github/workflows/azure-dev-validation.yml | 2 +- .github/workflows/deploy.yml | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/azure-dev-validation.yml b/.github/workflows/azure-dev-validation.yml index 2619e449..ce740a94 100644 --- a/.github/workflows/azure-dev-validation.yml +++ b/.github/workflows/azure-dev-validation.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 # Step 2: Validate the Azure template using microsoft/template-validation-action - 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 }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b63944eb..fdde396d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -72,6 +72,11 @@ jobs: - name: Fail Pipeline if Quota Check Fails if: env.QUOTA_FAILED == 'true' run: exit 1 + + - name: Set Deployment Region + run: | + echo "Selected Region: $VALID_REGION" + echo "AZURE_LOCATION=$VALID_REGION" >> $GITHUB_ENV - name: Install Bicep CLI run: az bicep install @@ -129,20 +134,13 @@ jobs: # Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ current_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%7NZ") - EFFECTIVE_AI_REGION="${{ env.VALID_REGION }}" - if [ -z "$EFFECTIVE_AI_REGION" ]; then - EFFECTIVE_AI_REGION="eastus" - echo "⚠️ WARNING: VALID_REGION not set, falling back to eastus" - fi - echo "✅ Using AI Deployments Region: $EFFECTIVE_AI_REGION" - az deployment group create \ --name ${{ env.SOLUTION_PREFIX }}-deployment \ --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ --template-file infra/main.bicep \ --parameters \ solutionName="${{ env.SOLUTION_PREFIX }}" \ - aiDeploymentsLocation="$EFFECTIVE_AI_REGION" \ + aiDeploymentsLocation='${{ env.AZURE_LOCATION }}' \ capacity=${{ env.GPT_MIN_CAPACITY }} \ imageVersion="${IMAGE_TAG}" \ createdBy="Pipeline" \