Skip to content

Commit 759442b

Browse files
Expose custom_domain_verification_id and document custom_domains DNS prerequisites (#279)
* Initial plan * plan: diagnose custom_domains asuid TXT record issue Agent-Logs-Url: https://github.com/Azure/terraform-azurerm-avm-res-web-site/sessions/475d34b0-5f16-4b9c-941d-fdd115e12c77 Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * fix: expose custom_domain_verification_id and document DNS prerequisites Agent-Logs-Url: https://github.com/Azure/terraform-azurerm-avm-res-web-site/sessions/475d34b0-5f16-4b9c-941d-fdd115e12c77 Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * docs: point users to resource_uri output for the CNAME target Agent-Logs-Url: https://github.com/Azure/terraform-azurerm-avm-res-web-site/sessions/475d34b0-5f16-4b9c-941d-fdd115e12c77 Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * Initial plan Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * Initial plan * feat: expose custom_domain_verification_id and document custom_domains DNS prerequisites Agent-Logs-Url: https://github.com/Azure/terraform-azurerm-avm-res-web-site/sessions/1a2b4195-34cb-4904-a72b-6ba77d890a40 Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * Initial plan Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * Initial plan Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> * feat(custom_domains): add certificates input, certificate_key reference, and certificate submodule - Add new certificates map input on the root module and a matching certificate submodule for Microsoft.Web/certificates resources sourced from Key Vault or inline PFX. - Extend custom_domains (and per-slot custom_domains) with certificate_key so callers can reference a managed certificate by key instead of raw humbprint (mutually exclusive). - Default the hostname_binding retry policy to also retry on transient DNS / hostname-validation errors that surface while custom domain ownership records are still propagating. - Rewrite the custom_domain example end-to-end: real Key Vault, App Service first-party SP role assignment, certificate via root module input, hostname binding on site and qa slot. Hostname locals inlined above Step 1 in main.tf. - Mark the example with .e2eignore (default contoso.com hostnames are not owned). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jaredfholgate <1612200+jaredfholgate@users.noreply.github.com> Co-authored-by: Jared Holgate <jaredholgate@microsoft.com>
1 parent 5c69373 commit 759442b

19 files changed

Lines changed: 1027 additions & 139 deletions

File tree

README.md

Lines changed: 95 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,43 @@ Type: `string`
579579

580580
Default: `"[1.*, 2.0.0)"`
581581

582+
### <a name="input_certificates"></a> [certificates](#input\_certificates)
583+
584+
Description: A map of `Microsoft.Web/certificates` resources to create on the App Service
585+
plan. Each entry materialises a certificate that can be referenced from a
586+
`custom_domains` entry (on the main site or any slot) via `certificate_key`,
587+
removing the need for callers to invoke the `certificate` submodule directly.
588+
589+
Either `key_vault_id` + `key_vault_secret_name` (Key Vault sourced) or
590+
`pfx_blob` (+ optional `password`) (inline upload) must be supplied; the two
591+
modes are mutually exclusive. When sourcing from Key Vault the App Service
592+
first-party service principal (`abfa0a7c-a6b6-4736-8310-5855508787cd`) must
593+
have the `Key Vault Certificate User` role on the vault scope.
594+
595+
- `name` - (Optional) The name of the certificate resource. Defaults to the map key.
596+
- `key_vault_id` - (Optional) The resource ID of the Key Vault holding the certificate.
597+
- `key_vault_secret_name` - (Optional) The Key Vault secret/certificate name.
598+
- `pfx_blob` - (Optional) Base64-encoded PFX contents.
599+
- `password` - (Optional) Password for the supplied PFX blob.
600+
- `host_names` - (Optional) Hostnames the certificate applies to.
601+
- `tags` - (Optional) Tags applied to the certificate resource.
602+
603+
Type:
604+
605+
```hcl
606+
map(object({
607+
name = optional(string)
608+
key_vault_id = optional(string)
609+
key_vault_secret_name = optional(string)
610+
pfx_blob = optional(string)
611+
password = optional(string)
612+
host_names = optional(list(string))
613+
tags = optional(map(string))
614+
}))
615+
```
616+
617+
Default: `{}`
618+
582619
### <a name="input_client_affinity_enabled"></a> [client\_affinity\_enabled](#input\_client\_affinity\_enabled)
583620

584621
Description: Should client affinity be enabled for the App Service? Defaults to `false`.
@@ -664,73 +701,47 @@ Default: `false`
664701

665702
### <a name="input_custom_domains"></a> [custom\_domains](#input\_custom\_domains)
666703

667-
Description: A map of custom domains to assign to the App Service.
668-
669-
- `slot_as_target` - (Optional) Should the slot be used as the target? Defaults to `false`.
670-
- `app_service_slot_key` - (Optional) The key of the deployment slot to target.
671-
- `create_certificate` - (Optional) Should a managed certificate be created? Defaults to `false`.
672-
- `certificate_name` - (Optional) The name of the certificate.
673-
- `certificate_location` - (Optional) The location of the certificate.
674-
- `pfx_blob` - (Optional) The PFX blob for the certificate.
675-
- `pfx_password` - (Optional) The password for the PFX certificate.
676-
- `hostname` - (Optional) The custom domain hostname.
677-
- `app_service_name` - (Optional) The App Service name.
678-
- `app_service_plan_resource_id` - (Optional) The resource ID of the App Service Plan.
679-
- `key_vault_secret_id` - (Optional) The Key Vault secret ID for the certificate.
680-
- `key_vault_id` - (Optional) The Key Vault ID for the certificate.
681-
- `zone_resource_group_name` - (Optional) The resource group of the DNS zone.
682-
- `resource_group_name` - (Optional) The resource group name.
704+
Description: A map of custom domains to bind to the main App Service site.
705+
706+
To bind a custom domain to a deployment slot instead, set
707+
`custom_domains` on the corresponding entry in `var.deployment_slots`.
708+
709+
This module only creates the hostname binding. It does **not** create the
710+
underlying DNS records – those must be provisioned separately (for example
711+
with `Azure/avm-res-network-dnszone/azurerm`) before the binding is applied.
712+
Certificates may either be provisioned out of band and referenced by
713+
`thumbprint`, or declared inline via `var.certificates` and referenced by
714+
`certificate_key`.
715+
716+
### DNS prerequisites
717+
718+
Azure validates ownership of the custom hostname when the binding is created.
719+
At least one of the following DNS records must already exist and be
720+
resolvable, otherwise the binding will fail with errors such as
721+
`A TXT record pointing from asuid.{0} to {1} was not found.`:
722+
723+
- A `CNAME` record for the custom hostname pointing to
724+
`<site-name>.azurewebsites.net` (the module exposes this value via the
725+
`resource_uri` output), **or**
726+
- A `TXT` record at `asuid.<custom-hostname>` whose value is the App Service's
727+
custom domain verification ID. This module exposes that value via the
728+
`custom_domain_verification_id` output.
729+
730+
### Field reference
731+
732+
- `hostname` - (Required) The custom domain hostname to bind.
683733
- `ssl_state` - (Optional) The SSL state. Possible values are `IpBasedEnabled` and `SniEnabled`.
684-
- `inherit_tags` - (Optional) Should tags be inherited from the parent? Defaults to `true`.
685-
- `tags` - (Optional) Tags to apply to the custom domain resources.
686-
- `thumbprint` - (Optional) The certificate thumbprint value.
687-
- `thumbprint_key` - (Optional) The key to look up the certificate thumbprint.
688-
- `ttl` - (Optional) The TTL for DNS records. Defaults to `300`.
689-
- `validation_type` - (Optional) The domain validation type. Defaults to `cname-delegation`.
690-
- `create_cname_records` - (Optional) Should CNAME records be created? Defaults to `false`.
691-
- `cname_name` - (Optional) The CNAME record name.
692-
- `cname_zone_name` - (Optional) The DNS zone name for the CNAME record.
693-
- `cname_record` - (Optional) The CNAME record value.
694-
- `cname_target_resource_id` - (Optional) The target resource ID for the CNAME record.
695-
- `create_txt_records` - (Optional) Should TXT records be created? Defaults to `false`.
696-
- `txt_name` - (Optional) The TXT record name.
697-
- `txt_zone_name` - (Optional) The DNS zone name for the TXT record.
698-
- `txt_records` - (Optional) A map of TXT records with `value` attribute.
734+
- `thumbprint` - (Optional) The thumbprint of a certificate already uploaded to the App Service. Mutually exclusive with `certificate_key`.
735+
- `certificate_key` - (Optional) The map key of an entry in `var.certificates` whose thumbprint should be used for this binding. Mutually exclusive with `thumbprint`.
699736

700737
Type:
701738

702739
```hcl
703740
map(object({
704-
slot_as_target = optional(bool, false)
705-
app_service_slot_key = optional(string)
706-
create_certificate = optional(bool, false)
707-
certificate_name = optional(string)
708-
certificate_location = optional(string)
709-
pfx_blob = optional(string)
710-
pfx_password = optional(string)
711-
hostname = optional(string)
712-
app_service_name = optional(string)
713-
app_service_plan_resource_id = optional(string)
714-
key_vault_secret_id = optional(string)
715-
key_vault_id = optional(string)
716-
zone_resource_group_name = optional(string)
717-
resource_group_name = optional(string)
718-
ssl_state = optional(string)
719-
inherit_tags = optional(bool, true)
720-
tags = optional(map(any), {})
721-
thumbprint = optional(string)
722-
thumbprint_key = optional(string)
723-
ttl = optional(number, 300)
724-
validation_type = optional(string, "cname-delegation")
725-
create_cname_records = optional(bool, false)
726-
cname_name = optional(string)
727-
cname_zone_name = optional(string)
728-
cname_record = optional(string)
729-
cname_target_resource_id = optional(string)
730-
create_txt_records = optional(bool, false)
731-
txt_name = optional(string)
732-
txt_zone_name = optional(string)
733-
txt_records = optional(map(object({ value = string })))
741+
hostname = string
742+
ssl_state = optional(string)
743+
thumbprint = optional(string)
744+
certificate_key = optional(string)
734745
}))
735746
```
736747

@@ -928,6 +939,10 @@ Description: A map of deployment slots to create for the App Service.
928939
- `value` - (Optional) The value of the connection string.
929940
- `zip_deploy_file` - (Optional) The path to the zip file to deploy to the slot.
930941
- `zip_deploy_wait_duration` - (Optional) The duration to wait after the slot is configured before triggering zip deploy. Defaults to `60s`.
942+
- `custom_domains` - (Optional) A map of custom domains to bind to this deployment slot. The same DNS prerequisites described on the top-level `custom_domains` variable apply.
943+
- `hostname` - (Required) The custom domain hostname to bind.
944+
- `ssl_state` - (Optional) The SSL state. Possible values are `IpBasedEnabled` and `SniEnabled`.
945+
- `thumbprint` - (Optional) The thumbprint of a certificate already uploaded to the App Service.
931946

932947
Type:
933948

@@ -1248,6 +1263,12 @@ map(object({
12481263
})), {})
12491264
zip_deploy_file = optional(string)
12501265
zip_deploy_wait_duration = optional(string, "60s")
1266+
custom_domains = optional(map(object({
1267+
hostname = string
1268+
ssl_state = optional(string)
1269+
thumbprint = optional(string)
1270+
certificate_key = optional(string)
1271+
})), {})
12511272
}))
12521273
```
12531274

@@ -2419,6 +2440,13 @@ The following outputs are exported:
24192440

24202441
Description: The active slot resource ID.
24212442

2443+
### <a name="output_custom_domain_verification_id"></a> [custom\_domain\_verification\_id](#output\_custom\_domain\_verification\_id)
2444+
2445+
Description: The custom domain verification ID for the App Service. Use this value to create
2446+
an `asuid.<custom-hostname>` TXT record in your DNS zone before binding a custom
2447+
domain via `var.custom_domains`. See the `custom_domains` variable documentation
2448+
for details on the DNS prerequisites that Azure enforces.
2449+
24222450
### <a name="output_deployment_slot_locks"></a> [deployment\_slot\_locks](#output\_deployment\_slot\_locks)
24232451

24242452
Description: The locks of the deployment slots.
@@ -2489,6 +2517,12 @@ Source: Azure/avm-utl-interfaces/azure
24892517

24902518
Version: 0.5.1
24912519

2520+
### <a name="module_certificate"></a> [certificate](#module\_certificate)
2521+
2522+
Source: ./modules/certificate
2523+
2524+
Version:
2525+
24922526
### <a name="module_config_appsettings"></a> [config\_appsettings](#module\_config\_appsettings)
24932527

24942528
Source: ./modules/config_appsettings

examples/custom_domain/.e2eignore

Whitespace-only changes.

0 commit comments

Comments
 (0)