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.59k stars 4.63k forks source link

Azure Function doesn't accept Key Vault Reference for Storage Connection String for consumption plan #7434

Open callppatel opened 4 years ago

callppatel commented 4 years ago

When we creating the Azure Function by referring Storage connection String as "Key Vault" String, we are getting below error message. Thru UI we can update connection string with Key Vault Reference but thru Terraform we can't.

Error message Error: web.AppsClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: Code="BadRequest" Message="The parameter 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' has an invalid value. Details: Cannot specify key vault references not referencing User Assigned Identity on Create Site." Details=[{"Message":"The parameter 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' has an invalid value. Details: Cannot specify key vault references not referencing User Assigned Identity on Create Site."},{"Code":"BadRequest"},{"ErrorEntity":{"Code":"BadRequest","ExtendedCode":"01033","Message":"The parameter 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' has an invalid value. Details: Cannot specify key vault references not referencing User Assigned Identity on Create Site.","MessageTemplate":"The parameter '{0}' has an invalid value. Details: {1}.","Parameters":["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING","Cannot specify key vault references not referencing User Assigned Identity on Create Site"]}}]

This could be related to below issues: https://github.com/microsoft/vscode-azurefunctions/issues/1934 https://github.com/Azure/azure-functions-host/issues/5306

Community Note

Terraform (and AzureRM Provider) Version

Terraform v0.12.23

Affected Resource(s)

Terraform Configuration Files

storage_connection_string = "@Microsoft.KeyVault(SecretUri=${azurerm_key_vault_secret.this.id})"

# Copy-paste your Terraform configurations here - for large Terraform configs,
# please use a service like Dropbox and share a link to the ZIP file. For
# security, you can also encrypt the files using our GPG public key: https://keybase.io/hashicorp

Debug Output

Error: web.AppsClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: Code="BadRequest" Message="The parameter 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' has an invalid value. Details: Cannot specify key vault references not referencing User Assigned Identity on Create Site." Details=[{"Message":"The parameter 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' has an invalid value. Details: Cannot specify key vault references not referencing User Assigned Identity on Create Site."},{"Code":"BadRequest"},{"ErrorEntity":{"Code":"BadRequest","ExtendedCode":"01033","Message":"The parameter 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' has an invalid value. Details: Cannot specify key vault references not referencing User Assigned Identity on Create Site.","MessageTemplate":"The parameter '{0}' has an invalid value. Details: {1}.","Parameters":["WEBSITE_CONTENTAZUREFILECONNECTIONSTRING","Cannot specify key vault references not referencing User Assigned Identity on Create Site"]}}]

Panic Output

Expected Behavior

Actual Behavior

Steps to Reproduce

  1. terraform apply

Important Factoids

References

neil-yechenwei commented 4 years ago

@callppatel, thanks for opening this issue. Could you provide your tf config that triggers this issue for further investigation? Thanks.

callppatel commented 4 years ago

@callppatel, thanks for opening this issue. Could you provide your tf config that triggers this issue for further investigation? Thanks.

locals {
  loc_short_name = {
    "centralus"     = "usc"
    "eastus2"       = "use2"
    "northeurope"   = "eun"
    "westeurope"    = "euw"
    "eastasia"      = "ase"
    "southeastasia" = "asse"
  }

  trimmed_loc = local.loc_short_name[data.azurerm_resource_group.this.location]
  # Name
  default_application_settings = {
    FUNCTIONS_WORKER_RUNTIME = var.function_language
  }
}

# Getting the client config with current Service Principal
data "azurerm_client_config" "current" {}

data "azurerm_resource_group" "this" {
  name = var.resource_group
}

data "azurerm_key_vault" "this" {
  name                = var.keyvault_name
  resource_group_name = data.azurerm_resource_group.this.name
}

resource "azurerm_storage_account" "this" {
  resource_group_name       = data.azurerm_resource_group.this.name
  name                      = "testvaluenameforstrg"
  location                  = data.azurerm_resource_group.this.location
  account_tier              = "Standard"
  account_replication_type  = "LRS"
  account_kind              = "StorageV2"
  access_tier               = "Hot"
  is_hns_enabled            = false
  enable_https_traffic_only = true
}

resource "azurerm_app_service_plan" "this" {
  name                = var.function_name
  location            = data.azurerm_resource_group.this.location
  resource_group_name = data.azurerm_resource_group.this.name
  kind                = "FunctionApp"
  sku {
    tier = "Dynamic"
    size = "Y1"
  }
}

