Skip to content

Commit ff788b2

Browse files
rajbosCopilot
andcommitted
feat: add custom domain + managed TLS certificate support
- variables.tf: add custom_domain variable (optional, default '') - main.tf: split locals into aca_fqdn + app_fqdn (prefers custom domain), add azurerm_container_app_environment_managed_certificate and azurerm_container_app_custom_domain resources (count-gated on custom_domain) - outputs.tf: add aca_fqdn output; existing outputs already use app_fqdn - deploy workflow: add TF_VAR_custom_domain from SHARING_CUSTOM_DOMAIN var, add import step that detects portal-created resources and imports them into Terraform state before plan (idempotent — skips if already managed) - GitHub env var SHARING_CUSTOM_DOMAIN=ai-fluency-server-test.devopsjournal.io set for testing environment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0133057 commit ff788b2

4 files changed

Lines changed: 77 additions & 1 deletion

File tree

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,42 @@ 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
170+
if: steps.prereqs.outputs.configured == 'true' && vars.SHARING_CUSTOM_DOMAIN != ''
171+
working-directory: sharing-server/infra
172+
env:
173+
TF_VAR_resource_group_name: ${{ vars.AZURE_RESOURCE_GROUP }}
174+
TF_VAR_app_name: ${{ needs.setup.outputs.app_name }}
175+
TF_VAR_custom_domain: ${{ vars.SHARING_CUSTOM_DOMAIN }}
176+
run: |
177+
# Authenticate az CLI with the same service principal used by Terraform.
178+
az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID" --output none
179+
az account set --subscription "$ARM_SUBSCRIPTION_ID"
180+
181+
ENV_NAME="${TF_VAR_app_name}-env"
182+
183+
# Import managed certificate if it already exists in Azure but not in state.
184+
if ! terraform state show 'azurerm_container_app_environment_managed_certificate.this[0]' > /dev/null 2>&1; then
185+
CERT_ID=$(az containerapp env certificate list \
186+
--name "$ENV_NAME" \
187+
--resource-group "$TF_VAR_resource_group_name" \
188+
--query "[?properties.subjectName=='$TF_VAR_custom_domain'].id | [0]" \
189+
-o tsv 2>/dev/null || true)
190+
if [[ -n "$CERT_ID" && "$CERT_ID" != "None" ]]; then
191+
echo "Importing managed certificate: $CERT_ID"
192+
terraform import 'azurerm_container_app_environment_managed_certificate.this[0]' "$CERT_ID"
193+
fi
194+
fi
195+
196+
# Import custom domain binding if it already exists in Azure but not in state.
197+
if ! terraform state show 'azurerm_container_app_custom_domain.this[0]' > /dev/null 2>&1; then
198+
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"
199+
if az rest --method get --url "https://management.azure.com${DOMAIN_ID}?api-version=2024-03-01" > /dev/null 2>&1; then
200+
echo "Importing custom domain: $DOMAIN_ID"
201+
terraform import 'azurerm_container_app_custom_domain.this[0]' "$DOMAIN_ID"
202+
fi
203+
fi
204+
169205
- name: Terraform plan
170206
if: steps.prereqs.outputs.configured == 'true'
171207
working-directory: sharing-server/infra
@@ -180,6 +216,7 @@ jobs:
180216
TF_VAR_allowed_github_org: ${{ vars.SHARING_ALLOWED_GITHUB_ORG }}
181217
TF_VAR_github_org_check_token: ${{ secrets.ORG_CHECK_TOKEN }}
182218
TF_VAR_min_replicas: ${{ needs.setup.outputs.min_replicas }}
219+
TF_VAR_custom_domain: ${{ vars.SHARING_CUSTOM_DOMAIN }}
183220
run: terraform plan -out=tfplan
184221

185222
- name: Terraform apply
@@ -196,6 +233,7 @@ jobs:
196233
TF_VAR_allowed_github_org: ${{ vars.SHARING_ALLOWED_GITHUB_ORG }}
197234
TF_VAR_github_org_check_token: ${{ secrets.ORG_CHECK_TOKEN }}
198235
TF_VAR_min_replicas: ${{ needs.setup.outputs.min_replicas }}
236+
TF_VAR_custom_domain: ${{ vars.SHARING_CUSTOM_DOMAIN }}
199237
run: terraform apply -auto-approve tfplan
200238

201239
- name: Output deployment summary

sharing-server/infra/main.tf

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ resource "azurerm_container_app_environment_storage" "data" {
5353
# The ACA environment default_domain is known after environment creation,
5454
# so this local can be used for BASE_URL before the container app is created.
5555
locals {
56-
app_fqdn = "${var.app_name}.${azurerm_container_app_environment.this.default_domain}"
56+
# Native ACA FQDN — always available; used as CNAME target for custom DNS setup.
57+
aca_fqdn = "${var.app_name}.${azurerm_container_app_environment.this.default_domain}"
58+
# Effective public hostname — custom domain when provided, ACA FQDN otherwise.
59+
app_fqdn = var.custom_domain != "" ? var.custom_domain : local.aca_fqdn
5760
}
5861

5962
resource "azurerm_container_app" "this" {
@@ -177,3 +180,27 @@ resource "azurerm_container_app" "this" {
177180
}
178181
}
179182
}
183+
184+
# ── Custom domain + managed TLS certificate ───────────────────────────────────
185+
# Only created when var.custom_domain is set.
186+
# DNS prerequisites (must exist before applying):
187+
# CNAME <subdomain> → local.aca_fqdn
188+
# TXT asuid.<subdomain> → azurerm_container_app_environment.this.custom_domain_verification_id
189+
190+
resource "azurerm_container_app_environment_managed_certificate" "this" {
191+
count = var.custom_domain != "" ? 1 : 0
192+
name = "sharing-cert"
193+
container_app_environment_id = azurerm_container_app_environment.this.id
194+
dns_suffix = var.custom_domain
195+
domain_control_validation = "CNAME"
196+
}
197+
198+
resource "azurerm_container_app_custom_domain" "this" {
199+
count = var.custom_domain != "" ? 1 : 0
200+
name = var.custom_domain
201+
container_app_id = azurerm_container_app.this.id
202+
container_app_environment_certificate_id = azurerm_container_app_environment_managed_certificate.this[0].id
203+
certificate_binding_type = "SniEnabled"
204+
205+
depends_on = [azurerm_container_app_environment_managed_certificate.this]
206+
}

sharing-server/infra/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
output "aca_fqdn" {
2+
description = "Native ACA FQDN — use as CNAME target when setting up a custom domain"
3+
value = local.aca_fqdn
4+
}
5+
16
output "app_url" {
27
description = "Public HTTPS URL of the deployed sharing server"
38
value = "https://${local.app_fqdn}"

sharing-server/infra/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ variable "github_org_check_token" {
5050
default = ""
5151
}
5252

53+
variable "custom_domain" {
54+
description = "Optional: custom domain to bind to the container app (e.g. sharing.example.com). Leave empty to use the ACA-generated FQDN."
55+
type = string
56+
default = ""
57+
}
58+
5359
variable "min_replicas" {
5460
description = "Minimum container replicas. Must be 1 for SQLite on Azure Files — scale-to-zero causes stale SMB oplocks that block DB startup. Scale-to-zero (0) is only safe if you accept occasional lock errors on cold start."
5561
type = number

0 commit comments

Comments
 (0)