@@ -52,30 +52,156 @@ jobs:
5252 API_APP_URL : ${{ steps.get_output_linux.outputs.API_APP_URL }}
5353
5454 steps :
55+ - name : Validate Workflow Input Parameters
56+ shell : bash
57+ env :
58+ INPUT_ENV_NAME : ${{ inputs.ENV_NAME }}
59+ INPUT_AZURE_ENV_OPENAI_LOCATION : ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
60+ INPUT_AZURE_LOCATION : ${{ inputs.AZURE_LOCATION }}
61+ INPUT_RESOURCE_GROUP_NAME : ${{ inputs.RESOURCE_GROUP_NAME }}
62+ INPUT_IMAGE_TAG : ${{ inputs.IMAGE_TAG }}
63+ INPUT_BUILD_DOCKER_IMAGE : ${{ inputs.BUILD_DOCKER_IMAGE }}
64+ INPUT_EXP : ${{ inputs.EXP }}
65+ INPUT_WAF_ENABLED : ${{ inputs.WAF_ENABLED }}
66+ INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID : ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
67+ INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID : ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
68+ run : |
69+ echo "🔍 Validating workflow input parameters..."
70+ VALIDATION_FAILED=false
71+
72+ # Validate ENV_NAME (required, alphanumeric and hyphens)
73+ if [[ -z "$INPUT_ENV_NAME" ]]; then
74+ echo "❌ ERROR: ENV_NAME is required but not provided"
75+ VALIDATION_FAILED=true
76+ elif [[ ! "$INPUT_ENV_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then
77+ echo "❌ ERROR: ENV_NAME '$INPUT_ENV_NAME' is invalid. Must contain only alphanumerics and hyphens"
78+ VALIDATION_FAILED=true
79+ else
80+ echo "✅ ENV_NAME: '$INPUT_ENV_NAME' is valid"
81+ fi
82+
83+ # Validate AZURE_ENV_OPENAI_LOCATION (required, Azure region format)
84+ if [[ -z "$INPUT_AZURE_ENV_OPENAI_LOCATION" ]]; then
85+ echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION is required but not provided"
86+ VALIDATION_FAILED=true
87+ elif [[ ! "$INPUT_AZURE_ENV_OPENAI_LOCATION" =~ ^[a-z0-9]+$ ]]; then
88+ echo "❌ ERROR: AZURE_ENV_OPENAI_LOCATION '$INPUT_AZURE_ENV_OPENAI_LOCATION' is invalid. Must contain only lowercase letters and numbers"
89+ VALIDATION_FAILED=true
90+ else
91+ echo "✅ AZURE_ENV_OPENAI_LOCATION: '$INPUT_AZURE_ENV_OPENAI_LOCATION' is valid"
92+ fi
93+
94+ # Validate AZURE_LOCATION (required, Azure region format)
95+ if [[ -z "$INPUT_AZURE_LOCATION" ]]; then
96+ echo "❌ ERROR: AZURE_LOCATION is required but not provided"
97+ VALIDATION_FAILED=true
98+ elif [[ ! "$INPUT_AZURE_LOCATION" =~ ^[a-z0-9]+$ ]]; then
99+ echo "❌ ERROR: AZURE_LOCATION '$INPUT_AZURE_LOCATION' is invalid. Must contain only lowercase letters and numbers"
100+ VALIDATION_FAILED=true
101+ else
102+ echo "✅ AZURE_LOCATION: '$INPUT_AZURE_LOCATION' is valid"
103+ fi
104+
105+ # Validate RESOURCE_GROUP_NAME (required, Azure naming convention)
106+ if [[ -z "$INPUT_RESOURCE_GROUP_NAME" ]]; then
107+ echo "❌ ERROR: RESOURCE_GROUP_NAME is required but not provided"
108+ VALIDATION_FAILED=true
109+ elif [[ ! "$INPUT_RESOURCE_GROUP_NAME" =~ ^[a-zA-Z0-9._\(\)-]+$ ]] || [[ "$INPUT_RESOURCE_GROUP_NAME" =~ \.$ ]]; then
110+ echo "❌ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' is invalid. Must contain only alphanumerics, periods, underscores, hyphens, and parentheses. Cannot end with period."
111+ VALIDATION_FAILED=true
112+ elif [[ ${#INPUT_RESOURCE_GROUP_NAME} -gt 90 ]]; then
113+ echo "❌ ERROR: RESOURCE_GROUP_NAME '$INPUT_RESOURCE_GROUP_NAME' exceeds 90 characters"
114+ VALIDATION_FAILED=true
115+ else
116+ echo "✅ RESOURCE_GROUP_NAME: '$INPUT_RESOURCE_GROUP_NAME' is valid"
117+ fi
118+
119+ # Validate IMAGE_TAG (required, Docker tag pattern)
120+ if [[ -z "$INPUT_IMAGE_TAG" ]]; then
121+ echo "❌ ERROR: IMAGE_TAG is required but not provided"
122+ VALIDATION_FAILED=true
123+ elif [[ ! "$INPUT_IMAGE_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$ ]]; then
124+ echo "❌ ERROR: IMAGE_TAG '$INPUT_IMAGE_TAG' is invalid. Must start with alphanumeric or underscore, max 128 characters"
125+ VALIDATION_FAILED=true
126+ else
127+ echo "✅ IMAGE_TAG: '$INPUT_IMAGE_TAG' is valid"
128+ fi
129+
130+ # Validate BUILD_DOCKER_IMAGE (required, must be 'true' or 'false')
131+ if [[ "$INPUT_BUILD_DOCKER_IMAGE" != "true" && "$INPUT_BUILD_DOCKER_IMAGE" != "false" ]]; then
132+ echo "❌ ERROR: BUILD_DOCKER_IMAGE must be 'true' or 'false', got: '$INPUT_BUILD_DOCKER_IMAGE'"
133+ VALIDATION_FAILED=true
134+ else
135+ echo "✅ BUILD_DOCKER_IMAGE: '$INPUT_BUILD_DOCKER_IMAGE' is valid"
136+ fi
137+
138+ # Validate EXP (required, must be 'true' or 'false')
139+ if [[ "$INPUT_EXP" != "true" && "$INPUT_EXP" != "false" ]]; then
140+ echo "❌ ERROR: EXP must be 'true' or 'false', got: '$INPUT_EXP'"
141+ VALIDATION_FAILED=true
142+ else
143+ echo "✅ EXP: '$INPUT_EXP' is valid"
144+ fi
145+
146+ # Validate WAF_ENABLED (must be 'true' or 'false')
147+ if [[ "$INPUT_WAF_ENABLED" != "true" && "$INPUT_WAF_ENABLED" != "false" ]]; then
148+ echo "❌ ERROR: WAF_ENABLED must be 'true' or 'false', got: '$INPUT_WAF_ENABLED'"
149+ VALIDATION_FAILED=true
150+ else
151+ echo "✅ WAF_ENABLED: '$INPUT_WAF_ENABLED' is valid"
152+ fi
153+
154+ # Validate AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID (optional, if provided must be valid Resource ID)
155+ if [[ -n "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
156+ if [[ ! "$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/resourceGroups/[^/]+/providers/microsoft\.operationalinsights/workspaces/[^/]+$ ]]; then
157+ echo "❌ ERROR: AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID is invalid. Must be a valid Azure Resource ID format:"
158+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}"
159+ echo " Got: '$INPUT_AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID'"
160+ VALIDATION_FAILED=true
161+ else
162+ echo "✅ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID: Valid Resource ID format"
163+ fi
164+ fi
165+
166+ # Validate AZURE_EXISTING_AI_PROJECT_RESOURCE_ID (optional, if provided must be valid Resource ID)
167+ if [[ -n "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
168+ if [[ ! "$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" =~ ^/subscriptions/[a-fA-F0-9-]+/resourceGroups/[^/]+/providers/(Microsoft\.MachineLearningServices/(workspaces|projects)/[^/]+|Microsoft\.CognitiveServices/accounts/[^/]+/projects/[^/]+)$ ]]; then
169+ echo "❌ ERROR: AZURE_EXISTING_AI_PROJECT_RESOURCE_ID is invalid. Must be a valid Azure Resource ID format:"
170+ echo " /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/{accountName}/projects/{projectName}"
171+ echo " Got: '$INPUT_AZURE_EXISTING_AI_PROJECT_RESOURCE_ID'"
172+ VALIDATION_FAILED=true
173+ else
174+ echo "✅ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID: Valid Resource ID format"
175+ fi
176+ fi
177+
178+ # Fail workflow if any validation failed
179+ if [[ "$VALIDATION_FAILED" == "true" ]]; then
180+ echo ""
181+ echo "❌ Parameter validation failed. Please correct the errors above and try again."
182+ exit 1
183+ fi
184+
185+ echo ""
186+ echo "✅ All input parameters validated successfully!"
187+
55188 - name : Checkout Code
56189 uses : actions/checkout@v4
57190
58191 - name : Configure Parameters Based on WAF Setting
59192 shell : bash
193+ env :
194+ WAF_ENABLED : ${{ inputs.WAF_ENABLED }}
60195 run : |
61- if [[ "${{ inputs. WAF_ENABLED }} " == "true" ]]; then
196+ if [[ "$WAF_ENABLED" == "true" ]]; then
62197 cp infra/main.waf.parameters.json infra/main.parameters.json
63198 echo "✅ Successfully copied WAF parameters to main parameters file"
64199 else
65200 echo "🔧 Configuring Non-WAF deployment - using default main.parameters.json..."
66201 fi
67-
68- - name : Setup Azure CLI
69- shell : bash
70- run : |
71- curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
72202
73- - name : Setup Azure Developer CLI (Linux)
74- if : runner.os == 'Linux'
75- shell : bash
76- run : |
77- curl -fsSL https://aka.ms/install-azd.sh | sudo bash
78- azd version
203+ - name : Install azd
204+ uses : Azure/setup-azd@v2
79205
80206 - name : Login to AZD
81207 id : login-azure
@@ -88,51 +214,58 @@ jobs:
88214 - name : Deploy using azd up and extract values (Linux)
89215 id : get_output_linux
90216 shell : bash
217+ env :
218+ ENV_NAME : ${{ inputs.ENV_NAME }}
219+ AZURE_ENV_OPENAI_LOCATION : ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
220+ AZURE_LOCATION : ${{ inputs.AZURE_LOCATION }}
221+ RESOURCE_GROUP_NAME : ${{ inputs.RESOURCE_GROUP_NAME }}
222+ IMAGE_TAG : ${{ inputs.IMAGE_TAG }}
223+ BUILD_DOCKER_IMAGE : ${{ inputs.BUILD_DOCKER_IMAGE }}
224+ EXP : ${{ inputs.EXP }}
225+ AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID : ${{ inputs.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}
226+ AZURE_EXISTING_AI_PROJECT_RESOURCE_ID : ${{ inputs.AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }}
91227 run : |
92228 set -e
93229 echo "Starting azd deployment..."
94- echo "EXP: ${{ inputs. EXP }} "
95- echo "Using Docker Image Tag: ${{ inputs. IMAGE_TAG }} "
230+ echo "EXP: $EXP"
231+ echo "Using Docker Image Tag: $IMAGE_TAG"
96232
97- # Install azd (Azure Developer CLI)
98- curl -fsSL https://aka.ms/install-azd.sh | bash
99-
100233 # Generate current timestamp in desired format: YYYY-MM-DDTHH:MM:SS.SSSSSSSZ
101234 current_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%7NZ")
102235
103236 echo "Creating environment..."
104- azd env new ${{ inputs. ENV_NAME }} --no-prompt
105- echo "Environment created: ${{ inputs. ENV_NAME }} "
237+ azd env new $ENV_NAME --no-prompt
238+ echo "Environment created: $ENV_NAME"
106239
107240 echo "Setting default subscription..."
108241 azd config set defaults.subscription ${{ secrets.AZURE_SUBSCRIPTION_ID }}
109242
110243 # Set additional parameters
111244 azd env set AZURE_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
112- azd env set AZURE_ENV_OPENAI_LOCATION="${{ inputs. AZURE_ENV_OPENAI_LOCATION }} "
113- azd env set AZURE_LOCATION="${{ inputs. AZURE_LOCATION }} "
114- azd env set AZURE_RESOURCE_GROUP="${{ inputs. RESOURCE_GROUP_NAME }} "
115- azd env set AZURE_ENV_IMAGETAG="${{ inputs. IMAGE_TAG }} "
245+ azd env set AZURE_ENV_OPENAI_LOCATION="$AZURE_ENV_OPENAI_LOCATION"
246+ azd env set AZURE_LOCATION="$AZURE_LOCATION"
247+ azd env set AZURE_RESOURCE_GROUP="$RESOURCE_GROUP_NAME"
248+ azd env set AZURE_ENV_IMAGETAG="$IMAGE_TAG"
116249
117- if [[ "${{ inputs. BUILD_DOCKER_IMAGE }} " == "true" ]]; then
250+ if [[ "$BUILD_DOCKER_IMAGE" == "true" ]]; then
118251 ACR_NAME=$(echo "${{ secrets.ACR_TEST_LOGIN_SERVER }}")
119252 azd env set AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT="$ACR_NAME"
120253 echo "Set ACR name to: $ACR_NAME"
121254 else
122255 echo "Skipping ACR name configuration (using existing image)"
123256 fi
124257
125- if [[ "${{ inputs. EXP }} " == "true" ]]; then
258+ if [[ "$EXP" == "true" ]]; then
126259 echo "✅ EXP ENABLED - Setting EXP parameters..."
127260
128- if [[ -n "${{ inputs. AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} " ]]; then
129- EXP_LOG_ANALYTICS_ID="${{ inputs. AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }} "
261+ if [[ -n "$AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID" ]]; then
262+ EXP_LOG_ANALYTICS_ID="$AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID"
130263 else
131264 EXP_LOG_ANALYTICS_ID="${{ secrets.AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID }}"
132265 fi
133266
134- if [[ -n "${{ inputs. AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} " ]]; then
135- EXP_AI_PROJECT_ID="${{ inputs. AZURE_EXISTING_AI_PROJECT_RESOURCE_ID }} "
267+ if [[ -n "$AZURE_EXISTING_AI_PROJECT_RESOURCE_ID" ]]; then
268+ EXP_AI_PROJECT_ID="$AZURE_EXISTING_AI_PROJECT_RESOURCE_ID"
136269 else
137270 EXP_AI_PROJECT_ID="${{ secrets.AZURE_ENV_FOUNDRY_PROJECT_ID }}"
138271 fi
@@ -159,28 +292,35 @@ jobs:
159292 exit 1
160293 fi
161294
162- export API_APP_URL="$(echo "$DEPLOY_OUTPUT" | jq -r '.API_APP_URL // empty')"
295+ API_APP_URL="$(echo "$DEPLOY_OUTPUT" | jq -r '.API_APP_URL // empty')"
163296 echo "API_APP_URL=$API_APP_URL" >> $GITHUB_ENV
164297 echo "API_APP_URL=$API_APP_URL" >> $GITHUB_OUTPUT
165298
166- export WEB_APP_URL="$(echo "$DEPLOY_OUTPUT" | jq -r '.WEB_APP_URL // empty')"
299+ WEB_APP_URL="$(echo "$DEPLOY_OUTPUT" | jq -r '.WEB_APP_URL // empty')"
167300 echo "WEB_APP_URL=$WEB_APP_URL" >> $GITHUB_ENV
168301 echo "WEB_APP_URL=$WEB_APP_URL" >> $GITHUB_OUTPUT
169302
170303 - name : Generate Deployment Summary
171304 if : always()
172305 shell : bash
306+ env :
307+ RESOURCE_GROUP_NAME : ${{ inputs.RESOURCE_GROUP_NAME }}
308+ WAF_ENABLED : ${{ inputs.WAF_ENABLED }}
309+ EXP : ${{ inputs.EXP }}
310+ AZURE_LOCATION : ${{ inputs.AZURE_LOCATION }}
311+ AZURE_ENV_OPENAI_LOCATION : ${{ inputs.AZURE_ENV_OPENAI_LOCATION }}
312+ IMAGE_TAG : ${{ inputs.IMAGE_TAG }}
173313 run : |
174314 echo "## 🚀 Deploy Job Summary (Linux)" >> $GITHUB_STEP_SUMMARY
175315 echo "" >> $GITHUB_STEP_SUMMARY
176316 echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
177317 echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
178318 echo "| **Job Status** | ${{ job.status == 'success' && '✅ Success' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
179- echo "| **Resource Group** | \`${{ inputs. RESOURCE_GROUP_NAME }} \` |" >> $GITHUB_STEP_SUMMARY
319+ echo "| **Resource Group** | \`$RESOURCE_GROUP_NAME\` |" >> $GITHUB_STEP_SUMMARY
180320 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
181- echo "| **Azure Region (Infrastructure)** | \`${{ inputs. AZURE_LOCATION }} \` |" >> $GITHUB_STEP_SUMMARY
182- echo "| **Azure OpenAI Region** | \`${{ inputs. AZURE_ENV_OPENAI_LOCATION }} \` |" >> $GITHUB_STEP_SUMMARY
183- echo "| **Docker Image Tag** | \`${{ inputs. IMAGE_TAG }} \` |" >> $GITHUB_STEP_SUMMARY
321+ echo "| **Azure Region (Infrastructure)** | \`$AZURE_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
322+ echo "| **Azure OpenAI Region** | \`$AZURE_ENV_OPENAI_LOCATION\` |" >> $GITHUB_STEP_SUMMARY
323+ echo "| **Docker Image Tag** | \`$IMAGE_TAG\` |" >> $GITHUB_STEP_SUMMARY
184324 echo "" >> $GITHUB_STEP_SUMMARY
185325 if [[ "${{ job.status }}" == "success" ]]; then
186326 echo "### ✅ Deployment Details" >> $GITHUB_STEP_SUMMARY
0 commit comments