From 8968421a686e0369a10f31f95314c3433da41fb0 Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Mon, 2 Mar 2026 10:56:28 +0530 Subject: [PATCH 1/5] Added Input Validation and Mapping Inputs to Env --- .github/workflows/deploy-linux.yml | 158 ++++++++++++++++- .github/workflows/deploy-orchestrator.yml | 2 +- .github/workflows/job-cleanup-deployment.yml | 93 ++++++++++ .github/workflows/job-deploy-linux.yml | 166 ++++++++++++++--- .github/workflows/job-deploy.yml | 176 +++++++++++++++++-- 5 files changed, 549 insertions(+), 46 deletions(-) diff --git a/.github/workflows/deploy-linux.yml b/.github/workflows/deploy-linux.yml index 64c8ce29..f31ed3fc 100644 --- a/.github/workflows/deploy-linux.yml +++ b/.github/workflows/deploy-linux.yml @@ -69,16 +69,158 @@ on: type: string jobs: + validate-inputs: + name: Validate Input Parameters + runs-on: ubuntu-latest + outputs: + validation_passed: ${{ steps.validate.outputs.passed }} + azure_location: ${{ steps.validate.outputs.azure_location }} + resource_group_name: ${{ steps.validate.outputs.resource_group_name }} + waf_enabled: ${{ steps.validate.outputs.waf_enabled }} + exp: ${{ steps.validate.outputs.exp }} + cleanup_resources: ${{ steps.validate.outputs.cleanup_resources }} + run_e2e_tests: ${{ steps.validate.outputs.run_e2e_tests }} + azure_env_log_analytics_workspace_id: ${{ steps.validate.outputs.azure_env_log_analytics_workspace_id }} + existing_webapp_url: ${{ steps.validate.outputs.existing_webapp_url }} + + steps: + - name: Validate Workflow Input Parameters + id: validate + shell: bash + env: + INPUT_AZURE_LOCATION: ${{ github.event.inputs.azure_location }} + INPUT_RESOURCE_GROUP_NAME: ${{ github.event.inputs.resource_group_name }} + INPUT_WAF_ENABLED: ${{ github.event.inputs.waf_enabled }} + INPUT_EXP: ${{ github.event.inputs.EXP }} + INPUT_CLEANUP_RESOURCES: ${{ github.event.inputs.cleanup_resources }} + INPUT_RUN_E2E_TESTS: ${{ github.event.inputs.run_e2e_tests }} + INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} + INPUT_EXISTING_WEBAPP_URL: ${{ github.event.inputs.existing_webapp_url }} + + run: | + echo "🔍 Validating workflow input parameters..." + VALIDATION_FAILED=false + + # Validate azure_location (Azure region format) + LOCATION="${INPUT_AZURE_LOCATION:-australiaeast}" + + if [[ ! "$LOCATION" =~ ^[a-z0-9]+$ ]]; then + echo "❌ ERROR: azure_location '$LOCATION' is invalid. Must contain only lowercase letters and numbers" + VALIDATION_FAILED=true + else + echo "✅ azure_location: '$LOCATION' is valid" + fi + + # Validate resource_group_name (Azure naming convention, optional) + if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then + if [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then + echo "❌ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period." + VALIDATION_FAILED=true + elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then + echo "❌ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters (length: ${#INPUT_RESOURCE_GROUP_NAME})" + VALIDATION_FAILED=true + else + echo "✅ resource_group_name: '$INPUT_RESOURCE_GROUP_NAME' is valid" + fi + else + echo "✅ resource_group_name: Not provided (will be auto-generated)" + fi + + # Validate waf_enabled (boolean) + WAF_ENABLED="${INPUT_WAF_ENABLED:-false}" + if [[ "$WAF_ENABLED" != "true" && "$WAF_ENABLED" != "false" ]]; then + echo "❌ ERROR: waf_enabled must be 'true' or 'false', got: '$WAF_ENABLED'" + VALIDATION_FAILED=true + else + echo "✅ waf_enabled: '$WAF_ENABLED' is valid" + fi + + # Validate EXP (boolean) + EXP_ENABLED="${INPUT_EXP:-false}" + if [[ "$EXP_ENABLED" != "true" && "$EXP_ENABLED" != "false" ]]; then + echo "❌ ERROR: EXP must be 'true' or 'false', got: '$EXP_ENABLED'" + VALIDATION_FAILED=true + else + echo "✅ EXP: '$EXP_ENABLED' is valid" + fi + + # Validate cleanup_resources (boolean) + CLEANUP_RESOURCES="${INPUT_CLEANUP_RESOURCES:-false}" + if [[ "$CLEANUP_RESOURCES" != "true" && "$CLEANUP_RESOURCES" != "false" ]]; then + echo "❌ ERROR: cleanup_resources must be 'true' or 'false', got: '$CLEANUP_RESOURCES'" + VALIDATION_FAILED=true + else + echo "✅ cleanup_resources: '$CLEANUP_RESOURCES' is valid" + fi + + # Validate run_e2e_tests (specific allowed values) + TEST_OPTION="${INPUT_RUN_E2E_TESTS:-GoldenPath-Testing}" + if [[ "$TEST_OPTION" != "GoldenPath-Testing" && "$TEST_OPTION" != "Smoke-Testing" && "$TEST_OPTION" != "None" ]]; then + echo "❌ ERROR: run_e2e_tests must be one of: GoldenPath-Testing, Smoke-Testing, None, got: '$TEST_OPTION'" + VALIDATION_FAILED=true + else + echo "✅ run_e2e_tests: '$TEST_OPTION' is valid" + fi + + # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional, Azure Resource ID format) + if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then + if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then + echo "❌ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:" + echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}" + echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'" + VALIDATION_FAILED=true + else + echo "✅ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format" + fi + else + echo "✅ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Not provided (optional)" + fi + + # Validate existing_webapp_url (optional, must start with https) + if [[ -n "$INPUT_EXISTING_WEBAPP_URL" ]]; then + if [[ ! "$INPUT_EXISTING_WEBAPP_URL" =~ ^https:// ]]; then + echo "❌ ERROR: existing_webapp_url must start with 'https://', got: '$INPUT_EXISTING_WEBAPP_URL'" + VALIDATION_FAILED=true + else + echo "✅ existing_webapp_url: '$INPUT_EXISTING_WEBAPP_URL' is valid" + fi + else + echo "✅ existing_webapp_url: Not provided (will perform deployment)" + fi + + # Fail workflow if any validation failed + if [[ "$VALIDATION_FAILED" == "true" ]]; then + echo "" + echo "❌ Parameter validation failed. Please correct the errors above and try again." + exit 1 + fi + + echo "" + echo "✅ All input parameters validated successfully!" + + # Output validated values + echo "passed=true" >> $GITHUB_OUTPUT + echo "azure_location=$LOCATION" >> $GITHUB_OUTPUT + echo "resource_group_name=$INPUT_RESOURCE_GROUP_NAME" >> $GITHUB_OUTPUT + echo "waf_enabled=$WAF_ENABLED" >> $GITHUB_OUTPUT + echo "exp=$EXP_ENABLED" >> $GITHUB_OUTPUT + echo "cleanup_resources=$CLEANUP_RESOURCES" >> $GITHUB_OUTPUT + echo "run_e2e_tests=$TEST_OPTION" >> $GITHUB_OUTPUT + echo "azure_env_log_analytics_workspace_id=$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" >> $GITHUB_OUTPUT + echo "existing_webapp_url=$INPUT_EXISTING_WEBAPP_URL" >> $GITHUB_OUTPUT + Run: + needs: validate-inputs + if: needs.validate-inputs.outputs.validation_passed == 'true' uses: ./.github/workflows/deploy-orchestrator.yml with: - azure_location: ${{ github.event.inputs.azure_location || 'australiaeast' }} - resource_group_name: ${{ github.event.inputs.resource_group_name || '' }} - waf_enabled: ${{ github.event.inputs.waf_enabled == 'true' }} - EXP: ${{ github.event.inputs.EXP == 'true' }} - cleanup_resources: ${{ github.event.inputs.cleanup_resources == 'true' }} - run_e2e_tests: ${{ github.event.inputs.run_e2e_tests || 'GoldenPath-Testing' }} - AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID || '' }} - existing_webapp_url: ${{ github.event.inputs.existing_webapp_url || '' }} + azure_location: ${{ needs.validate-inputs.outputs.azure_location || 'australiaeast' }} + resource_group_name: ${{ needs.validate-inputs.outputs.resource_group_name || '' }} + waf_enabled: ${{ needs.validate-inputs.outputs.waf_enabled == 'true' }} + EXP: ${{ needs.validate-inputs.outputs.exp == 'true' }} + cleanup_resources: ${{ needs.validate-inputs.outputs.cleanup_resources == 'true' }} + run_e2e_tests: ${{ needs.validate-inputs.outputs.run_e2e_tests || 'GoldenPath-Testing' }} + AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ needs.validate-inputs.outputs.azure_env_log_analytics_workspace_id || '' }} + existing_webapp_url: ${{ needs.validate-inputs.outputs.existing_webapp_url || '' }} trigger_type: ${{ github.event_name }} secrets: inherit diff --git a/.github/workflows/deploy-orchestrator.yml b/.github/workflows/deploy-orchestrator.yml index b42ca2b8..1b8e1cbd 100644 --- a/.github/workflows/deploy-orchestrator.yml +++ b/.github/workflows/deploy-orchestrator.yml @@ -96,7 +96,7 @@ jobs: secrets: inherit cleanup-deployment: - if: "!cancelled() && needs.deploy.result == 'success' && needs.deploy.outputs.RESOURCE_GROUP_NAME != '' && inputs.existing_webapp_url == '' && (inputs.trigger_type != 'workflow_dispatch' || inputs.cleanup_resources)" + if: "!cancelled() && needs.deploy.outputs.RESOURCE_GROUP_NAME != '' && inputs.existing_webapp_url == '' && (inputs.trigger_type != 'workflow_dispatch' || inputs.cleanup_resources)" needs: [deploy, e2e-test] uses: ./.github/workflows/job-cleanup-deployment.yml with: diff --git a/.github/workflows/job-cleanup-deployment.yml b/.github/workflows/job-cleanup-deployment.yml index 30e518e3..428e4da5 100644 --- a/.github/workflows/job-cleanup-deployment.yml +++ b/.github/workflows/job-cleanup-deployment.yml @@ -48,6 +48,99 @@ jobs: ENV_NAME: ${{ inputs.ENV_NAME }} IMAGE_TAG: ${{ inputs.IMAGE_TAG }} steps: + - name: Validate Workflow Input Parameters + shell: bash + env: + INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }} + INPUT_CLEANUP_RESOURCES: ${{ inputs.cleanup_resources }} + INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }} + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} + INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }} + INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }} + INPUT_ENV_NAME: ${{ inputs.ENV_NAME }} + INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }} + run: | + echo "🔍 Validating workflow input parameters..." + VALIDATION_FAILED=false + + # Validate trigger_type (required - alphanumeric with underscores) + if [[ -z "$INPUT_TRIGGER_TYPE" ]]; then + echo "❌ ERROR: trigger_type is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_TRIGGER_TYPE" =~ ^[a-zA-Z0-9_]+$ ]]; then + echo "❌ ERROR: trigger_type '$INPUT_TRIGGER_TYPE' is invalid. Must contain only alphanumeric characters and underscores" + VALIDATION_FAILED=true + fi + + # Validate cleanup_resources (boolean) + if [[ "$INPUT_CLEANUP_RESOURCES" != "true" && "$INPUT_CLEANUP_RESOURCES" != "false" ]]; then + echo "❌ ERROR: cleanup_resources must be 'true' or 'false', got '$INPUT_CLEANUP_RESOURCES'" + VALIDATION_FAILED=true + fi + + # Validate existing_webapp_url (optional - must start with https if provided) + if [[ -n "$INPUT_EXISTING_WEBAPP_URL" ]]; then + if [[ ! "$INPUT_EXISTING_WEBAPP_URL" =~ ^https:// ]]; then + echo "❌ ERROR: existing_webapp_url must start with 'https://', got '$INPUT_EXISTING_WEBAPP_URL'" + VALIDATION_FAILED=true + fi + fi + + # Validate RESOURCE_GROUP_NAME (required - Azure resource group naming convention) + if [[ -z "$INPUT_RESOURCE_GROUP_NAME" ]]; then + echo "❌ ERROR: RESOURCE_GROUP_NAME is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then + echo "❌ ERROR: RESOURCE_GROUP_NAME is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period." + VALIDATION_FAILED=true + elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then + echo "❌ ERROR: RESOURCE_GROUP_NAME exceeds 90 characters" + VALIDATION_FAILED=true + fi + + # Validate AZURE_LOCATION (required - Azure region format) + if [[ -z "$INPUT_AZURE_LOCATION" ]]; then + echo "❌ ERROR: AZURE_LOCATION is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then + echo "❌ ERROR: AZURE_LOCATION '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers" + VALIDATION_FAILED=true + fi + + # Validate AZURE_ENV_OPENAI_LOCATION (required - Azure region format) + if [[ -z "$INPUT_AZURE_ENV_OPENAI_LOCATION" ]]; then + echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_AZURE_ENV_OPENAI_LOCATION" =~ ^[a-z0-9]+$ ]]; then + echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION '$INPUT_AZURE_ENV_OPENAI_LOCATION' is invalid. Must contain only lowercase letters and numbers" + VALIDATION_FAILED=true + fi + + # Validate ENV_NAME (required - alphanumeric with underscores and hyphens) + if [[ -z "$INPUT_ENV_NAME" ]]; then + echo "❌ ERROR: ENV_NAME is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_ENV_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "❌ ERROR: ENV_NAME '$INPUT_ENV_NAME' is invalid. Must contain only alphanumeric characters, underscores, and hyphens" + VALIDATION_FAILED=true + fi + + # Validate IMAGE_TAG (required - Docker tag pattern) + if [[ -z "$INPUT_IMAGE_TAG" ]]; then + echo "❌ ERROR: IMAGE_TAG is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then + echo "❌ ERROR: IMAGE_TAG '$INPUT_IMAGE_TAG' is invalid. Must be a valid Docker tag (alphanumeric start, up to 128 chars)" + VALIDATION_FAILED=true + fi + + if [[ "$VALIDATION_FAILED" == "true" ]]; then + echo "❌ Input validation failed. Please check the errors above." + exit 1 + fi + + echo "✅ All input parameters validated successfully" + - name: Setup Azure CLI shell: bash run: | diff --git a/.github/workflows/job-deploy-linux.yml b/.github/workflows/job-deploy-linux.yml index b8066ec5..28708215 100644 --- a/.github/workflows/job-deploy-linux.yml +++ b/.github/workflows/job-deploy-linux.yml @@ -41,13 +41,129 @@ jobs: outputs: WEB_APPURL: ${{ steps.get_webapp_url.outputs.WEB_APPURL }} steps: + - name: Validate Workflow Input Parameters + shell: bash + env: + INPUT_ENV_NAME: ${{ inputs.ENV_NAME }} + INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }} + INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }} + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} + INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }} + INPUT_EXP: ${{ inputs.EXP }} + INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }} + INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} + run: | + echo "🔍 Validating workflow input parameters..." + VALIDATION_FAILED=false + + # Validate ENV_NAME (required - alphanumeric) + if [[ -z "$INPUT_ENV_NAME" ]]; then + echo "❌ ERROR: ENV_NAME is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_ENV_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then + echo "❌ ERROR: ENV_NAME '$INPUT_ENV_NAME' is invalid. Must contain only alphanumeric characters, underscores, and hyphens" + VALIDATION_FAILED=true + else + echo "✅ ENV_NAME: '$INPUT_ENV_NAME' is valid" + fi + + # Validate AZURE_ENV_OPENAI_LOCATION (required - Azure region format) + if [[ -z "$INPUT_AZURE_ENV_OPENAI_LOCATION" ]]; then + echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_AZURE_ENV_OPENAI_LOCATION" =~ ^[a-z0-9]+$ ]]; then + echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION '$INPUT_AZURE_ENV_OPENAI_LOCATION' is invalid. Must contain only lowercase letters and numbers (e.g., 'australiaeast', 'westus2')" + VALIDATION_FAILED=true + else + echo "✅ AZURE_ENV_OPENAI_LOCATION: '$INPUT_AZURE_ENV_OPENAI_LOCATION' is valid" + fi + + # Validate AZURE_LOCATION (required - Azure region format) + if [[ -z "$INPUT_AZURE_LOCATION" ]]; then + echo "❌ ERROR: AZURE_LOCATION is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then + echo "❌ ERROR: AZURE_LOCATION '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers (e.g., 'australiaeast', 'westus2')" + VALIDATION_FAILED=true + else + echo "✅ AZURE_LOCATION: '$INPUT_AZURE_LOCATION' is valid" + fi + + # Validate RESOURCE_GROUP_NAME (required - Azure resource group naming convention) + if [[ -z "$INPUT_RESOURCE_GROUP_NAME" ]]; then + echo "❌ ERROR: RESOURCE_GROUP_NAME is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then + echo "❌ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period." + VALIDATION_FAILED=true + elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then + echo "❌ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters" + VALIDATION_FAILED=true + else + echo "✅ RESOURCE_GROUP_NAME: '$INPUT_RESOURCE_GROUP_NAME' is valid" + fi + + # Validate IMAGE_TAG (required - Docker tag pattern) + if [[ -z "$INPUT_IMAGE_TAG" ]]; then + echo "❌ ERROR: IMAGE_TAG is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then + echo "❌ ERROR: IMAGE_TAG '$INPUT_IMAGE_TAG' is invalid. Must start with alphanumeric or underscore, contain only alphanumerics, underscores, periods, hyphens, and be max 128 characters" + VALIDATION_FAILED=true + else + echo "✅ IMAGE_TAG: '$INPUT_IMAGE_TAG' is valid" + fi + + # Validate EXP (required - must be 'true' or 'false') + if [[ -z "$INPUT_EXP" ]]; then + echo "❌ ERROR: EXP is required but was not provided" + VALIDATION_FAILED=true + elif [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then + echo "❌ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'" + VALIDATION_FAILED=true + else + echo "✅ EXP: '$INPUT_EXP' is valid" + fi + + # Validate WAF_ENABLED (must be 'true' or 'false') + if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then + echo "❌ ERROR: WAF_ENABLED must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'" + VALIDATION_FAILED=true + else + echo "✅ WAF_ENABLED: '$INPUT_WAF_ENABLED' is valid" + fi + + # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional - Azure Resource ID format) + if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then + if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then + echo "❌ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:" + echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}" + echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'" + VALIDATION_FAILED=true + else + echo "✅ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format" + fi + fi + + # Fail workflow if any validation failed + if [[ "$VALIDATION_FAILED" == "true" ]]; then + echo "" + echo "❌ Parameter validation failed. Please correct the errors above and try again." + exit 1 + fi + + echo "" + echo "✅ All input parameters validated successfully!" + - name: Checkout Code uses: actions/checkout@v4 - name: Configure Parameters Based on WAF Setting shell: bash + env: + INPUT_WAF_ENABLED: ${{ inputs.WAF_ENABLED }} run: | - if [[ "${{ inputs.WAF_ENABLED }}" == "true" ]]; then + if [[ "$INPUT_WAF_ENABLED" == "true" ]]; then cp infra/main.waf.parameters.json infra/main.parameters.json echo "✅ Successfully copied WAF parameters to main parameters file" else @@ -114,28 +230,36 @@ jobs: - name: Deploy using azd up id: azd_deploy shell: pwsh + env: + INPUT_ENV_NAME: ${{ inputs.ENV_NAME }} + INPUT_AZURE_ENV_OPENAI_LOCATION: ${{ inputs.AZURE_ENV_OPENAI_LOCATION }} + INPUT_AZURE_LOCATION: ${{ inputs.AZURE_LOCATION }} + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} + INPUT_IMAGE_TAG: ${{ inputs.IMAGE_TAG }} + INPUT_EXP: ${{ inputs.EXP }} + INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} run: | # Create azd environment - azd env new ${{ inputs.ENV_NAME }} --no-prompt + azd env new $env:INPUT_ENV_NAME --no-prompt # Set environment variables azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}" - azd env set AZURE_ENV_OPENAI_LOCATION="${{ inputs.AZURE_ENV_OPENAI_LOCATION }}" - azd env set AZURE_LOCATION="${{ inputs.AZURE_LOCATION }}" - azd env set AZURE_RESOURCE_GROUP="${{ inputs.RESOURCE_GROUP_NAME }}" - azd env set AZURE_ENV_IMAGE_TAG="${{ inputs.IMAGE_TAG }}" + azd env set AZURE_ENV_OPENAI_LOCATION="$env:INPUT_AZURE_ENV_OPENAI_LOCATION" + azd env set AZURE_LOCATION="$env:INPUT_AZURE_LOCATION" + azd env set AZURE_RESOURCE_GROUP="$env:INPUT_RESOURCE_GROUP_NAME" + azd env set AZURE_ENV_IMAGE_TAG="$env:INPUT_IMAGE_TAG" # Set AI model capacity parameters azd env set AZURE_ENV_MODEL_CAPACITY="150" azd env set AZURE_ENV_EMBEDDING_MODEL_CAPACITY="200" - if ("${{ inputs.EXP }}" -eq "true") { + if ($env:INPUT_EXP -eq "true") { Write-Host "✅ EXP ENABLED - Setting EXP parameters..." # Set EXP variables dynamically - if ("${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}" -ne "") { - $EXP_LOG_ANALYTICS_ID = "${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}" + if ($env:INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID -ne "") { + $EXP_LOG_ANALYTICS_ID = $env:INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID } else { $EXP_LOG_ANALYTICS_ID = "${{ secrets.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}" } @@ -153,6 +277,8 @@ jobs: - name: Get Deployment Outputs id: get_output + env: + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} run: | # Get outputs from azd azd env get-values --output json > /tmp/azd_output.json @@ -167,7 +293,7 @@ jobs: # Get AKS node resource group if AKS exists if [ -n "$AZURE_AKS_NAME" ]; then - krg_name=$(az aks show --name "$AZURE_AKS_NAME" --resource-group "${{ inputs.RESOURCE_GROUP_NAME }}" --query "nodeResourceGroup" -o tsv || echo "") + krg_name=$(az aks show --name "$AZURE_AKS_NAME" --resource-group "$INPUT_RESOURCE_GROUP_NAME" --query "nodeResourceGroup" -o tsv || echo "") if [ -n "$krg_name" ]; then echo "krg_name=$krg_name" >> $GITHUB_ENV echo "AKS node resource group: $krg_name" @@ -176,16 +302,6 @@ jobs: - name: Run Deployment Script with Input shell: pwsh - run: | - cd Deployment - $input = @" - ${{ secrets.EMAIL }} - yes - "@ - $input | pwsh ./resourcedeployment.ps1 - Write-Host "Resource Group: ${{ inputs.RESOURCE_GROUP_NAME }}" - Write-Host "AKS Cluster Name: ${{ env.AZURE_AKS_NAME }}" - Write-Host "AKS Node Resource Group: ${{ env.krg_name }}" env: # From GitHub secrets (for login) AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} @@ -212,6 +328,16 @@ jobs: AZ_GPT_EMBEDDING_MODEL_ID: ${{ env.AZ_GPT_EMBEDDING_MODEL_ID }} AZURE_APP_CONFIG_ENDPOINT: ${{ env.AZURE_APP_CONFIG_ENDPOINT }} AZURE_APP_CONFIG_NAME: ${{ env.AZURE_APP_CONFIG_NAME }} + run: | + cd Deployment + $input = @" + ${{ secrets.EMAIL }} + yes + "@ + $input | pwsh ./resourcedeployment.ps1 + Write-Host "Resource Group: $env:RESOURCE_GROUP_NAME" + Write-Host "AKS Cluster Name: $env:AZURE_AKS_NAME" + Write-Host "AKS Node Resource Group: $env:krg_name" - name: Retrieve Web App URL id: get_webapp_url diff --git a/.github/workflows/job-deploy.yml b/.github/workflows/job-deploy.yml index 40be0127..99ff9717 100644 --- a/.github/workflows/job-deploy.yml +++ b/.github/workflows/job-deploy.yml @@ -92,27 +92,154 @@ jobs: AZURE_ENV_OPENAI_LOCATION: ${{ steps.set_region.outputs.AZURE_ENV_OPENAI_LOCATION }} IMAGE_TAG: ${{ steps.determine_image_tag.outputs.IMAGE_TAG }} QUOTA_FAILED: ${{ steps.quota_failure_output.outputs.QUOTA_FAILED }} + EXP_ENABLED: ${{ steps.configure_exp.outputs.EXP_ENABLED }} steps: - - name: Validate EXP Configuration + - name: Validate Workflow Input Parameters shell: bash + env: + INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }} + INPUT_AZURE_LOCATION: ${{ inputs.azure_location }} + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }} + INPUT_WAF_ENABLED: ${{ inputs.waf_enabled }} + INPUT_EXP: ${{ inputs.EXP }} + INPUT_CLEANUP_RESOURCES: ${{ inputs.cleanup_resources }} + INPUT_RUN_E2E_TESTS: ${{ inputs.run_e2e_tests }} + INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} + INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }} + run: | + echo "🔍 Validating workflow input parameters..." + VALIDATION_FAILED=false + + # Validate trigger_type (required - alphanumeric with underscores) + if [[ -z "$INPUT_TRIGGER_TYPE" ]]; then + echo "❌ ERROR: trigger_type is required but was not provided" + VALIDATION_FAILED=true + elif [[ ! "$INPUT_TRIGGER_TYPE" =~ ^[a-zA-Z0-9_]+$ ]]; then + echo "❌ ERROR: trigger_type '$INPUT_TRIGGER_TYPE' is invalid. Must contain only alphanumeric characters and underscores" + VALIDATION_FAILED=true + else + echo "✅ trigger_type: '$INPUT_TRIGGER_TYPE' is valid" + fi + + # Validate azure_location (Azure region format) + if [[ -n "$INPUT_AZURE_LOCATION" ]]; then + if [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then + echo "❌ ERROR: azure_location '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers (e.g., 'australiaeast', 'westus2')" + VALIDATION_FAILED=true + else + echo "✅ azure_location: '$INPUT_AZURE_LOCATION' is valid" + fi + fi + + # Validate resource_group_name (Azure resource group naming convention) + if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then + if [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then + echo "❌ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period." + VALIDATION_FAILED=true + elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then + echo "❌ ERROR: resource_group_name '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters" + VALIDATION_FAILED=true + else + echo "✅ resource_group_name: '$INPUT_RESOURCE_GROUP_NAME' is valid" + fi + fi + + # Validate waf_enabled (boolean) + if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then + echo "❌ ERROR: waf_enabled must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'" + VALIDATION_FAILED=true + else + echo "✅ waf_enabled: '$INPUT_WAF_ENABLED' is valid" + fi + + # Validate EXP (boolean) + if [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then + echo "❌ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'" + VALIDATION_FAILED=true + else + echo "✅ EXP: '$INPUT_EXP' is valid" + fi + + # Validate cleanup_resources (boolean) + if [[ "$INPUT_CLEANUP_RESOURCES" != "true" && "$INPUT_CLEANUP_RESOURCES" != "false" ]]; then + echo "❌ ERROR: cleanup_resources must be 'true' or 'false', got: '$INPUT_CLEANUP_RESOURCES'" + VALIDATION_FAILED=true + else + echo "✅ cleanup_resources: '$INPUT_CLEANUP_RESOURCES' is valid" + fi + + # Validate run_e2e_tests (specific allowed values) + if [[ -n "$INPUT_RUN_E2E_TESTS" ]]; then + ALLOWED_VALUES=("None" "GoldenPath-Testing" "Smoke-Testing") + if [[ ! " ${ALLOWED_VALUES[@]} " =~ " ${INPUT_RUN_E2E_TESTS} " ]]; then + echo "❌ ERROR: run_e2e_tests '$INPUT_RUN_E2E_TESTS' is invalid. Allowed values: ${ALLOWED_VALUES[*]}" + VALIDATION_FAILED=true + else + echo "✅ run_e2e_tests: '$INPUT_RUN_E2E_TESTS' is valid" + fi + fi + + # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (Azure Resource ID format) + if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then + if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/[Mm]icrosoft\.[Oo]perational[Ii]nsights/[Ww]orkspaces/[^/]+$ ]]; then + echo "❌ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:" + echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}" + echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'" + VALIDATION_FAILED=true + else + echo "✅ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format" + fi + fi + + # Validate existing_webapp_url (must start with https) + if [[ -n "$INPUT_EXISTING_WEBAPP_URL" ]]; then + if [[ ! "$INPUT_EXISTING_WEBAPP_URL" =~ ^https:// ]]; then + echo "❌ ERROR: existing_webapp_url must start with 'https://', got: '$INPUT_EXISTING_WEBAPP_URL'" + VALIDATION_FAILED=true + else + echo "✅ existing_webapp_url: '$INPUT_EXISTING_WEBAPP_URL' is valid" + fi + fi + + # Fail workflow if any validation failed + if [[ "$VALIDATION_FAILED" == "true" ]]; then + echo "" + echo "❌ Parameter validation failed. Please correct the errors above and try again." + exit 1 + fi + + echo "" + echo "✅ All input parameters validated successfully!" + + - name: Validate and Auto-Configure EXP + id: configure_exp + shell: bash + env: + INPUT_EXP: ${{ inputs.EXP }} + INPUT_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} run: | echo "🔍 Validating EXP configuration..." - if [[ "${{ inputs.EXP }}" == "false" && -n "${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}" ]]; then - echo "âš ī¸ WARNING: EXP is disabled but Log Analytics Workspace ID was provided." - echo "The provided workspace ID will be ignored unless EXP is enabled." + EXP_ENABLED="false" + + if [[ "$INPUT_EXP" == "true" ]]; then + EXP_ENABLED="true" + echo "✅ EXP explicitly enabled by user input" + elif [[ -n "$INPUT_LOG_ANALYTICS_WORKSPACE_ID" ]]; then + echo "🔧 AUTO-ENABLING EXP: Log Analytics Workspace ID was provided but EXP was not explicitly enabled." echo "" - echo "Provided but unused:" - echo " - Azure Log Analytics Workspace ID: '${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}'" + echo "You provided values for:" + echo " - Azure Log Analytics Workspace ID: '$INPUT_LOG_ANALYTICS_WORKSPACE_ID'" echo "" - echo "To use these values, set EXP=true in your workflow dispatch." - elif [[ "${{ inputs.EXP }}" == "true" ]]; then - echo "✅ EXP is enabled" - else - echo "â„šī¸ EXP is disabled" + echo "✅ Automatically enabling EXP to use these values." + EXP_ENABLED="true" fi + echo "EXP_ENABLED=$EXP_ENABLED" >> $GITHUB_ENV + echo "EXP_ENABLED=$EXP_ENABLED" >> $GITHUB_OUTPUT + echo "Final EXP status: $EXP_ENABLED" + - name: Checkout Code uses: actions/checkout@v4 @@ -171,6 +298,9 @@ jobs: - name: Set Deployment Region id: set_region shell: bash + env: + INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }} + INPUT_AZURE_LOCATION: ${{ inputs.azure_location }} run: | if [[ -z "$VALID_REGION" ]]; then echo "❌ ERROR: VALID_REGION is not set. The quota check script (Deployment/checkquota.ps1) must set this variable before this step runs." >&2 @@ -180,8 +310,8 @@ jobs: echo "AZURE_ENV_OPENAI_LOCATION=$VALID_REGION" >> $GITHUB_ENV echo "AZURE_ENV_OPENAI_LOCATION=$VALID_REGION" >> $GITHUB_OUTPUT - if [[ "${{ inputs.trigger_type }}" == "workflow_dispatch" && -n "${{ inputs.azure_location }}" ]]; then - USER_SELECTED_LOCATION="${{ inputs.azure_location }}" + if [[ "$INPUT_TRIGGER_TYPE" == "workflow_dispatch" && -n "$INPUT_AZURE_LOCATION" ]]; then + USER_SELECTED_LOCATION="$INPUT_AZURE_LOCATION" echo "Using user-selected Azure location: $USER_SELECTED_LOCATION" echo "AZURE_LOCATION=$USER_SELECTED_LOCATION" >> $GITHUB_ENV echo "AZURE_LOCATION=$USER_SELECTED_LOCATION" >> $GITHUB_OUTPUT @@ -194,11 +324,13 @@ jobs: - name: Generate Resource Group Name id: generate_rg_name shell: bash + env: + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }} run: | # Check if a resource group name was provided as input - if [[ -n "${{ inputs.resource_group_name }}" ]]; then - echo "Using provided Resource Group name: ${{ inputs.resource_group_name }}" - echo "RESOURCE_GROUP_NAME=${{ inputs.resource_group_name }}" >> $GITHUB_ENV + if [[ -n "$INPUT_RESOURCE_GROUP_NAME" ]]; then + echo "Using provided Resource Group name: $INPUT_RESOURCE_GROUP_NAME" + echo "RESOURCE_GROUP_NAME=$INPUT_RESOURCE_GROUP_NAME" >> $GITHUB_ENV else echo "Generating a unique resource group name..." ACCL_NAME="dkm" # Account name as specified @@ -270,6 +402,16 @@ jobs: - name: Display Workflow Configuration to GitHub Summary shell: bash + env: + INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }} + INPUT_AZURE_LOCATION: ${{ inputs.azure_location }} + INPUT_RESOURCE_GROUP_NAME: ${{ inputs.resource_group_name }} + STEP_EVENT_NAME: ${{ github.event_name }} + STEP_BRANCH_NAME: ${{ env.BRANCH_NAME }} + STEP_WAF_ENABLED: ${{ env.WAF_ENABLED }} + STEP_EXP: ${{ env.EXP }} + STEP_RUN_E2E_TESTS: ${{ env.RUN_E2E_TESTS }} + STEP_CLEANUP_RESOURCES: ${{ env.CLEANUP_RESOURCES }} run: | echo "## 📋 Workflow Configuration Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY @@ -311,7 +453,7 @@ jobs: AZURE_LOCATION: ${{ needs.azure-setup.outputs.AZURE_LOCATION }} RESOURCE_GROUP_NAME: ${{ needs.azure-setup.outputs.RESOURCE_GROUP_NAME }} IMAGE_TAG: ${{ needs.azure-setup.outputs.IMAGE_TAG }} - EXP: ${{ inputs.EXP || 'false' }} + EXP: ${{ needs.azure-setup.outputs.EXP_ENABLED || inputs.EXP || 'false' }} WAF_ENABLED: ${{ inputs.waf_enabled == true && 'true' || 'false' }} AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} secrets: inherit From 2bf44414c2b3dac984402aad75f7b83cb5df5bb9 Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Mon, 2 Mar 2026 11:25:46 +0530 Subject: [PATCH 2/5] Migrated GitHub Actions authentication from client secrets to OIDC --- .github/workflows/CI.yml | 38 +++++++++---------- .github/workflows/deploy-orchestrator.yml | 3 +- .../{deploy-linux.yml => deploy-v2.yml} | 5 +++ .github/workflows/job-cleanup-deployment.yml | 10 +++-- .github/workflows/job-deploy-linux.yml | 25 ++++++++---- .github/workflows/job-deploy.yml | 14 +++---- .github/workflows/test-automation-v2.yml | 13 +++---- .github/workflows/test-automation.yml | 3 -- Deployment/checkquota.ps1 | 20 ++++------ Deployment/resourcedeployment.ps1 | 13 ++++--- 10 files changed, 79 insertions(+), 65 deletions(-) rename .github/workflows/{deploy-linux.yml => deploy-v2.yml} (99%) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3d72c38f..dab5903e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -16,6 +16,7 @@ on: schedule: - cron: "0 10,22 * * *" # Runs at 10:00 AM and 10:00 PM GMT permissions: + id-token: write contents: read actions: read env: @@ -25,6 +26,7 @@ env: jobs: deploy: runs-on: ubuntu-latest + environment: production outputs: RESOURCE_GROUP_NAME: ${{ steps.get_webapp_url.outputs.RESOURCE_GROUP_NAME }} KUBERNETES_RESOURCE_GROUP_NAME: ${{ steps.get_webapp_url.outputs.KUBERNETES_RESOURCE_GROUP_NAME }} @@ -78,6 +80,14 @@ jobs: with: driver: docker + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + - name: Run Quota Check id: quota-check shell: pwsh @@ -105,9 +115,6 @@ jobs: } env: AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} GPT_MIN_CAPACITY: ${{ env.GPT_CAPACITY }} TEXT_EMBEDDING_MIN_CAPACITY: ${{ env.TEXT_EMBEDDING_CAPACITY }} AZURE_REGIONS: "${{ vars.AZURE_REGIONS }}" @@ -158,11 +165,6 @@ jobs: echo "RESOURCE_GROUP_NAME=${UNIQUE_RG_NAME}" >> $GITHUB_ENV echo "Generated RESOURCE_GROUP_NAME: ${UNIQUE_RG_NAME}" - - name: Login to Azure - run: | - 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 }} - - name: Check and Create Resource Group id: check_create_rg run: | @@ -252,11 +254,8 @@ jobs: Write-Host "Resource Group Name is ${{ env.RESOURCE_GROUP_NAME }}" Write-Host "Kubernetes resource group is ${{ env.AZURE_AKS_NAME }}" env: - # From GitHub secrets (for login) + # From GitHub secrets AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} # From deployment outputs step (these come from $GITHUB_ENV) RESOURCE_GROUP_NAME: ${{ env.RESOURCE_GROUP_NAME }} @@ -292,10 +291,9 @@ jobs: if az account show &> /dev/null; then echo "Azure CLI is authenticated." else - echo "Azure CLI is not authenticated. Logging in..." - az login --service-principal --username ${{ secrets.AZURE_CLIENT_ID }} --password ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} + echo "Azure CLI is not authenticated. Please check the OIDC login step." + exit 1 fi - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} # Get the Web App URL and save it to GITHUB_OUTPUT echo "Retrieving Web App URL..." @@ -393,6 +391,7 @@ jobs: if: always() needs: [deploy, e2e-test] runs-on: ubuntu-latest + environment: production env: RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }} KUBERNETES_RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.KUBERNETES_RESOURCE_GROUP_NAME }} @@ -402,10 +401,11 @@ jobs: steps: - name: Login to Azure - shell: bash - run: | - az login --service-principal --username ${{ secrets.AZURE_CLIENT_ID }} --password ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - az account set --subscription "${{ secrets.AZURE_SUBSCRIPTION_ID }}" + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Delete Resource Groups if: env.RESOURCE_GROUP_NAME != '' diff --git a/.github/workflows/deploy-orchestrator.yml b/.github/workflows/deploy-orchestrator.yml index 1b8e1cbd..aa2cdfc2 100644 --- a/.github/workflows/deploy-orchestrator.yml +++ b/.github/workflows/deploy-orchestrator.yml @@ -77,7 +77,8 @@ jobs: secrets: inherit send-notification: - if: "!cancelled()" + # if: "!cancelled()" + if: false # Temporarily disable notification job needs: [deploy, e2e-test] uses: ./.github/workflows/job-send-notification.yml with: diff --git a/.github/workflows/deploy-linux.yml b/.github/workflows/deploy-v2.yml similarity index 99% rename from .github/workflows/deploy-linux.yml rename to .github/workflows/deploy-v2.yml index f31ed3fc..e52e6d7e 100644 --- a/.github/workflows/deploy-linux.yml +++ b/.github/workflows/deploy-v2.yml @@ -68,6 +68,11 @@ on: default: '' type: string +permissions: + id-token: write + contents: read + actions: read + jobs: validate-inputs: name: Validate Input Parameters diff --git a/.github/workflows/job-cleanup-deployment.yml b/.github/workflows/job-cleanup-deployment.yml index 428e4da5..082dffe8 100644 --- a/.github/workflows/job-cleanup-deployment.yml +++ b/.github/workflows/job-cleanup-deployment.yml @@ -40,6 +40,7 @@ on: jobs: cleanup-deployment: runs-on: ubuntu-latest + environment: production continue-on-error: true env: RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} @@ -150,10 +151,11 @@ jobs: az --version - name: Login to Azure - shell: bash - run: | - az login --service-principal --username ${{ secrets.AZURE_CLIENT_ID }} --password ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Delete Resource Group (Optimized Cleanup) id: delete_rg diff --git a/.github/workflows/job-deploy-linux.yml b/.github/workflows/job-deploy-linux.yml index 28708215..ca9488ab 100644 --- a/.github/workflows/job-deploy-linux.yml +++ b/.github/workflows/job-deploy-linux.yml @@ -36,6 +36,7 @@ on: jobs: deploy-linux: runs-on: ubuntu-latest + environment: production env: AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }} outputs: @@ -221,10 +222,15 @@ jobs: uses: Azure/setup-azd@v2 - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Login to azd run: | - az login --service-principal --username ${{ secrets.AZURE_CLIENT_ID }} --password ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} - azd auth login --client-id ${{ secrets.AZURE_CLIENT_ID }} --client-secret ${{ secrets.AZURE_CLIENT_SECRET }} --tenant-id ${{ secrets.AZURE_TENANT_ID }} + azd auth login --client-id ${{ secrets.AZURE_CLIENT_ID }} --federated-credential-provider "github" --tenant-id ${{ secrets.AZURE_TENANT_ID }} - name: Deploy using azd up @@ -299,15 +305,20 @@ jobs: echo "AKS node resource group: $krg_name" fi fi + + - name: Login to Azure to refresh credentials for subsequent steps + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true - name: Run Deployment Script with Input shell: pwsh env: - # From GitHub secrets (for login) + # From GitHub secrets AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} # From workflow inputs and deployment outputs RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }} diff --git a/.github/workflows/job-deploy.yml b/.github/workflows/job-deploy.yml index 99ff9717..d1ff4009 100644 --- a/.github/workflows/job-deploy.yml +++ b/.github/workflows/job-deploy.yml @@ -85,6 +85,7 @@ jobs: name: Azure Setup if: inputs.trigger_type != 'workflow_dispatch' || inputs.existing_webapp_url == '' || inputs.existing_webapp_url == null runs-on: ubuntu-latest + environment: production outputs: RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }} ENV_NAME: ${{ steps.generate_env_name.outputs.ENV_NAME }} @@ -244,10 +245,12 @@ jobs: uses: actions/checkout@v4 - name: Login to Azure - shell: bash - run: | - az login --service-principal --username ${{ secrets.AZURE_CLIENT_ID }} --password ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }} - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true - name: Run Quota Check id: quota-check @@ -275,9 +278,6 @@ jobs: } env: AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} GPT_MIN_CAPACITY: ${{ env.GPT_MIN_CAPACITY }} TEXT_EMBEDDING_MIN_CAPACITY: ${{ env.TEXT_EMBEDDING_MIN_CAPACITY }} AZURE_REGIONS: "${{ vars.AZURE_REGIONS }}" diff --git a/.github/workflows/test-automation-v2.yml b/.github/workflows/test-automation-v2.yml index fe64a1ec..20e8e48a 100644 --- a/.github/workflows/test-automation-v2.yml +++ b/.github/workflows/test-automation-v2.yml @@ -28,6 +28,7 @@ env: jobs: test: runs-on: ubuntu-latest + environment: production outputs: TEST_SUCCESS: ${{ steps.test1.outcome == 'success' || steps.test2.outcome == 'success' || steps.test3.outcome == 'success' }} TEST_REPORT_URL: ${{ steps.upload_report.outputs.artifact-url }} @@ -41,13 +42,11 @@ jobs: python-version: '3.13' - name: Login to Azure - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - run: | - az login --service-principal --username "$AZURE_CLIENT_ID" --password "$AZURE_CLIENT_SECRET" --tenant "$AZURE_TENANT_ID" - az account set --subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }} + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Install dependencies run: | diff --git a/.github/workflows/test-automation.yml b/.github/workflows/test-automation.yml index dd6e5a57..aa0ae606 100644 --- a/.github/workflows/test-automation.yml +++ b/.github/workflows/test-automation.yml @@ -15,9 +15,6 @@ on: env: url: ${{ inputs.DKM_URL }} accelerator_name: "DKM" -permissions: - contents: read - actions: read jobs: test: runs-on: ubuntu-latest diff --git a/Deployment/checkquota.ps1 b/Deployment/checkquota.ps1 index cc5c4822..c16a0b85 100644 --- a/Deployment/checkquota.ps1 +++ b/Deployment/checkquota.ps1 @@ -8,24 +8,20 @@ Write-Output "📍 Processed Regions: $($REGIONS -join ', ')" $SUBSCRIPTION_ID = $env:AZURE_SUBSCRIPTION_ID $GPT_MIN_CAPACITY = $env:GPT_MIN_CAPACITY $TEXT_EMBEDDING_MIN_CAPACITY = $env:TEXT_EMBEDDING_MIN_CAPACITY -$AZURE_CLIENT_ID = $env:AZURE_CLIENT_ID -$AZURE_TENANT_ID = $env:AZURE_TENANT_ID -$AZURE_CLIENT_SECRET = $env:AZURE_CLIENT_SECRET - -# Authenticate using Service Principal -Write-Host "Authentication using Service Principal..." # Ensure Azure PowerShell module is installed and imported Install-Module -Name Az -AllowClobber -Force -Scope CurrentUser Import-Module Az -# Create a PSCredential object for authentication -$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AZURE_CLIENT_ID, (ConvertTo-SecureString $AZURE_CLIENT_SECRET -AsPlainText -Force) - -# Attempt to connect using Service Principal +# Verify existing Azure session (authentication is handled by the caller workflow via OIDC) try { - Connect-AzAccount -ServicePrincipal -TenantId $AZURE_TENANT_ID -Credential $creds + $context = Get-AzContext + if (-not $context) { + Write-Host "❌ Error: No active Azure session found. Ensure the caller workflow authenticates via azure/login@v2 with enable-AzPSSession: true." + exit 1 + } + Write-Host "✅ Using existing Azure session: $($context.Account.Id)" } catch { - Write-Host "❌ Error: Failed to authenticate using Service Principal. $_" + Write-Host "❌ Error: Failed to verify Azure session. $_" exit 1 } diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 9919ba9a..9e4f4dce 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -120,11 +120,14 @@ function LoginAzure([string]$tenantId, [string]$subscriptionID) { } } if ($env:CI -eq "true"){ - az login --service-principal ` - --username $env:AZURE_CLIENT_ID ` - --password $env:AZURE_CLIENT_SECRET ` - --tenant $env:AZURE_TENANT_ID ` - Write-Host "CI deployment mode" + # Authentication is handled by the caller workflow via OIDC (azure/login@v2) + $account = az account show 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Error: No active Azure CLI session found. Ensure the caller workflow authenticates via azure/login@v2." -ForegroundColor Red + failureBanner + exit 1 + } + Write-Host "CI deployment mode - using existing OIDC session" } else{ az login --tenant $tenantId From c823a25a0b626d96dc3979d777778e2a2d6391e1 Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Mon, 2 Mar 2026 11:52:45 +0530 Subject: [PATCH 3/5] Remove paths-ignore entries from CodeQL workflow --- .github/workflows/codeql.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7a7342cb..d56a9fb2 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -8,10 +8,6 @@ on: - 'App/frontend-app/**' - 'App/kernel-memory/**' - '.github/workflows/codeql.yml' - paths-ignore: - - '**/.gitignore' - - '**/Dockerfile' - - '**/.dockerignore' pull_request: branches: [ "main", "dev", "demo" ] paths: @@ -19,10 +15,6 @@ on: - 'App/frontend-app/**' - 'App/kernel-memory/**' - '.github/workflows/codeql.yml' - paths-ignore: - - '**/.gitignore' - - '**/Dockerfile' - - '**/.dockerignore' schedule: - cron: '37 2 * * 5' From e34ac5628dfa30d9e21c74a183d362885fd34201 Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Mon, 2 Mar 2026 11:53:42 +0530 Subject: [PATCH 4/5] renamed to deploy-linux for testing --- .github/workflows/{deploy-v2.yml => deploy-linux.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{deploy-v2.yml => deploy-linux.yml} (100%) diff --git a/.github/workflows/deploy-v2.yml b/.github/workflows/deploy-linux.yml similarity index 100% rename from .github/workflows/deploy-v2.yml rename to .github/workflows/deploy-linux.yml From db3b477dc5354db6dcd4a83c93798ba8c4f70ddd Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Wed, 4 Mar 2026 10:34:53 +0530 Subject: [PATCH 5/5] Minor updates --- .github/workflows/{deploy-linux.yml => deploy-v2.yml} | 0 .github/workflows/job-deploy.yml | 2 -- 2 files changed, 2 deletions(-) rename .github/workflows/{deploy-linux.yml => deploy-v2.yml} (100%) diff --git a/.github/workflows/deploy-linux.yml b/.github/workflows/deploy-v2.yml similarity index 100% rename from .github/workflows/deploy-linux.yml rename to .github/workflows/deploy-v2.yml diff --git a/.github/workflows/job-deploy.yml b/.github/workflows/job-deploy.yml index d1ff4009..1464b151 100644 --- a/.github/workflows/job-deploy.yml +++ b/.github/workflows/job-deploy.yml @@ -417,8 +417,6 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "| Configuration | Value |" >> $GITHUB_STEP_SUMMARY echo "|---------------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| **Trigger Type** | \`${{ github.event_name }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| **Branch** | \`${{ env.BRANCH_NAME }}\` |" >> $GITHUB_STEP_SUMMARY echo "| **WAF Enabled** | ${{ env.WAF_ENABLED == 'true' && '✅ Yes' || '❌ No' }} |" >> $GITHUB_STEP_SUMMARY echo "| **EXP Enabled** | ${{ env.EXP == 'true' && '✅ Yes' || '❌ No' }} |" >> $GITHUB_STEP_SUMMARY echo "| **Run E2E Tests** | \`${{ env.RUN_E2E_TESTS }}\` |" >> $GITHUB_STEP_SUMMARY