1111 - " .github/workflows/sharing-server-deploy.yml"
1212 workflow_dispatch :
1313
14+ # One deploy per branch at a time — prevents concurrent Terraform runs from
15+ # conflicting on the same remote state file (state blob already locked errors).
16+ # Cancel in-progress for branch pushes so stale deploys don't block newer ones;
17+ # queue (no cancel) for main so production deploys always complete.
18+ concurrency :
19+ group : sharing-server-deploy-${{ github.ref }}
20+ cancel-in-progress : ${{ github.ref != 'refs/heads/main' }}
21+
1422# Minimal baseline; jobs declare only what they need.
1523permissions :
1624 contents : read
@@ -179,6 +187,7 @@ jobs:
179187 TF_VAR_session_secret : ${{ secrets.SHARING_SESSION_SECRET }}
180188 TF_VAR_allowed_github_org : ${{ vars.SHARING_ALLOWED_GITHUB_ORG }}
181189 TF_VAR_github_org_check_token : ${{ secrets.ORG_CHECK_TOKEN }}
190+ TF_VAR_admin_github_logins : ${{ vars.SHARING_ADMIN_GITHUB_LOGINS }}
182191 TF_VAR_min_replicas : ${{ needs.setup.outputs.min_replicas }}
183192 TF_VAR_custom_domain : ${{ vars.SHARING_CUSTOM_DOMAIN }}
184193 run : |
@@ -196,35 +205,71 @@ jobs:
196205 | grep -E '^\s+name\s+=' | head -1 \
197206 | sed 's/.*= "\(.*\)".*/\1/' || true)
198207
199- if [[ "$CURRENT_CERT_NAME" != "$EXPECTED_CERT_NAME" ]]; then
200- echo "Cert not TF-managed (current: '${CURRENT_CERT_NAME:-none}'). Cleaning up Azure resources so Terraform can recreate them."
201-
202- # Remove hostname binding first (cert cannot be deleted while a domain uses it)
203- az containerapp hostname delete \
204- --name "$TF_VAR_app_name" \
205- --resource-group "$TF_VAR_resource_group_name" \
206- --hostname "$TF_VAR_custom_domain" --yes 2>/dev/null || true
207-
208- # Find the cert by subject name and delete it
209- AZURE_CERT_NAME=$(az containerapp env certificate list \
208+ if [[ "$CURRENT_CERT_NAME" == "$EXPECTED_CERT_NAME" ]]; then
209+ echo "Cert already TF-managed as '$EXPECTED_CERT_NAME'. No cleanup needed."
210+ else
211+ # Cert is absent from TF state or has a mismatched name.
212+ # Before deleting anything, check whether the correctly-named cert already
213+ # exists in Azure (e.g. a previous apply timed out while polling for the cert
214+ # to become Succeeded, leaving it stranded in Azure but dropped from TF state).
215+ AZURE_CERT_ID=$(az containerapp env certificate list \
210216 --name "$ENV_NAME" \
211217 --resource-group "$TF_VAR_resource_group_name" \
212- --query "[?properties.subjectName =='$TF_VAR_custom_domain '].name | [0]" \
218+ --query "[?name =='$EXPECTED_CERT_NAME '].id | [0]" \
213219 -o tsv 2>/dev/null || true)
214- if [[ -n "$AZURE_CERT_NAME" && "$AZURE_CERT_NAME" != "None" ]]; then
215- echo "Deleting Azure cert: $AZURE_CERT_NAME"
216- az containerapp env certificate delete \
220+
221+ if [[ -n "$AZURE_CERT_ID" && "$AZURE_CERT_ID" != "None" ]]; then
222+ # The correctly-named cert exists in Azure but TF lost track of it.
223+ # Import it so apply doesn't delete-and-recreate (which resets provisioning
224+ # and triggers another 60-minute wait).
225+ echo "Cert '$EXPECTED_CERT_NAME' found in Azure but not in TF state. Importing..."
226+ if terraform import 'azurerm_container_app_environment_managed_certificate.this[0]' "$AZURE_CERT_ID"; then
227+ # Drop stale custom-domain state so cert_binding re-runs to re-bind.
228+ terraform state rm 'azurerm_container_app_custom_domain.this[0]' 2>/dev/null || true
229+ echo "Import done. Terraform will rebind the cert without recreating it."
230+ else
231+ # Import failed; delete the Azure cert so apply doesn't hit "already exists".
232+ echo "Import failed. Deleting Azure cert so Terraform can create a fresh one."
233+ az containerapp hostname delete \
234+ --name "$TF_VAR_app_name" \
235+ --resource-group "$TF_VAR_resource_group_name" \
236+ --hostname "$TF_VAR_custom_domain" --yes 2>/dev/null || true
237+ az containerapp env certificate delete \
238+ --name "$ENV_NAME" \
239+ --resource-group "$TF_VAR_resource_group_name" \
240+ --certificate "$EXPECTED_CERT_NAME" --yes 2>/dev/null || true
241+ terraform state rm 'azurerm_container_app_custom_domain.this[0]' 2>/dev/null || true
242+ terraform state rm 'azurerm_container_app_environment_managed_certificate.this[0]' 2>/dev/null || true
243+ echo "Cleanup done. Terraform will create cert and domain binding from scratch."
244+ fi
245+ else
246+ echo "Cert not TF-managed (current: '${CURRENT_CERT_NAME:-none}'). Cleaning up Azure resources so Terraform can recreate them."
247+
248+ # Remove hostname binding first (cert cannot be deleted while a domain uses it)
249+ az containerapp hostname delete \
250+ --name "$TF_VAR_app_name" \
251+ --resource-group "$TF_VAR_resource_group_name" \
252+ --hostname "$TF_VAR_custom_domain" --yes 2>/dev/null || true
253+
254+ # Find the cert by subject name and delete it
255+ AZURE_CERT_NAME=$(az containerapp env certificate list \
217256 --name "$ENV_NAME" \
218257 --resource-group "$TF_VAR_resource_group_name" \
219- --certificate "$AZURE_CERT_NAME" --yes 2>/dev/null || true
220- fi
258+ --query "[?properties.subjectName=='$TF_VAR_custom_domain'].name | [0]" \
259+ -o tsv 2>/dev/null || true)
260+ if [[ -n "$AZURE_CERT_NAME" && "$AZURE_CERT_NAME" != "None" ]]; then
261+ echo "Deleting Azure cert: $AZURE_CERT_NAME"
262+ az containerapp env certificate delete \
263+ --name "$ENV_NAME" \
264+ --resource-group "$TF_VAR_resource_group_name" \
265+ --certificate "$AZURE_CERT_NAME" --yes 2>/dev/null || true
266+ fi
221267
222- # Remove stale TF state entries so Terraform creates fresh resources
223- terraform state rm 'azurerm_container_app_custom_domain.this[0]' 2>/dev/null || true
224- terraform state rm 'azurerm_container_app_environment_managed_certificate.this[0]' 2>/dev/null || true
225- echo "Cleanup done. Terraform will create cert and domain binding from scratch."
226- else
227- echo "Cert already TF-managed as '$EXPECTED_CERT_NAME'. No cleanup needed."
268+ # Remove stale TF state entries so Terraform creates fresh resources
269+ terraform state rm 'azurerm_container_app_custom_domain.this[0]' 2>/dev/null || true
270+ terraform state rm 'azurerm_container_app_environment_managed_certificate.this[0]' 2>/dev/null || true
271+ echo "Cleanup done. Terraform will create cert and domain binding from scratch."
272+ fi
228273 fi
229274
230275 - name : Terraform plan
@@ -240,6 +285,7 @@ jobs:
240285 TF_VAR_session_secret : ${{ secrets.SHARING_SESSION_SECRET }}
241286 TF_VAR_allowed_github_org : ${{ vars.SHARING_ALLOWED_GITHUB_ORG }}
242287 TF_VAR_github_org_check_token : ${{ secrets.ORG_CHECK_TOKEN }}
288+ TF_VAR_admin_github_logins : ${{ vars.SHARING_ADMIN_GITHUB_LOGINS }}
243289 TF_VAR_min_replicas : ${{ needs.setup.outputs.min_replicas }}
244290 TF_VAR_custom_domain : ${{ vars.SHARING_CUSTOM_DOMAIN }}
245291 run : terraform plan -out=tfplan
@@ -257,6 +303,7 @@ jobs:
257303 TF_VAR_session_secret : ${{ secrets.SHARING_SESSION_SECRET }}
258304 TF_VAR_allowed_github_org : ${{ vars.SHARING_ALLOWED_GITHUB_ORG }}
259305 TF_VAR_github_org_check_token : ${{ secrets.ORG_CHECK_TOKEN }}
306+ TF_VAR_admin_github_logins : ${{ vars.SHARING_ADMIN_GITHUB_LOGINS }}
260307 TF_VAR_min_replicas : ${{ needs.setup.outputs.min_replicas }}
261308 TF_VAR_custom_domain : ${{ vars.SHARING_CUSTOM_DOMAIN }}
262309 run : terraform apply -auto-approve tfplan
0 commit comments