Azure / Azure-Functions

1.11k stars 198 forks source link

Function breaks when custom hostname and function name begin with same letter on Linux Consumption Plan #2096

Open joshlrogers opened 2 years ago

joshlrogers commented 2 years ago

Yes, I know it sounds crazy, but it seems true from all appearances. I have been defeated over the last 2 days trying to bind a hostname to an Azure Linux Function App using a consumption plan and this ultimately seems why.

I have a function app that contains a number of triggers and in particular a Blob trigger. This function app works as intended upon deployment, and by work I mean I can monitor the log stream and see the internal timers of the triggers firing and no exceptions are being reported. This code also works locally against Azurite and the function host. Once I bind this function app to a domain that shares the same first letter as the function app it immediately begins failing. I begin to get an exception continuously showing up in the logs Microsoft.Azure.Storage.StorageException: The specifed resource name contains invalid characters (typo in that exception message btw specifed rather than specified). The entire function app becomes non responsive. In the portal you can't even get a response from the Functions > {Function Name} -> Code + Test, the screen just remains blank for every function.

I am able to resolve this problem by removing the hostname binding or by changing it to a domain that doesn't share the same first letter.

I have a demo that when deployed will break as I laid out: https://github.com/joshlrogers/AzFuncBreakingOnDomainBinding

Reproduction Steps:

  1. Create a resource group (purely for isolation)
  2. Create two storage accounts (I don't know if this is necessary but it mirrors my situation)
  3. Create a Linux Consumption function app pointed to the storage accounts in Step 2
  4. Add an app setting called Azure_Storage_ConnectionString and set it to the storage account not used in step 3
  5. Create a domain with a name that shares the first letter as the name of the function app created in step 3
  6. Bind that domain to the function app created in step 3
  7. Deploy the demo code to the function app
    • func azure functionapp publish {FunctionAppName} --csharp --dotnet-cli-params -- "-c Release"
  8. Attempt to hit the HTTP trigger at HTTP://{Your domain name}/api/ExampleHttpTrigger
  9. Observe that it doesn't work
  10. You can add an application insights instance and attach it to the function app and then observe the log stream and notice that nothing is being logged

At this point, you can now remove the domain binding and it will begin working or change it to any other domain that starts with a different first character and it will begin working.

🤯

{
@t:"2021-10-28T01:55:00.5177239Z",
@mt:"An unhandled exception has occurred. Host is shutting down.",
@l:"Error",
@x:"Microsoft.Azure.Storage.StorageException: The specifed resource name contains invalid characters.\
   at Microsoft.Azure.Storage.Core.Executor.Executor.ExecuteAsync[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token)\
   at Microsoft.Azure.WebJobs.Host.Queues.Listeners.QueueListener.ExecuteAsync(CancellationToken cancellationToken) in C:\\projects\\azure-webjobs-sdk-rqm4t\\src\\Microsoft.Azure.WebJobs.Extensions.Storage\\Queues\\Listeners\\QueueListener.cs:line 193\
   at Microsoft.Azure.WebJobs.Host.Timers.TaskSeriesTimer.RunAsync(CancellationToken cancellationToken) in C:\\projects\\azure-webjobs-sdk-rqm4t\\src\\Microsoft.Azure.WebJobs.Host\\Timers\\TaskSeriesTimer.cs:line 147\
Request Information\
RequestID:83c0c9dc-c003-003d-549e-cb28bc000000\
RequestDate:Thu, 28 Oct 2021 01:55:00 GMT\
StatusMessage:The specifed resource name contains invalid characters.\
ErrorCode:InvalidResourceName\",
SourceContext:"Microsoft.Azure.WebJobs.Script.WebHost.WebScriptHostExceptionHandler",
MachineName:"SandboxHost-637709807952401124",
ThreadId:19
}

Terraform code for app:

resource "azurerm_app_service_plan" "functions" {
  name                = "${var.role}-${var.environment}-asp"
  location            = var.region
  resource_group_name = var.resource_group_name
  kind                = "FunctionApp"
  reserved            = true

  sku {
    tier = "Dynamic"
    size = "Y1"
  }

  tags = {
    environment = var.environment
    service     = var.role
  }
}

resource "azurerm_function_app" "functions" {
  name                       = "${var.role}-${var.environment}-func"
  app_service_plan_id        = azurerm_app_service_plan.functions.id
  location                   = var.region
  os_type                    = "linux"
  resource_group_name        = var.resource_group_name
  storage_account_name       = var.storage_account_name
  storage_account_access_key = var.storage_account_access_key
  version                    = "~3"
  https_only                 = true

  app_settings = {
    ASPNETCORE_ENVIRONMENT         = "Production"
    LANG                           = "en_US.UTF-8"
    FUNCTIONS_WORKER_RUNTIME       = "dotnet",
    APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.functions.instrumentation_key,
  }
v-anvari commented 2 years ago

Hi @joshlrogers , Can you please further explain line 5 . What domain are you referring to, do you have a doc link that you are trying to follow. and what is the name? Create a domain with a name that shares the first letter as the name of the function app created in step 3

joshlrogers commented 2 years ago

Yes, @v-anvari , you can reproduce by binding a function to any custom domain name that shares the same first letter as the function name. For instance, I have a function name called domainbreakdemo that is hosting the example code in my original post in the Central US region. I have a custom domain bound to it at d.catchco.io. Since they both start with d the function will never fully start. The only function I can get to sporadically respond is the HttpTrigger and if you just sit and hit refresh on it over and over you can see that it only responds a few times then stops then responds a few times and stops.

If I change the domain to any other domain that doesn't start with a d it will begin working.

v-bbalaiagar commented 2 years ago

Hi @joshlrogers, Thank you for your feedback! We will investigate this further and update you with the findings.