Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions .github/workflows/deploy-orchestrator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,6 @@ jobs:
cleanup_resources: ${{ inputs.cleanup_resources }}
secrets: inherit

send-notification:
if: "!cancelled()"
needs: [docker-build, deploy]
uses: ./.github/workflows/job-send-notification.yml
with:
trigger_type: ${{ inputs.trigger_type }}
waf_enabled: ${{ inputs.waf_enabled }}
EXP: ${{ inputs.EXP }}
existing_webapp_url: ${{ inputs.existing_webapp_url }}
deploy_result: ${{ needs.deploy.result }}
WEB_APPURL: ${{ needs.deploy.outputs.WEB_APPURL || inputs.existing_webapp_url }}
RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
QUOTA_FAILED: ${{ needs.deploy.outputs.QUOTA_FAILED }}
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)"
needs: [docker-build, deploy]
Expand All @@ -117,3 +102,19 @@ jobs:
ENV_NAME: ${{ needs.deploy.outputs.ENV_NAME }}
IMAGE_TAG: ${{ needs.deploy.outputs.IMAGE_TAG }}
secrets: inherit

send-notification:
if: "!cancelled()"
needs: [docker-build, deploy, cleanup-deployment]
uses: ./.github/workflows/job-send-notification.yml
with:
trigger_type: ${{ inputs.trigger_type }}
waf_enabled: ${{ inputs.waf_enabled }}
EXP: ${{ inputs.EXP }}
existing_webapp_url: ${{ inputs.existing_webapp_url }}
deploy_result: ${{ needs.deploy.result }}
cleanup_result: ${{ needs.cleanup-deployment.result }}
WEB_APPURL: ${{ needs.deploy.outputs.WEB_APPURL || inputs.existing_webapp_url }}
RESOURCE_GROUP_NAME: ${{ needs.deploy.outputs.RESOURCE_GROUP_NAME }}
QUOTA_FAILED: ${{ needs.deploy.outputs.QUOTA_FAILED }}
secrets: inherit
206 changes: 58 additions & 148 deletions .github/workflows/job-send-notification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ on:
required: false
default: 'false'
type: string
cleanup_result:
description: 'Cleanup job result (success, failure, skipped)'
required: false
default: 'skipped'
type: string

env:
GPT_MIN_CAPACITY: 100
Expand All @@ -55,199 +60,104 @@ jobs:
env:
accelerator_name: "Container Migration"
steps:
- name: Validate Workflow Input Parameters
- name: Determine Cleanup Status
id: cleanup
shell: bash
env:
INPUT_TRIGGER_TYPE: ${{ inputs.trigger_type }}
INPUT_WAF_ENABLED: ${{ inputs.waf_enabled }}
INPUT_EXP: ${{ inputs.EXP }}
INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
INPUT_DEPLOY_RESULT: ${{ inputs.deploy_result }}
INPUT_WEB_APPURL: ${{ inputs.WEB_APPURL }}
INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
INPUT_QUOTA_FAILED: ${{ inputs.QUOTA_FAILED }}
CLEANUP_RESULT: ${{ inputs.cleanup_result }}
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 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 existing_webapp_url (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
else
echo "✅ existing_webapp_url: '$INPUT_EXISTING_WEBAPP_URL' is valid"
fi
fi

# Validate deploy_result (required, must be specific values)
if [[ -z "$INPUT_DEPLOY_RESULT" ]]; then
echo "❌ ERROR: deploy_result is required but not provided"
VALIDATION_FAILED=true
else
ALLOWED_DEPLOY_RESULTS=("success" "failure" "skipped")
if [[ ! " ${ALLOWED_DEPLOY_RESULTS[@]} " =~ " ${INPUT_DEPLOY_RESULT} " ]]; then
echo "❌ ERROR: deploy_result '$INPUT_DEPLOY_RESULT' is invalid. Allowed values: ${ALLOWED_DEPLOY_RESULTS[*]}"
VALIDATION_FAILED=true
else
echo "✅ deploy_result: '$INPUT_DEPLOY_RESULT' is valid"
fi
fi

# Validate WEB_APPURL (must start with https if provided)
if [[ -n "$INPUT_WEB_APPURL" ]]; then
if [[ ! "$INPUT_WEB_APPURL" =~ ^https:// ]]; then
echo "❌ ERROR: WEB_APPURL must start with 'https://', got: '$INPUT_WEB_APPURL'"
VALIDATION_FAILED=true
else
echo "✅ WEB_APPURL: '$INPUT_WEB_APPURL' is valid"
fi
fi

# Validate RESOURCE_GROUP_NAME (Azure resource group naming convention if provided)
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 QUOTA_FAILED (must be 'true', 'false', or empty string)
if [[ "$INPUT_QUOTA_FAILED" != "true" && "$INPUT_QUOTA_FAILED" != "false" && "$INPUT_QUOTA_FAILED" != "" ]]; then
echo "❌ ERROR: QUOTA_FAILED must be 'true', 'false', or empty string, got: '$INPUT_QUOTA_FAILED'"
VALIDATION_FAILED=true
else
echo "✅ QUOTA_FAILED: '$INPUT_QUOTA_FAILED' is valid"
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!"
case "$CLEANUP_RESULT" in
success) echo "CLEANUP_STATUS=✅ SUCCESS" >> $GITHUB_OUTPUT ;;
failure) echo "CLEANUP_STATUS=❌ FAILED (Needs Manual Cleanup)" >> $GITHUB_OUTPUT ;;
*) echo "CLEANUP_STATUS=⏭️ SKIPPED (Needs Manual Cleanup)" >> $GITHUB_OUTPUT ;;
esac