resource "azurerm_function_app" "this" {
  name                      = var.function_name
  location                  = data.azurerm_resource_group.this.location
  resource_group_name       = data.azurerm_resource_group.this.name
  app_service_plan_id       = azurerm_app_service_plan.this.id 
  storage_connection_string = "@Microsoft.KeyVault(SecretUri=${azurerm_key_vault_secret.this.id})"
  identity {
    type = "SystemAssigned"
  }

  app_settings = merge(
    local.default_application_settings,
    var.function_app_application_settings,
  )
  site_config {
    always_on = false
  }
  dynamic "connection_string" {
    for_each = var.connection_strings
    content {
      name  = connection_string.value.name
      type  = connection_string.value.type
      value = connection_string.value.value
    }
  }

  auth_settings {
    enabled = true
    active_directory {
      client_id = var.auth_client_id
      allowed_audiences = var.auth_allowed_audiences
    }
    default_provider = "AzureActiveDirectory"
  }
  depends_on = [azurerm_key_vault_secret.this]
}
## Storing the Storage Account Key to Key Vault
resource "azurerm_key_vault_secret" "this" {
  name         = var.function_name
  value        = azurerm_storage_account.this.primary_connection_string
  key_vault_id = data.azurerm_key_vault.this.id
  depends_on   = [azurerm_storage_account.this]
}

resource "azurerm_key_vault_access_policy" "this" {
  count        = 1
  key_vault_id = data.azurerm_key_vault.this.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = lookup(azurerm_function_app.this.identity[0], "principal_id")

  secret_permissions = [
    "get", "list"
  ]
  depends_on = [azurerm_function_app.this]
}

provider "azurerm" {
  version = "=2.5.0"
  features {}
}

variable.tf

variable "resource_group" {
  description = "Resource Group where the resources will be created."
  type        = string
}

variable "function_name" {
  description = "Specifies the name of the Key Vault, it will be prefix with CIO Naming convention. Changing this forces a new resource to be created."
  type        = string
}

variable "keyvault_name" {
  description = "Specifies the name of the Key Vault, it will be prefix with CIO Naming convention. Changing this forces a new resource to be created."
  type        = string
}

variable "function_app_application_settings" {
  description = "Function App application settings"
  type        = map(string)
  default     = {}
}

variable "function_language" {
  description = "Language of the Function App, can be \"dotnet\", \"node\" or \"python\""
  type        = string
  default     = "dotnet"
}

variable "connection_strings" {
  description = "Definition of the dedicated plan to use"
  type = list(object({
    name  = string # The name of the Connection String
    type  = string # The type of the Connection String. Possible values are APIHub, Custom, DocDb, EventHub, MySQL, NotificationHub, PostgreSQL, RedisCache, ServiceBus, SQLAzure and SQLServer.
    value = string # The value for the Connection String.
  }))
  default = []
}
variable "auth_client_id" {
  type        = string
  description = "Azure AAD Authentication's client ID which need to be added"
}

variable "auth_allowed_audiences" {
  type        = list(string)
  description = "Azure AAD allowed audiance"
  default     = []
}
callppatel commented 4 years ago

Let me know if need any info, by looking the link which I mentioned in my original request. It looks like API issue, because I can see the same issue is reported when they do deploy thru vscode as well.

I am able to update above mention environment variable "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" with Key Vault Reference thru UI.

neil-yechenwei commented 4 years ago

Yes. I assume you're right. I raised an issue on Azure/azure-rest-api-specs#9947

callppatel commented 3 years ago

any update on this.

callppatel commented 2 years ago

This is not resolve yet, but we have workaround, first you let function to be created with without key vault reference with terraform resource.

Once function is created, with null resource (use Azure CLi command) to update the setting like this below

resource "null_resource" "update_setting_consumption_plan" {
  provisioner "local-exec" {
    when        = create
    interpreter = ["pwsh", "-command"]
    command     = <<EOT
      sleep 30
      az functionapp config appsettings set --name ${azurerm_function_app.this.name} --resource-group ${var.resource_group} `
      --settings 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING="@Microsoft.KeyVault(SecretUri=${module.azure_key_vault_secret.secret_identifier})"' `
      'AzureWebJobsStorage="@Microsoft.KeyVault(SecretUri=${module.azure_key_vault_secret.secret_identifier})"' 
    EOT
  }
}
mishaskalova commented 1 year ago

any ETA on the fix for this issue..?