@@ -47,13 +47,147 @@ jobs:
4747 outputs :
4848 WEB_APPURL : ${{ steps.get_output_linux.outputs.WEB_APPURL }}
4949 steps :
50+ - name : Validate Workflow Input Parameters
51+ shell : bash
52+ env :
53+ INPUT_ENV_NAME : ${{ inputs.ENV_NAME }}
54+ INPUT_AZURE_ENV_OPENAI_LOCATION : ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
55+ INPUT_AZURE_LOCATION : ${{ inputs.AZURE_LOCATION }}
56+ INPUT_RESOURCE_GROUP_NAME : ${{ inputs.RESOURCE_GROUP_NAME }}
57+ INPUT_IMAGE_TAG : ${{ inputs.IMAGE_TAG }}
58+ INPUT_BUILD_DOCKER_IMAGE : ${{ inputs.BUILD_DOCKER_IMAGE }}
59+ INPUT_EXP : ${{ inputs.EXP }}
60+ INPUT_WAF_ENABLED : ${{ inputs.WAF_ENABLED }}
61+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID : ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
62+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID : ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
63+ run : |
64+ echo "🔍 Validating workflow input parameters..."
65+ VALIDATION_FAILED=false
66+
67+ # Validate ENV_NAME (required, alphanumeric and hyphens)
68+ if [[ -z "$INPUT_ENV_NAME" ]]; then
69+ echo "❌ ERROR: ENV_NAME is required but not provided"
70+ VALIDATION_FAILED=true
71+ elif [[ ! "$INPUT_ENV_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then
72+ echo "❌ ERROR: ENV_NAME '$INPUT_ENV_NAME' is invalid. Must contain only alphanumerics and hyphens"
73+ VALIDATION_FAILED=true
74+ else
75+ echo "✅ ENV_NAME: '$INPUT_ENV_NAME' is valid"
76+ fi
77+
78+ # Validate AZURE_ENV_OPENAI_LOCATION (required, Azure region format)
79+ if [[ -z "$INPUT_AZURE_ENV_OPENAI_LOCATION" ]]; then
80+ echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION is required but not provided"
81+ VALIDATION_FAILED=true
82+ elif [[ ! "$INPUT_AZURE_ENV_OPENAI_LOCATION" =~ ^[a-z0-9]+$ ]]; then
83+ echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION '$INPUT_AZURE_ENV_OPENAI_LOCATION' is invalid. Must contain only lowercase letters and numbers"
84+ VALIDATION_FAILED=true
85+ else
86+ echo "✅ AZURE_ENV_OPENAI_LOCATION: '$INPUT_AZURE_ENV_OPENAI_LOCATION' is valid"
87+ fi
88+
89+ # Validate AZURE_LOCATION (required, Azure region format)
90+ if [[ -z "$INPUT_AZURE_LOCATION" ]]; then
91+ echo "❌ ERROR: AZURE_LOCATION is required but not provided"
92+ VALIDATION_FAILED=true
93+ elif [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then
94+ echo "❌ ERROR: AZURE_LOCATION '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers"
95+ VALIDATION_FAILED=true
96+ else
97+ echo "✅ AZURE_LOCATION: '$INPUT_AZURE_LOCATION' is valid"
98+ fi
99+
100+ # Validate RESOURCE_GROUP_NAME (required, Azure naming convention)
101+ if [[ -z "$INPUT_RESOURCE_GROUP_NAME" ]]; then
102+ echo "❌ ERROR: RESOURCE_GROUP_NAME is required but not provided"
103+ VALIDATION_FAILED=true
104+ elif [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then
105+ echo "❌ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period."
106+ VALIDATION_FAILED=true
107+ elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then
108+ echo "❌ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters"
109+ VALIDATION_FAILED=true
110+ else
111+ echo "✅ RESOURCE_GROUP_NAME: '$INPUT_RESOURCE_GROUP_NAME' is valid"
112+ fi
113+
114+ # Validate IMAGE_TAG (required, Docker tag pattern)
115+ if [[ -z "$INPUT_IMAGE_TAG" ]]; then
116+ echo "❌ ERROR: IMAGE_TAG is required but not provided"
117+ VALIDATION_FAILED=true
118+ elif [[ ! "$INPUT_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then
119+ echo "❌ ERROR: IMAGE_TAG '$INPUT_IMAGE_TAG' is invalid. Must start with alphanumeric or underscore, max 128 characters"
120+ VALIDATION_FAILED=true
121+ else
122+ echo "✅ IMAGE_TAG: '$INPUT_IMAGE_TAG' is valid"
123+ fi
124+
125+ # Validate BUILD_DOCKER_IMAGE (required, must be 'true' or 'false')
126+ if [[ "$INPUT_BUILD_DOCKER_IMAGE" != "true" && "$INPUT_BUILD_DOCKER_IMAGE" != "false" ]]; then
127+ echo "❌ ERROR: BUILD_DOCKER_IMAGE must be 'true' or 'false', got: '$INPUT_BUILD_DOCKER_IMAGE'"
128+ VALIDATION_FAILED=true
129+ else
130+ echo "✅ BUILD_DOCKER_IMAGE: '$INPUT_BUILD_DOCKER_IMAGE' is valid"
131+ fi
132+
133+ # Validate EXP (required, must be 'true' or 'false')
134+ if [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then
135+ echo "❌ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'"
136+ VALIDATION_FAILED=true
137+ else
138+ echo "✅ EXP: '$INPUT_EXP' is valid"
139+ fi
140+
141+ # Validate WAF_ENABLED (must be 'true' or 'false')
142+ if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then
143+ echo "❌ ERROR: WAF_ENABLED must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'"
144+ VALIDATION_FAILED=true
145+ else
146+ echo "✅ WAF_ENABLED: '$INPUT_WAF_ENABLED' is valid"
147+ fi
148+
149+ # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional, if provided must be valid Resource ID)
150+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
151+ if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/resourceGroups/[^/]+/providers/microsoft\.operationalinsights/workspaces/[^/]+$ ]]; then
152+ echo "❌ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:"
153+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
154+ echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'"
155+ VALIDATION_FAILED=true
156+ else
157+ echo "✅ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format"
158+ fi
159+ fi
160+
161+ # Validate AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (optional, if provided must be valid Resource ID)
162+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
163+ if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/resourceGroups/[^/]+/providers/(Microsoft\.MachineLearningServices/(workspaces|projects)/[^/]+|Microsoft\.CognitiveServices/accounts/[^/]+/projects/[^/]+)$ ]]; then
164+ echo "❌ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
165+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
166+ echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
167+ VALIDATION_FAILED=true
168+ else
169+ echo "✅ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
170+ fi
171+ fi
172+
173+ # Fail workflow if any validation failed
174+ if [[ "$VALIDATION_FAILED" == "true" ]]; then
175+ echo ""
176+ echo "❌ Parameter validation failed. Please correct the errors above and try again."
177+ exit 1
178+ fi
179+
180+ echo ""
181+ echo "✅ All input parameters validated successfully!"
50182 - name : Checkout Code
51183 uses : actions/checkout@v4
52184
53185 - name : Configure Parameters Based on WAF Setting
54186 shell : bash
187+ env :
188+ WAF_ENABLED : ${{ inputs.WAF_ENABLED }}
55189 run : |
56- if [[ "${{ inputs. WAF_ENABLED }} " == "true" ]]; then
190+ if [[ "$WAF_ENABLED" == "true" ]]; then
57191 cp infra/main.waf.parameters.json infra/main.parameters.json
58192 echo "✅ Successfully copied WAF parameters to main parameters file"
59193 else
@@ -83,45 +217,55 @@ jobs:
83217 - name : Deploy using azd up and extract values (Linux)
84218 id : get_output_linux
85219 shell : bash
220+ env :
221+ ENV_NAME : ${{ inputs.ENV_NAME }}
222+ AZURE_ENV_OPENAI_LOCATION : ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
223+ AZURE_LOCATION : ${{ inputs.AZURE_LOCATION }}
224+ RESOURCE_GROUP_NAME : ${{ inputs.RESOURCE_GROUP_NAME }}
225+ IMAGE_TAG : ${{ inputs.IMAGE_TAG }}
226+ BUILD_DOCKER_IMAGE : ${{ inputs.BUILD_DOCKER_IMAGE }}
227+ EXP : ${{ inputs.EXP }}
228+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID : ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
229+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID : ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
86230 run : |
87231 set -e
88232
89233 echo "Creating environment..."
90- azd env new ${{ inputs. ENV_NAME }} --no-prompt
91- echo "Environment created: ${{ inputs. ENV_NAME }} "
234+ azd env new "$ ENV_NAME" --no-prompt
235+ echo "Environment created: $ENV_NAME"
92236
93237 echo "Setting default subscription..."
94- azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
238+ azd config set defaults.subscription " ${{ secrets.AZURE_SUBSCRIPTION_ID }}"
95239
96240 # Set additional parameters
97241 azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
98- azd env set AZURE_ENV_OPENAI_LOCATION="${{ inputs. AZURE_ENV_OPENAI_LOCATION }} "
99- azd env set AZURE_LOCATION="${{ inputs. AZURE_LOCATION }} "
100- azd env set AZURE_RESOURCE_GROUP="${{ inputs. RESOURCE_GROUP_NAME }} "
101- azd env set AZURE_ENV_IMAGETAG="${{ inputs. IMAGE_TAG }} "
242+ azd env set AZURE_ENV_OPENAI_LOCATION="$AZURE_ENV_OPENAI_LOCATION"
243+ azd env set AZURE_LOCATION="$AZURE_LOCATION"
244+ azd env set AZURE_RESOURCE_GROUP="$RESOURCE_GROUP_NAME"
245+ azd env set AZURE_ENV_IMAGETAG="$IMAGE_TAG"
102246
103247 # Set ACR name only when building Docker image
104- if [[ "${{ inputs. BUILD_DOCKER_IMAGE }} " == "true" ]]; then
248+ if [[ "$BUILD_DOCKER_IMAGE" == "true" ]]; then
105249 # Extract ACR name from login server and set as environment variable
106- ACR_NAME=$(echo "${{ secrets.ACR_TEST_USERNAME }}")
250+ ACR_NAME="${{ secrets.ACR_TEST_USERNAME }}"
107251 azd env set AZURE_ENV_ACR_NAME="$ACR_NAME"
108252 echo "Set ACR name to: $ACR_NAME"
109253 else
110254 echo "Skipping ACR name configuration (using existing image)"
111255 fi
112256
113- if [[ "${{ inputs. EXP }} " == "true" ]]; then
257+ if [[ "$EXP" == "true" ]]; then
114258 echo "✅ EXP ENABLED - Setting EXP parameters..."
115259
116260 # Set EXP variables dynamically
117- if [[ -n "${{ github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} " ]]; then
118- EXP_LOG_ANALYTICS_ID="${{ github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} "
261+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID " ]]; then
262+ EXP_LOG_ANALYTICS_ID="$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID "
119263 else
120264 EXP_LOG_ANALYTICS_ID="${{ secrets.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}"
121265 fi
122266
123- if [[ -n "${{ github.event.inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} " ]]; then
124- EXP_AI_PROJECT_ID="${{ github.event.inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} "
267+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID " ]]; then
268+ EXP_AI_PROJECT_ID="$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID "
125269 else
126270 EXP_AI_PROJECT_ID="${{ secrets.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}"
127271 fi
@@ -132,7 +276,7 @@ jobs:
132276 azd env set AZURE_EXISTING_AI_PROJECT_RESOURCE_ID="$EXP_AI_PROJECT_ID"
133277 else
134278 echo "❌ EXP DISABLED - Skipping EXP parameters"
135- if [[ -n "${{ github.event.inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} " ]] || [[ -n "${{ github.event.inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} " ]]; then
279+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID " ]] || [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID " ]]; then
136280 echo "⚠️ Warning: EXP parameter values provided but EXP is disabled. These values will be ignored."
137281 fi
138282 fi
@@ -181,9 +325,10 @@ jobs:
181325 id : post_deploy
182326 env :
183327 AZURE_SUBSCRIPTION_ID : ${{ secrets.AZURE_SUBSCRIPTION_ID }}
328+ AZURE_CLIENT_ID : ${{ secrets.AZURE_CLIENT_ID }}
184329 run : |
185330 set -e
186- az account set --subscription "${{ secrets. AZURE_SUBSCRIPTION_ID }} "
331+ az account set --subscription "$AZURE_SUBSCRIPTION_ID"
187332
188333 echo "Running post-deployment script..."
189334
@@ -194,26 +339,54 @@ jobs:
194339 "$AZURE_COSMOSDB_ACCOUNT" \
195340 "$RESOURCE_GROUP_NAME" \
196341 "$AI_SEARCH_SERVICE_NAME" \
197- "${{ secrets. AZURE_CLIENT_ID }} " \
342+ "$AZURE_CLIENT_ID" \
198343 "$AI_FOUNDRY_RESOURCE_ID"
199344
200345 - name : Generate Deploy Job Summary
201346 if : always()
347+ env :
348+ RESOURCE_GROUP_NAME : ${{ inputs.RESOURCE_GROUP_NAME }}
349+ WAF_ENABLED : ${{ inputs.WAF_ENABLED }}
350+ EXP : ${{ inputs.EXP }}
351+ AZURE_LOCATION : ${{ inputs.AZURE_LOCATION }}
352+ AZURE_ENV_OPENAI_LOCATION : ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
353+ IMAGE_TAG : ${{ inputs.IMAGE_TAG }}
354+ JOB_STATUS : ${{ job.status }}
355+ WEB_APPURL : ${{ steps.get_output_linux.outputs.WEB_APPURL }}
202356 run : |
203357 echo "## 🚀 Deploy Job Summary (Linux)" >> $GITHUB_STEP_SUMMARY
204358 echo "" >> $GITHUB_STEP_SUMMARY
205359 echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
206360 echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
207- echo "| **Job Status** | ${{ job.status == 'success' && '✅ Success' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
208- echo "| **Resource Group** | \`${{ inputs.RESOURCE_GROUP_NAME }}\` |" >> $GITHUB_STEP_SUMMARY
209- echo "| **Configuration Type** | \`${{ inputs.WAF_ENABLED == 'true' && inputs.EXP == 'true' && 'WAF + EXP' || inputs.WAF_ENABLED == 'true' && inputs.EXP != 'true' && 'WAF + Non-EXP' || inputs.WAF_ENABLED != 'true' && inputs.EXP == 'true' && 'Non-WAF + EXP' || 'Non-WAF + Non-EXP' }}\` |" >> $GITHUB_STEP_SUMMARY
210- echo "| **Azure Region (Infrastructure)** | \`${{ inputs.AZURE_LOCATION }}\` |" >> $GITHUB_STEP_SUMMARY
211- echo "| **Azure OpenAI Region** | \`${{ inputs.AZURE_ENV_OPENAI_LOCATION }}\` |" >> $GITHUB_STEP_SUMMARY
212- echo "| **Docker Image Tag** | \`${{ inputs.IMAGE_TAG }}\` |" >> $GITHUB_STEP_SUMMARY
361+
362+ if [[ "$JOB_STATUS" == "success" ]]; then
363+ echo "| **Job Status** | ✅ Success |" >> $GITHUB_STEP_SUMMARY
364+ else
365+ echo "| **Job Status** | ❌ Failed |" >> $GITHUB_STEP_SUMMARY
366+ fi
367+
368+ echo "| **Resource Group** | \`$RESOURCE_GROUP_NAME\` |" >> $GITHUB_STEP_SUMMARY
369+
370+ # Determine configuration type
371+ if [[ "$WAF_ENABLED" == "true" && "$EXP" == "true" ]]; then
372+ CONFIG_TYPE="WAF + EXP"
373+ elif [[ "$WAF_ENABLED" == "true" && "$EXP" != "true" ]]; then
374+ CONFIG_TYPE="WAF + Non-EXP"
375+ elif [[ "$WAF_ENABLED" != "true" && "$EXP" == "true" ]]; then
376+ CONFIG_TYPE="Non-WAF + EXP"
377+ else
378+ CONFIG_TYPE="Non-WAF + Non-EXP"
379+ fi
380+ echo "| **Configuration Type** | \`$CONFIG_TYPE\` |" >> $GITHUB_STEP_SUMMARY
381+
382+ echo "| **Azure Region (Infrastructure)** | \`$AZURE_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
383+ echo "| **Azure OpenAI Region** | \`$AZURE_ENV_OPENAI_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
384+ echo "| **Docker Image Tag** | \`$IMAGE_TAG\` |" >> $GITHUB_STEP_SUMMARY
213385 echo "" >> $GITHUB_STEP_SUMMARY
214- if [[ "${{ job.status }}" == "success" ]]; then
386+
387+ if [[ "$JOB_STATUS" == "success" ]]; then
215388 echo "### ✅ Deployment Details" >> $GITHUB_STEP_SUMMARY
216- echo "- **Web App URL**: [${{ steps.get_output_linux.outputs. WEB_APPURL }} ](${{ steps.get_output_linux.outputs. WEB_APPURL }} )" >> $GITHUB_STEP_SUMMARY
389+ echo "- **Web App URL**: [$WEB_APPURL]($WEB_APPURL)" >> $GITHUB_STEP_SUMMARY
217390 echo "- Successfully deployed to Azure with all resources configured" >> $GITHUB_STEP_SUMMARY
218391 echo "- Post-deployment scripts executed successfully" >> $GITHUB_STEP_SUMMARY
219392 else
0 commit comments