hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.53k stars 4.61k forks source link

azurerm_static_site_custom_domain.validation_token dissapears after successfull validation yielding entire TF setup stuck on failed validation #14750

Closed piotr-lasota closed 1 year ago

piotr-lasota commented 2 years ago

Community Note

Terraform (and AzureRM Provider) Version

Terraform v1.1.2
on darwin_amd64
+ provider registry.terraform.io/hashicorp/azurerm v2.90.0

Affected Resource(s)

Terraform Configuration Files

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "2.90.0"
    }
  }
}

provider "azurerm" {
  features {}
}

variable "minimum_working_example_name" {
  description = "Resources name to be used with appropriate resource type prefixes"
  type        = string
  default     = "terraform-mwe"
}

variable "domain_name" {
  description = "The domain you will map to (like mywebsite.com)"
  type        = string
}

resource "azurerm_resource_group" "resource_group" {
  name     = "rg-${var.minimum_working_example_name}"
  location = "westeurope"
}

resource "azurerm_static_site" "website" {
  name                = "app-${var.minimum_working_example_name}"
  resource_group_name = azurerm_resource_group.resource_group.name
  location            = azurerm_resource_group.resource_group.location
  sku_size            = "Free"
  sku_tier            = "Free"
}

resource "azurerm_dns_zone" "dns" {
  name                = var.domain_name
  resource_group_name = azurerm_resource_group.resource_group.name
}

resource "azurerm_static_site_custom_domain" "txt-root" {
  static_site_id  = azurerm_static_site.website.id
  domain_name     = azurerm_dns_zone.dns.name
  validation_type = "dns-txt-token"
}

resource "azurerm_dns_txt_record" "txt-root" {
  name                = "@"
  zone_name           = azurerm_dns_zone.dns.name
  resource_group_name = azurerm_resource_group.resource_group.name
  ttl                 = 3600
  record {
    value = azurerm_static_site_custom_domain.txt-root.validation_token
  }
}

Debug Output

Debug output Github Gist link

Panic Output

Not applicable

Expected Behaviour

I can use plan and apply after successful TXT validation by azurerm_static_site_custom_domain

Actual Behaviour

As long as the TXT validation is still pending (on Azure Portal), everything works perfectly.

As soon as the validation is done:

