Skip to content

Commit 43b8c0b

Browse files
rajbosCopilot
andcommitted
fix: clean up portal-created cert/domain before TF apply instead of importing
The previous import approach caused a 400 'CertificateInUse' error because Azure auto-generates cert names in the portal, which don't match the Terraform resource name ('sharing-cert'). After import, TF detected the name drift and planned a destroy+recreate, but Azure blocks cert deletion while a domain binding exists. New approach: - Detect if TF state has a cert with a non-TF name (portal-created) - If so: delete the domain binding first, then delete the cert, remove stale TF state entries — letting Terraform create both resources fresh with the correct names - If cert is already TF-managed ('sharing-cert'): no-op Also added lifecycle.create_before_destroy = true on the cert resource as a safety net for future cert replacements. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5d1e5bc commit 43b8c0b

2 files changed

Lines changed: 38 additions & 14 deletions

File tree

.github/workflows/sharing-server-deploy.yml

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ jobs:
166166
-backend-config="container_name=${{ vars.TF_STATE_CONTAINER }}" \
167167
-backend-config="key=${{ needs.setup.outputs.state_key }}"
168168
169-
- name: Import existing custom domain resources
169+
- name: Reconcile custom domain Terraform state
170170
if: steps.prereqs.outputs.configured == 'true' && vars.SHARING_CUSTOM_DOMAIN != ''
171171
working-directory: sharing-server/infra
172172
env:
@@ -186,25 +186,45 @@ jobs:
186186
az account set --subscription "$ARM_SUBSCRIPTION_ID"
187187
188188
ENV_NAME="${TF_VAR_app_name}-env"
189+
EXPECTED_CERT_NAME="sharing-cert"
189190
190-
if ! terraform state show 'azurerm_container_app_environment_managed_certificate.this[0]' > /dev/null 2>&1; then
191-
CERT_ID=$(az containerapp env certificate list \
191+
# Detect if TF state has a cert with a name that doesn't match what Terraform
192+
# would create (e.g. a portal-created cert was previously imported). If so, we
193+
# must clean up Azure + state before apply, otherwise Terraform will try to
194+
# destroy the old cert while the custom domain is still bound to it (400 error).
195+
CURRENT_CERT_NAME=$(terraform state show 'azurerm_container_app_environment_managed_certificate.this[0]' 2>/dev/null \
196+
| grep -E '^\s+name\s+=' | head -1 \
197+
| sed 's/.*= "\(.*\)".*/\1/' || true)
198+
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 \
192210
--name "$ENV_NAME" \
193211
--resource-group "$TF_VAR_resource_group_name" \
194-
--query "[?properties.subjectName=='$TF_VAR_custom_domain'].id | [0]" \
212+
--query "[?properties.subjectName=='$TF_VAR_custom_domain'].name | [0]" \
195213
-o tsv 2>/dev/null || true)
196-
if [[ -n "$CERT_ID" && "$CERT_ID" != "None" ]]; then
197-
echo "Importing managed certificate: $CERT_ID"
198-
terraform import -input=false 'azurerm_container_app_environment_managed_certificate.this[0]' "$CERT_ID"
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 \
217+
--name "$ENV_NAME" \
218+
--resource-group "$TF_VAR_resource_group_name" \
219+
--certificate "$AZURE_CERT_NAME" --yes 2>/dev/null || true
199220
fi
200-
fi
201221
202-
if ! terraform state show 'azurerm_container_app_custom_domain.this[0]' > /dev/null 2>&1; then
203-
DOMAIN_ID="/subscriptions/$ARM_SUBSCRIPTION_ID/resourceGroups/$TF_VAR_resource_group_name/providers/Microsoft.App/containerApps/$TF_VAR_app_name/customDomainName/$TF_VAR_custom_domain"
204-
if az rest --method get --url "https://management.azure.com${DOMAIN_ID}?api-version=2024-03-01" > /dev/null 2>&1; then
205-
echo "Importing custom domain: $DOMAIN_ID"
206-
terraform import -input=false 'azurerm_container_app_custom_domain.this[0]' "$DOMAIN_ID"
207-
fi
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."
208228
fi
209229
210230
- name: Terraform plan

sharing-server/infra/main.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ resource "azurerm_container_app_environment_managed_certificate" "this" {
193193
container_app_environment_id = azurerm_container_app_environment.this.id
194194
subject_name = var.custom_domain
195195
domain_control_validation = "CNAME"
196+
197+
lifecycle {
198+
create_before_destroy = true
199+
}
196200
}
197201

198202
resource "azurerm_container_app_custom_domain" "this" {

0 commit comments

Comments
 (0)