- name: Determine Configuration Label
id: config
shell: bash
env:
WAF_ENABLED: ${{ env.WAF_ENABLED }}
EXP: ${{ env.EXP }}
run: |
WAF_LABEL=$( [ "$WAF_ENABLED" = "true" ] && echo "WAF" || echo "Non-WAF" )
EXP_LABEL=$( [ "$EXP" = "true" ] && echo "EXP" || echo "Non-EXP" )
echo "CONFIG_LABEL=${WAF_LABEL} + ${EXP_LABEL}" >> $GITHUB_OUTPUT

- name: Send Quota Failure Notification
if: inputs.deploy_result == 'failure' && inputs.QUOTA_FAILED == 'true'
shell: bash
env:
DEPLOY_RESULT: ${{ inputs.deploy_result }}
QUOTA_FAILED: ${{ inputs.QUOTA_FAILED }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_RUN_ID: ${{ github.run_id }}
ACCELERATOR_NAME: ${{ env.accelerator_name }}
LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
run: |
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
EMAIL_BODY=$(cat <<EOF
{
"body": "<p>Dear Team,</p><p>We would like to inform you that the ${{ env.accelerator_name }} deployment has failed due to insufficient quota in the requested regions.</p><p><strong>Issue Details:</strong><br>• Quota check failed for GPT model<br>• Required GPT Capacity: ${{ env.GPT_MIN_CAPACITY }}<br>• Checked Regions: ${{ vars.AZURE_REGIONS }}</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Please resolve the quota issue and retry the deployment.</p><p>Best regards,<br>Your Automation Team</p>",
"subject": "${{ env.accelerator_name }} Pipeline - Failed (Insufficient Quota)"
"body": "<p>Dear Team,</p><p>We would like to inform you that the ${ACCELERATOR_NAME} deployment has failed due to insufficient quota.</p><p><strong>Status Summary:</strong><br><table border='1' cellpadding='5' cellspacing='0'><tr><th>Stage</th><th>Status</th></tr><tr><td>Deployment</td><td>❌ FAILED (Insufficient Quota)</td></tr><tr><td>Cleanup</td><td>${CLEANUP_STATUS}</td></tr></table></p><p><strong>Configuration:</strong> ${CONFIG_LABEL}</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Please resolve the quota issue and retry the deployment.</p><p>Best regards,<br>Your Automation Team</p>",
"subject": "❌[CI/CD-Automation] [${ACCELERATOR_NAME}] Insufficient Quota"
}
EOF
)

curl -X POST "${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}" \
curl -X POST "${LOGICAPP_URL}" \
-H "Content-Type: application/json" \
-d "$EMAIL_BODY" || echo "Failed to send quota failure notification"

- name: Send Deployment Failure Notification
if: inputs.deploy_result == 'failure' && inputs.QUOTA_FAILED != 'true'
shell: bash
env:
DEPLOY_RESULT: ${{ inputs.deploy_result }}
QUOTA_FAILED: ${{ inputs.QUOTA_FAILED }}
RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
ACCELERATOR_NAME: ${{ env.accelerator_name }}
LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
run: |
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
RESOURCE_GROUP="${RESOURCE_GROUP_NAME}"
RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME"

EMAIL_BODY=$(cat <<EOF
{
"body": "<p>Dear Team,</p><p>We would like to inform you that the ${{ env.accelerator_name }} deployment process has encountered an issue and has failed to complete successfully.</p><p><strong>Deployment Details:</strong><br>• Resource Group: ${RESOURCE_GROUP}<br>• WAF Enabled: ${{ env.WAF_ENABLED }}<br>• EXP Enabled: ${{ env.EXP }}</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Please investigate the deployment failure at your earliest convenience.</p><p>Best regards,<br>Your Automation Team</p>",
"subject": "${{ env.accelerator_name }} Pipeline - Failed"
"body": "<p>Dear Team,</p><p>We would like to inform you that the ${ACCELERATOR_NAME} deployment has failed.</p><p><strong>Status Summary:</strong><br><table border='1' cellpadding='5' cellspacing='0'><tr><th>Stage</th><th>Status</th></tr><tr><td>Deployment</td><td>❌ FAILED (Deployment Issue)</td></tr><tr><td>Cleanup</td><td>${CLEANUP_STATUS}</td></tr></table></p><p><strong>Deployment Details:</strong><br>• Resource Group: ${RESOURCE_GROUP}</p><p><strong>Configuration:</strong> ${CONFIG_LABEL}</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Please investigate the deployment failure at your earliest convenience.</p><p>Best regards,<br>Your Automation Team</p>",
"subject": "❌[CI/CD-Automation] [${ACCELERATOR_NAME}] Deployment-Failed"
}
EOF
)

curl -X POST "${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}" \
curl -X POST "${LOGICAPP_URL}" \
-H "Content-Type: application/json" \
-d "$EMAIL_BODY" || echo "Failed to send deployment failure notification"

- name: Send Success Notification
if: inputs.deploy_result == 'success'
shell: bash
env:
DEPLOY_RESULT: ${{ inputs.deploy_result }}
WEB_APPURL: ${{ inputs.WEB_APPURL }}
EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
INPUT_WEB_APPURL: ${{ inputs.WEB_APPURL }}
INPUT_EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
INPUT_RESOURCE_GROUP_NAME: ${{ inputs.RESOURCE_GROUP_NAME }}
ACCELERATOR_NAME: ${{ env.accelerator_name }}
LOGICAPP_URL: ${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_RUN_ID: ${{ github.run_id }}
CONFIG_LABEL: ${{ steps.config.outputs.CONFIG_LABEL }}
CLEANUP_STATUS: ${{ steps.cleanup.outputs.CLEANUP_STATUS }}
run: |
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
WEBAPP_URL="${WEB_APPURL:-$EXISTING_WEBAPP_URL}"
RESOURCE_GROUP="${RESOURCE_GROUP_NAME}"
RUN_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
WEBAPP_URL="${INPUT_WEB_APPURL:-$INPUT_EXISTING_WEBAPP_URL}"
RESOURCE_GROUP="$INPUT_RESOURCE_GROUP_NAME"

EMAIL_BODY=$(cat <<EOF
{
"body": "<p>Dear Team,</p><p>We would like to inform you that the ${{ env.accelerator_name }} deployment has completed successfully.</p><p><strong>Deployment Details:</strong><br>• Resource Group: ${RESOURCE_GROUP}<br>• Web App URL: <a href='${WEBAPP_URL}'>${WEBAPP_URL}</a></p><p><strong>Configuration:</strong><br>• WAF Enabled: ${{ env.WAF_ENABLED }}<br>• EXP Enabled: ${{ env.EXP }}</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Best regards,<br>Your Automation Team</p>",
"subject": "${{ env.accelerator_name }} Pipeline - Deployment Success"
"body": "<p>Dear Team,</p><p>We would like to inform you that the ${ACCELERATOR_NAME} deployment has completed successfully.</p><p><strong>Status Summary:</strong><br><table border='1' cellpadding='5' cellspacing='0'><tr><th>Stage</th><th>Status</th></tr><tr><td>Deployment</td><td>✅ SUCCESS</td></tr><tr><td>Cleanup</td><td>${CLEANUP_STATUS}</td></tr></table></p><p><strong>Deployment Details:</strong><br>• Resource Group: ${RESOURCE_GROUP}<br>• Web App URL: <a href='${WEBAPP_URL}'>${WEBAPP_URL}</a></p><p><strong>Configuration:</strong> ${CONFIG_LABEL}</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Best regards,<br>Your Automation Team</p>",
"subject": "✅[CI/CD-Automation] [${ACCELERATOR_NAME}] Success"
}
EOF
)

curl -X POST "${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}" \
curl -X POST "${LOGICAPP_URL}" \
-H "Content-Type: application/json" \
-d "$EMAIL_BODY" || echo "Failed to send success notification"

- name: Send Existing URL Test Failure Notification
if: inputs.deploy_result == 'skipped' && inputs.existing_webapp_url != ''
shell: bash
env:
DEPLOY_RESULT: ${{ inputs.deploy_result }}
EXISTING_WEBAPP_URL: ${{ inputs.existing_webapp_url }}
run: |
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
EXISTING_URL="${EXISTING_WEBAPP_URL}"

EMAIL_BODY=$(cat <<EOF
{
"body": "<p>Dear Team,</p><p>The ${{ env.accelerator_name }} pipeline executed against the <strong>existing WebApp URL</strong>.</p><p><strong>Details:</strong><br>• Target URL: <a href='${EXISTING_URL}'>${EXISTING_URL}</a><br>• Deployment: Skipped</p><p><strong>Run URL:</strong> <a href='${RUN_URL}'>${RUN_URL}</a></p><p>Best regards,<br>Your Automation Team</p>",
"subject": "${{ env.accelerator_name }} Pipeline - Existing URL Notification"
}
EOF
)

curl -X POST "${{ secrets.EMAILNOTIFICATION_LOGICAPP_URL_TA }}" \
-H "Content-Type: application/json" \
-d "$EMAIL_BODY" || echo "Failed to send existing URL test failure notification"
7 changes: 6 additions & 1 deletion docs/DeploymentGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Ensure you have access to an [Azure subscription](https://azure.microsoft.com/fr
|------------------------------|-----------|-------------|
| **Contributor** | Subscription level | Create and manage Azure resources |
| **User Access Administrator** | Subscription level | Manage user access and role assignments |
| **Role Based Access Control** | Subscription/Resource Group level | Configure RBAC permissions |
| **Role Based Access Control Admin** | Subscription/Resource Group level | Configure RBAC permissions |
| **App Registration Creation** | Azure Active Directory | Create and configure authentication |

**🔍 How to Check Your Permissions:**
Expand Down Expand Up @@ -271,6 +271,11 @@ azd auth login --tenant-id <tenant-id>
> ⚠️ **Critical: Redeployment Warning**
> If you have previously run `azd up` in this folder (i.e., a `.azure` folder exists), you must [create a fresh environment](#creating-a-new-environment) to avoid conflicts and deployment failures.
**NOTE:** If you are running the latest azd version (version 1.23.9), please run the following command.
```bash
azd config set provision.preflight off
```
```shell
azd up
```
Expand Down
Loading