diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml
index 7c2fdea6..7e5a6f57 100644
--- a/.github/workflows/azure-dev.yml
+++ b/.github/workflows/azure-dev.yml
@@ -24,7 +24,6 @@ jobs:
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 }}
diff --git a/.github/workflows/build-docker-images.yml b/.github/workflows/build-docker-images.yml
index 5bba326c..6a4b8909 100644
--- a/.github/workflows/build-docker-images.yml
+++ b/.github/workflows/build-docker-images.yml
@@ -2,6 +2,7 @@ name: Build Docker and Optional Push
permissions:
contents: read
actions: read
+ id-token: write
on:
push:
branches:
@@ -50,15 +51,11 @@ jobs:
include:
- app_name: cmsabackend
dockerfile: docker/Backend.Dockerfile
- password_secret: DOCKER_PASSWORD
- app_name: cmsafrontend
dockerfile: docker/Frontend.Dockerfile
- password_secret: DOCKER_PASSWORD
uses: ./.github/workflows/build-docker.yml
with:
registry: cmsacontainerreg.azurecr.io
- username: cmsacontainerreg
- 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' }}
diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml
index 0fd0da72..04161f2c 100644
--- a/.github/workflows/build-docker.yml
+++ b/.github/workflows/build-docker.yml
@@ -6,12 +6,6 @@ on:
registry:
required: true
type: string
- username:
- required: true
- type: string
- password_secret:
- required: true
- type: string
app_name:
required: true
type: string
@@ -21,25 +15,27 @@ on:
push:
required: true
type: boolean
- secrets:
- DOCKER_PASSWORD:
- required: false
jobs:
docker-build:
runs-on: ubuntu-latest
+ environment: production
steps:
- name: Checkout
uses: actions/checkout@v6
- - name: Docker Login
+ - name: Login to Azure
if: ${{ inputs.push }}
- uses: docker/login-action@v3
+ uses: azure/login@v2
with:
- registry: ${{ inputs.registry }}
- username: ${{ inputs.username }}
- password: ${{ secrets[inputs.password_secret] }}
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Login to ACR
+ if: ${{ inputs.push }}
+ run: az acr login --name ${{ inputs.registry }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
diff --git a/.github/workflows/deploy-orchestrator.yml b/.github/workflows/deploy-orchestrator.yml
index 8e326abd..a7730fe7 100644
--- a/.github/workflows/deploy-orchestrator.yml
+++ b/.github/workflows/deploy-orchestrator.yml
@@ -1,9 +1,5 @@
name: Deployment orchestrator
-permissions:
- contents: read
- actions: read
-
on:
workflow_call:
inputs:
diff --git a/.github/workflows/deploy-linux.yml b/.github/workflows/deploy-v2.yml
similarity index 91%
rename from .github/workflows/deploy-linux.yml
rename to .github/workflows/deploy-v2.yml
index 25df9846..2f59ee82 100644
--- a/.github/workflows/deploy-linux.yml
+++ b/.github/workflows/deploy-v2.yml
@@ -1,7 +1,8 @@
-name: Deploy-Test-Cleanup (v2) Linux
+name: Deploy-Test-Cleanup (v2)
permissions:
contents: read
actions: read
+ id-token: write
on:
workflow_run:
workflows: ["Build Docker and Optional Push"]
@@ -13,6 +14,14 @@ on:
- demo
workflow_dispatch:
inputs:
+ runner_os:
+ description: 'Deployment Environment'
+ required: false
+ type: choice
+ options:
+ - 'codespace'
+ - 'Local'
+ default: 'codespace'
azure_location:
description: 'Azure Location For Deployment'
required: false
@@ -85,6 +94,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
validation_passed: ${{ steps.validate.outputs.passed }}
+ runner_os: ${{ steps.validate.outputs.runner_os }}
azure_location: ${{ steps.validate.outputs.azure_location }}
resource_group_name: ${{ steps.validate.outputs.resource_group_name }}
waf_enabled: ${{ steps.validate.outputs.waf_enabled }}
@@ -100,6 +110,7 @@ jobs:
id: validate
shell: bash
env:
+ INPUT_RUNNER_OS: ${{ github.event.inputs.runner_os }}
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 }}
@@ -114,6 +125,20 @@ jobs:
echo "🔍 Validating workflow input parameters..."
VALIDATION_FAILED=false
+ # Validate runner_os (Deployment Environment)
+ RUNNER_INPUT="${INPUT_RUNNER_OS:-codespace}"
+ if [[ "$RUNNER_INPUT" == "codespace" ]]; then
+ RUNNER_OS="ubuntu-latest"
+ echo "✅ runner_os: '$RUNNER_INPUT' → ubuntu-latest"
+ elif [[ "$RUNNER_INPUT" == "Local" ]]; then
+ RUNNER_OS="windows-latest"
+ echo "✅ runner_os: '$RUNNER_INPUT' → windows-latest"
+ else
+ echo "❌ ERROR: runner_os must be 'codespace' or 'Local', got: '$RUNNER_INPUT'"
+ VALIDATION_FAILED=true
+ RUNNER_OS="ubuntu-latest"
+ fi
+
# Validate azure_location (Azure region format)
LOCATION="${INPUT_AZURE_LOCATION:-australiaeast}"
@@ -236,6 +261,7 @@ jobs:
# Output validated values
echo "passed=true" >> $GITHUB_OUTPUT
+ echo "runner_os=$RUNNER_OS" >> $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
@@ -252,7 +278,7 @@ jobs:
if: needs.validate-inputs.outputs.validation_passed == 'true'
uses: ./.github/workflows/deploy-orchestrator.yml
with:
- runner_os: ubuntu-latest
+ runner_os: ${{ needs.validate-inputs.outputs.runner_os || 'ubuntu-latest' }}
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' }}
diff --git a/.github/workflows/deploy-windows.yml b/.github/workflows/deploy-windows.yml
deleted file mode 100644
index 1c2e49ed..00000000
--- a/.github/workflows/deploy-windows.yml
+++ /dev/null
@@ -1,263 +0,0 @@
-name: Deploy-Test-Cleanup (v2) Windows
-permissions:
- contents: read
- actions: read
-on:
- workflow_dispatch:
- inputs:
- azure_location:
- description: 'Azure Location For Deployment'
- required: false
- default: 'australiaeast'
- type: choice
- options:
- - 'australiaeast'
- - 'centralus'
- - 'eastasia'
- - 'eastus2'
- - 'japaneast'
- - 'northeurope'
- - 'southeastasia'
- - 'uksouth'
- resource_group_name:
- description: 'Resource Group Name (Optional)'
- required: false
- default: ''
- type: string
-
- waf_enabled:
- description: 'Enable WAF'
- required: false
- default: false
- type: boolean
- EXP:
- description: 'Enable EXP'
- required: false
- default: false
- type: boolean
- build_docker_image:
- description: 'Build & Push Docker Image (Optional)'
- required: false
- default: false
- type: boolean
-
- cleanup_resources:
- description: 'Cleanup Deployed Resources'
- required: false
- default: false
- type: boolean
-
- run_e2e_tests:
- description: 'Run End-to-End Tests'
- required: false
- default: 'GoldenPath-Testing'
- type: choice
- options:
- - 'GoldenPath-Testing'
- - 'Smoke-Testing'
- - 'None'
-
- AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID:
- description: 'Log Analytics Workspace ID (Optional)'
- required: false
- default: ''
- type: string
- AZURE_EXISTING_AI_PROJECT_RESOURCE_ID:
- description: 'AI Project Resource ID (Optional)'
- required: false
- default: ''
- type: string
- existing_webapp_url:
- description: 'Existing Container WebApp URL (Skips Deployment)'
- required: false
- default: ''
- type: string
-
- # schedule:
- # - cron: '0 5,17 * * *' # Runs at 5:00 AM and 5:00 PM GMT
-
-jobs:
- validate-inputs:
- 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 }}
- build_docker_image: ${{ steps.validate.outputs.build_docker_image }}
- 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 }}
- azure_existing_ai_project_resource_id: ${{ steps.validate.outputs.azure_existing_ai_project_resource_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_BUILD_DOCKER_IMAGE: ${{ github.event.inputs.build_docker_image }}
- 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_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ github.event.inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_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 build_docker_image (boolean)
- BUILD_DOCKER="${INPUT_BUILD_DOCKER_IMAGE:-false}"
- if [[ "$BUILD_DOCKER" != "true" && "$BUILD_DOCKER" != "false" ]]; then
- echo "❌ ERROR: build_docker_image must be 'true' or 'false', got: '$BUILD_DOCKER'"
- VALIDATION_FAILED=true
- else
- echo "✅ build_docker_image: '$BUILD_DOCKER' 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 AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (optional, Azure Resource ID format)
- if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
- if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/[Rr]esource[Gg]roups/[^/]+/providers/([Mm]icrosoft\.[Mm]achine[Ll]earning[Ss]ervices/([Ww]orkspaces|[Pp]rojects)/[^/]+|[Mm]icrosoft\.[Cc]ognitive[Ss]ervices/[Aa]ccounts/[^/]+/[Pp]rojects/[^/]+)$ ]]; then
- echo "❌ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
- echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
- echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
- VALIDATION_FAILED=true
- else
- echo "✅ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
- fi
- else
- echo "✅ AZURE_EXISTING_AI_PROJECT_RESOURCE_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 "build_docker_image=$BUILD_DOCKER" >> $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 "azure_existing_ai_project_resource_id=$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_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:
- runner_os: windows-latest
- 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' }}
- build_docker_image: ${{ needs.validate-inputs.outputs.build_docker_image == '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 || '' }}
- AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: ${{ needs.validate-inputs.outputs.azure_existing_ai_project_resource_id || '' }}
- existing_webapp_url: ${{ needs.validate-inputs.outputs.existing_webapp_url || '' }}
- trigger_type: ${{ github.event_name }}
- secrets: inherit
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index dd8dbed4..cb0ad3d2 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -3,6 +3,7 @@ name: Deploy-Test-Cleanup Pipeline
permissions:
contents: read
actions: read
+ id-token: write
on:
workflow_run:
workflows: ["Build Docker and Optional Push"]
@@ -23,6 +24,7 @@ env:
jobs:
deploy:
runs-on: ubuntu-latest
+ environment: production
outputs:
RESOURCE_GROUP_NAME: ${{ steps.check_create_rg.outputs.RESOURCE_GROUP_NAME }}
WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }}
@@ -31,15 +33,15 @@ jobs:
uses: actions/checkout@v6
- name: Login to Azure
- run: |
- az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_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: Run Quota Check
id: quota-check
env:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_REGIONS: ${{ vars.AZURE_REGIONS }}
GPT_MIN_CAPACITY: ${{ env.GPT_MIN_CAPACITY }}
@@ -182,14 +184,17 @@ jobs:
if: always() && needs.deploy.outputs.RESOURCE_GROUP_NAME != ''
needs: [deploy, e2e-test]
runs-on: ubuntu-latest
+ environment: production
env:
RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
steps:
- 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 }}"
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Assign Contributor role to Service Principal
if: always()
diff --git a/.github/workflows/job-cleanup-deployment.yml b/.github/workflows/job-cleanup-deployment.yml
index e1afa455..0ad4dedb 100644
--- a/.github/workflows/job-cleanup-deployment.yml
+++ b/.github/workflows/job-cleanup-deployment.yml
@@ -1,8 +1,5 @@
name: Cleanup Deployment Job
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
@@ -48,6 +45,7 @@ on:
jobs:
cleanup-deployment:
runs-on: ${{ inputs.runner_os }}
+ environment: production
continue-on-error: true
env:
RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
@@ -58,10 +56,11 @@ jobs:
steps:
- name: Login to Azure
- shell: bash
- 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 }}
+ 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 ff987fe2..a7240859 100644
--- a/.github/workflows/job-deploy-linux.yml
+++ b/.github/workflows/job-deploy-linux.yml
@@ -1,8 +1,5 @@
name: Deploy Steps - Linux
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
@@ -45,6 +42,7 @@ on:
jobs:
deploy-linux:
runs-on: ubuntu-latest
+ environment: production
env:
AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
outputs:
@@ -201,13 +199,18 @@ jobs:
- name: Install azd
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
id: login-azure
shell: bash
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 }}
- 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 and extract values (Linux)
id: get_output_linux
diff --git a/.github/workflows/job-deploy-windows.yml b/.github/workflows/job-deploy-windows.yml
index 6a62b7f4..e36f1b1e 100644
--- a/.github/workflows/job-deploy-windows.yml
+++ b/.github/workflows/job-deploy-windows.yml
@@ -1,9 +1,5 @@
name: Deploy Steps - Windows
-permissions:
- contents: read
- actions: read
-
on:
workflow_call:
inputs:
@@ -46,6 +42,7 @@ on:
jobs:
deploy-windows:
runs-on: windows-latest
+ environment: production
env:
AZURE_DEV_COLLECT_TELEMETRY: ${{ vars.AZURE_DEV_COLLECT_TELEMETRY }}
outputs:
@@ -202,13 +199,18 @@ jobs:
- name: Install azd
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
id: login-azure
shell: bash
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 }}
- 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 and extract values (Windows)
diff --git a/.github/workflows/job-deploy.yml b/.github/workflows/job-deploy.yml
index 6ac7d130..ddaee746 100644
--- a/.github/workflows/job-deploy.yml
+++ b/.github/workflows/job-deploy.yml
@@ -1,8 +1,5 @@
name: Deploy Job
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
@@ -106,6 +103,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 }}
@@ -273,17 +271,15 @@ jobs:
uses: actions/checkout@v6
- name: Login to Azure
- shell: bash
- 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 }}
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run Quota Check
id: quota-check
env:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_REGIONS: ${{ vars.AZURE_REGIONS }}
GPT_MIN_CAPACITY: ${{ env.GPT_MIN_CAPACITY }}
diff --git a/.github/workflows/job-docker-build.yml b/.github/workflows/job-docker-build.yml
index 09348d79..d79c00e4 100644
--- a/.github/workflows/job-docker-build.yml
+++ b/.github/workflows/job-docker-build.yml
@@ -1,7 +1,4 @@
name: Docker Build Job
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
@@ -26,6 +23,7 @@ jobs:
docker-build:
if: inputs.trigger_type == 'workflow_dispatch' && inputs.build_docker_image == true
runs-on: ubuntu-latest
+ environment: production
outputs:
IMAGE_TAG: ${{ steps.generate_docker_tag.outputs.IMAGE_TAG }}
steps:
@@ -49,12 +47,15 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- - name: Log in to Azure Container Registry
- uses: azure/docker-login@v2
+ - name: Login to Azure
+ uses: azure/login@v2
with:
- login-server: ${{ secrets.ACR_TEST_LOGIN_SERVER }}
- username: ${{ secrets.ACR_TEST_USERNAME }}
- password: ${{ secrets.ACR_TEST_PASSWORD }}
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Login to Azure Container Registry
+ run: az acr login --name ${{ secrets.ACR_TEST_LOGIN_SERVER }}
- name: Build and Push Cod Mod backend Docker image
uses: docker/build-push-action@v6
diff --git a/.github/workflows/job-send-notification.yml b/.github/workflows/job-send-notification.yml
index fb503c79..fe8279f1 100644
--- a/.github/workflows/job-send-notification.yml
+++ b/.github/workflows/job-send-notification.yml
@@ -1,7 +1,4 @@
name: Send Notification Job
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
diff --git a/.github/workflows/test-automation-v2.yml b/.github/workflows/test-automation-v2.yml
index 82fe0331..a5c1cea4 100644
--- a/.github/workflows/test-automation-v2.yml
+++ b/.github/workflows/test-automation-v2.yml
@@ -1,8 +1,5 @@
name: Test Automation Code Modernization - v2
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
@@ -31,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 }}
@@ -44,9 +42,11 @@ jobs:
python-version: '3.13'
- 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 }}
+ 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 5d3cccc1..1739cc75 100644
--- a/.github/workflows/test-automation.yml
+++ b/.github/workflows/test-automation.yml
@@ -1,7 +1,4 @@
name: Test Automation Code Modernization
-permissions:
- contents: read
- actions: read
on:
workflow_call:
inputs:
@@ -21,6 +18,7 @@ env:
jobs:
test:
runs-on: ubuntu-latest
+ environment: production
steps:
- name: Checkout repository
uses: actions/checkout@v6
@@ -33,7 +31,9 @@ jobs:
- name: Azure CLI Login
uses: azure/login@v2
with:
- creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
+ 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/README.md b/README.md
index a3576a95..2f848e68 100644
--- a/README.md
+++ b/README.md
@@ -197,6 +197,14 @@ Check out similar solution accelerators
+💡 Want to get familiar with Microsoft's AI Engineering best practices? Check out our playbook to learn more
+
+| Playbook | Description |
+|:---|:---|
+| [AI playbook](https://learn.microsoft.com/en-us/ai/playbook/) | The Artificial Intelligence (AI) Playbook provides enterprise software engineers with solutions, capabilities, and code developed to solve real-world AI problems. |
+
+
+
## Provide feedback
Have questions, find a bug, or want to request a feature? [Submit a new issue](https://github.com/microsoft/Modernize-your-Code-Solution-Accelerator/issues) on this repo and we'll connect.
diff --git a/docs/TroubleShootingSteps.md b/docs/TroubleShootingSteps.md
index f203a98f..95ebe363 100644
--- a/docs/TroubleShootingSteps.md
+++ b/docs/TroubleShootingSteps.md
@@ -58,6 +58,7 @@ Use these as quick reference guides to unblock your deployments.
| Issue/Error Code | Description | Steps to Resolve |
|-----------------|-------------|------------------|
| **InternalSubscriptionIsOverQuotaForSku/
ManagedEnvironmentProvisioningError** | Subscription quota exceeded for the requested SKU | Quotas are applied per resource group, subscriptions, accounts, and other scopes. For example, your subscription might be configured to limit the number of vCPUs for a region. If you attempt to deploy a virtual machine with more vCPUs than the permitted amount, you receive an error that the quota was exceeded.
For PowerShell, use the `Get-AzVMUsage` cmdlet to find virtual machine quotas:
`Get-AzVMUsage -Location "West US"`
Based on available quota you can deploy application otherwise, you can request for more quota |
+| **ServiceQuotaExceeded** | Free tier service quota limit reached for Azure AI Search | This error occurs when you attempt to deploy an Azure AI Search service but have already reached the **free tier quota limit** for your subscription. Each Azure subscription is limited to **one free tier Search service**.
**Example error message:**
`ServiceQuotaExceeded: Operation would exceed 'free' tier service quota. You are using 1 out of 1 'free' tier service quota.`
**Common causes:**