$: terraform plan / terraform apply
azurerm_resource_group.resource_group: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe]
azurerm_static_site.website: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe]
azurerm_dns_zone.dns: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Network/dnszones/<redacted_domain>]
azurerm_static_site_custom_domain.txt-root: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe/customDomains/<redacted_domain>]
azurerm_dns_txt_record.txt-root: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Network/dnszones/<redacted_domain>/TXT/@]
β•·
β”‚ Error: expected length of record.0.value to be in the range (1 - 1024), got 
β”‚ 
β”‚   with azurerm_dns_txt_record.txt-root,
β”‚   on main.tf line 38, in resource "azurerm_dns_txt_record" "txt-root":
β”‚   38: resource "azurerm_dns_txt_record" "txt-root" {
β”‚ 
β•΅

Steps to Reproduce

  1. terraform apply the provided configuration file
  2. Wait for TXT validation to finalize (not awaited by TF as per explicit warning in the docs) via Azure Portal
    1. Go to Azure Portal
    2. Go to Static Web Apps
    3. Go to Custom Domains
    4. Wait for your TXT "Validating" spinner to get replaced by green checkbox + "Ready" (this can take up to 12h)
  3. Try doing terraform plan or terraform apply (even without changing anything)

If you do a terraform plan -refresh-only then it is indicated that the azurerm_static_site_custom_domain no longer contains the validation_token.

$: terraform plan -refresh-only
azurerm_resource_group.resource_group: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe]
azurerm_static_site.website: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe]
azurerm_dns_zone.dns: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Network/dnszones/<redacted_domain>]
azurerm_static_site_custom_domain.txt-root: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe/customDomains/<redacted_domain>]
azurerm_dns_txt_record.txt-root: Refreshing state... [id=/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Network/dnszones/<redacted_domain>/TXT/@]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # azurerm_dns_txt_record.txt-root has changed
  ~ resource "azurerm_dns_txt_record" "txt-root" {
        id                  = "/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Network/dnszones/<redacted_domain>/TXT/@"
        name                = "@"
      + tags                = {}
        # (4 unchanged attributes hidden)

        record {
          # At least one attribute in this block is (or was) sensitive,
          # so its contents will not be displayed.
        }
    }

  # azurerm_dns_zone.dns has changed
  ~ resource "azurerm_dns_zone" "dns" {
        id                        = "/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Network/dnszones/<redacted_domain>"
        name                      = "<redacted_domain>"
      ~ number_of_record_sets     = 2 -> 3
      + tags                      = {}
        # (3 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # azurerm_resource_group.resource_group has changed
  ~ resource "azurerm_resource_group" "resource_group" {
        id       = "/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe"
        name     = "rg-terraform-mwe"
      + tags     = {}
        # (1 unchanged attribute hidden)
    }

  # azurerm_static_site.website has changed
  ~ resource "azurerm_static_site" "website" {
        id                  = "/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe"
        name                = "app-terraform-mwe"
      + tags                = {}
        # (6 unchanged attributes hidden)
    }

  # azurerm_static_site_custom_domain.txt-root has changed
  ~ resource "azurerm_static_site_custom_domain" "txt-root" {
        id               = "/subscriptions/<redacted_subscription_id>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe/customDomains/<redacted_domain>"
      - validation_token = (sensitive value)
        # (3 unchanged attributes hidden)
    }

Proceeding with the refresh via terraform apply -refresh-only yields no improvement on the issue

Important Factoids

None that I am aware of

References

Searching for similiar issue yielded no results so I assume this one is first.

piotr-lasota commented 2 years ago

I assume that the issue stems from the fact that after validation the token is no longer returned by Azure. Thus during refreshing the property is not present in the response.

During validation:

$: az staticwebapp hostname list --name app-terraform-mwe
[
    {
        "createdOn": "2021-12-31T12:19:45.157424",
        "domainName": "<domain>",
        "errorMessage": null,
        "id": "/subscriptions/<sub_id>>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe/customDomains/<domain>",
        "kind": null,
        "location": "West Europe",
        "name": "<domain>",
        "resourceGroup": "rg-terraform-mwe",
        "status": "Validating",
        "type": "Microsoft.Web/staticSites/customDomains",
        "validationToken": "<validation_token>"
    }
]

After validation passed (validation_token is null):

$: az staticwebapp hostname list --name app-terraform-mwe
[
    {
        "createdOn": "2021-12-31T12:19:45.157424",
        "domainName": "<domain>",
        "errorMessage": null,
        "id": "/subscriptions/<sub_id>>/resourceGroups/rg-terraform-mwe/providers/Microsoft.Web/staticSites/app-terraform-mwe/customDomains/<domain>",
        "kind": null,
        "location": "West Europe",
        "name": "<domain>",
        "resourceGroup": "rg-terraform-mwe",
        "status": "Ready",
        "type": "Microsoft.Web/staticSites/customDomains",
        "validationToken": null
    }
]

Attempt to preserve the original token via livecycle ignore_changes did not fix this

resource "azurerm_static_site_custom_domain" "txt-root" {
  static_site_id  = azurerm_static_site.website.id
  domain_name     = azurerm_dns_zone.dns.name
  validation_type = "dns-txt-token"

  lifecycle {
    ignore_changes = [
      validation_token
    ]
  }
}

and still yields the same error message as in the issue description

piotr-lasota commented 2 years ago

Workaround

I went with just hard-coding the validation_token value into the "azurerm_dns_txt_record" "txt-root" via a variable:

variable "txt_validation_token" {
  description = "Manual override of the Custom Domain TXT validation token until https://github.com/hashicorp/terraform-provider-azurerm/issues/14750 is fixed"
  type        = string
  nullable    = true
  default     = null
}
resource "azurerm_dns_txt_record" "txt-root" {
   resource_group_name = var.resource_group_name
   ttl                 = var.ttl
   record {
-    value = azurerm_static_site_custom_domain.txt-root.validation_token
+    value = var.txt_validation_token != null ? var.txt_validation_token : azurerm_static_site_custom_domain.txt-root.validation_token
   }
 }

For the initial go the variable is null so we actually read the token (as it's available) and after the validation_token is generated and validation is secure (and the validation_token gone) I populate the variable with it's value manually after extracting it from the state. Not great but at least temporarily I can proceed and not have to go back to the Azure Portal

rdeveen commented 2 years ago

After the validation of the domain name, the TXT record can be removed. Because the validation_token is no longer there, the record can also be removed. Keeping it in the Terraform state is, I think, not necessary anymore.

lukelengl commented 2 years ago

Workaround

I went with just hard-coding the validation_token value into the "azurerm_dns_txt_record" "txt-root" via a variable:

variable "txt_validation_token" {
  description = "Manual override of the Custom Domain TXT validation token until https://github.com/hashicorp/terraform-provider-azurerm/issues/14750 is fixed"
  type        = string
  nullable    = true
  default     = null
}
resource "azurerm_dns_txt_record" "txt-root" {
   resource_group_name = var.resource_group_name
   ttl                 = var.ttl
   record {
-    value = azurerm_static_site_custom_domain.txt-root.validation_token
+    value = var.txt_validation_token != null ? var.txt_validation_token : azurerm_static_site_custom_domain.txt-root.validation_token
   }
 }

For the initial go the variable is null so we actually read the token (as it's available) and after the validation_token is generated and validation is secure (and the validation_token gone) I populate the variable with it's value manually after extracting it from the state. Not great but at least temporarily I can proceed and not have to go back to the Azure Portal

After a few hours of debugging this same problem and assuming it was that PEBCAK, but then I discovered this GitHub issue. Thank you for posting this problem and the workaround @piotr-lasota !!! Much appreciated! ❀️

lukelengl commented 2 years ago

After the validation of the domain name, the TXT record can be removed. Because the validation_token is no longer there, the record can also be removed. Keeping it in the Terraform state is, I think, not necessary anymore.

To this point, I tried a different workaround. Since the TXT record is no longer needed after validation, and this problem only occurs after validation happens, I changed my resource to this:

resource "azurerm_dns_txt_record" "client_subdomain_validation" {
  name                = "_dnsauth.mydomain"
  zone_name           = azurerm_dns_zone.dns.name
  resource_group_name = azurerm_dns_zone.dns.resource_group_name
  ttl                 = 300
  record {
    value = azurerm_static_site_custom_domain.client.validation_token == "" ? "validated" : azurerm_static_site_custom_domain.client.validation_token
  }
}
github-actions[bot] commented 4 months ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.