Home > database >  Can not get web app to write to Azure Storage Blob in Terraform
Can not get web app to write to Azure Storage Blob in Terraform

Time:10-08

I'm having an issue with getting my Web App to write to my Storage blob in Terraform in Azure.

As far as I can tell I have created everything, all I want it to do is send some .Net Log Files there in blob format. The connection is to happen through Key Vault, I have specified a key and made the relevant key vault policy.

Please see my code bellow everything builds but I get no logs dumped in the Storage Account. Do I have to create a storage blob or does the web app do that? I created one before but then nothing wrote to it.

Provider:

# Terraform Block
terraform {
  required_version = ">= 1.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 2.0"
    }
    random = {
      source  = "hashicorp/random"
      version = ">= 3.0"
    }
  }
  #Terraform State Storage Account
  backend "azurerm" {}
}

# Providers Block
provider "azurerm" {
  features {}
}

# Random String Resource

resource "random_string" "myrandom" {
  length  = 6
  number  = false
  upper   = false
  special = false
}

Web App:

resource "azurerm_app_service_plan" "websiteappserviceplan" {
  name                = "appserviceplan-dgyn27h2dfoyojc"
  location            = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  resource_group_name = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name

  sku {
    tier = "Standard"
    size = "B1"
  }
}

resource "azurerm_app_service" "website_app" {
  name                = var.website_name
  location            = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  resource_group_name = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name
  app_service_plan_id = azurerm_app_service_plan.websiteappserviceplan.id

  #storage_account {
    #name       = azurerm_storage_account.website_logs_key.name
    #type       = "AzureBlob" 
    #access_key = lookup(azurerm_storage_account.value,"access_key")
  #}

  app_settings = {
    "KEY_VAULT_URL"                        = azurerm_key_vault.nscsecrets.vault_uri
    "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL" = azurerm_storage_container.website_logs_container.name
    "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS" = 365
  }

  connection_string {
    name  = "StorageAccount"
    type  = "Custom"
    value = azurerm_storage_account.website_log_storage.primary_access_key
  }

  identity {
    type = "SystemAssigned"
  }
}

Storage Account:

resource "azurerm_storage_account" "website_log_storage" {
  name                     = "cicweblogsstorageacc"
  resource_group_name      = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name
  location                 = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  account_tier             = "Standard"
  account_replication_type = "LRS"

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_storage_container" "website_logs_container" {
  name                  = "${var.website_name}-cont"
  storage_account_name  = azurerm_storage_account.website_log_storage.name
  container_access_type = "private"
}

#resource "azurerm_storage_blob" "website_logs_blob" {
# name                   = "website-logs.zip"
# storage_account_name   = azurerm_storage_account.website_log_storage.name
# storage_container_name = azurerm_storage_container.website_logs_container.name
# type                   = "Block"
#}

resource "azurerm_storage_account_customer_managed_key" "website_log_key" {
  depends_on = [azurerm_key_vault_access_policy.website_logs_storage_accesspolicy,
    azurerm_key_vault_key.website_logs_key
  ]
  storage_account_id = azurerm_storage_account.website_log_storage.id
  key_vault_id       = azurerm_key_vault.nscsecrets.id
  key_name           = azurerm_key_vault_key.website_logs_key.name

}

Key Vault:

// This gets the Azure AD Tenant ID information to deploy for KeyVault. 
resource "azurerm_key_vault" "nscsecrets" {
  name                       = "${var.key_vault_name}-${random_string.myrandom.id}"
  resource_group_name        = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name
  location                   = azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  sku_name                   = "standard"
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days = 7
  purge_protection_enabled   = true

}

resource "azurerm_key_vault_access_policy" "client" { // This is for AD Users Logged into Azure to give them the right access when creating resources. 
  key_vault_id        = azurerm_key_vault.nscsecrets.id
  tenant_id           = data.azurerm_client_config.current.tenant_id
  object_id           = data.azurerm_client_config.current.object_id
  secret_permissions  = ["Backup", "Delete", "Get", "List", "Purge", "Recover", "Restore", "Set", ]
  key_permissions     = ["Backup", "Create", "Decrypt", "Delete", "Encrypt", "Get", "Import", "List", "Purge", "Recover", "Restore", "Sign", "UnwrapKey", "Update", "Verify", "WrapKey", ]
  storage_permissions = ["Backup", "Delete", "DeleteSAS", "Get", "GetSAS", "List", "ListSAS", "Purge", "Recover", "RegenerateKey", "Restore", "Set", "SetSAS", "Update", ]
}

resource "azurerm_key_vault_access_policy" "service_principal" { // This is for the Service Principal in the pipeline to be able to make changes to Key Vault. 
  key_vault_id        = azurerm_key_vault.nscsecrets.id
  tenant_id           = data.azurerm_client_config.current.tenant_id
  object_id           = data.azuread_service_principal.current.object_id
  secret_permissions  = ["Backup", "Delete", "Get", "List", "Purge", "Recover", "Restore", "Set", ]
  key_permissions     = ["Backup", "Create", "Decrypt", "Delete", "Encrypt", "Get", "Import", "List", "Purge", "Recover", "Restore", "Sign", "UnwrapKey", "Update", "Verify", "WrapKey", ]
  storage_permissions = ["Backup", "Delete", "DeleteSAS", "Get", "GetSAS", "List", "ListSAS", "Purge", "Recover", "RegenerateKey", "Restore", "Set", "SetSAS", "Update", ]
}

resource "azurerm_key_vault_access_policy" "website_accesspolicy" {
  key_vault_id       = azurerm_key_vault.nscsecrets.id
  tenant_id          = azurerm_app_service.website_app.identity[0].tenant_id
  object_id          = azurerm_app_service.website_app.identity[0].principal_id
  secret_permissions = ["get"]
}

resource "azurerm_key_vault_access_policy" "website_logs_storage_accesspolicy" { // This is for the Storage Account for Website Logs. 
  key_vault_id       = azurerm_key_vault.nscsecrets.id
  tenant_id          = data.azurerm_client_config.current.tenant_id
  object_id          = azurerm_storage_account.website_log_storage.identity[0].principal_id
  key_permissions    = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify", ]
  secret_permissions = ["Backup", "Delete", "Get", "List", "Purge", "Recover", "Restore", "Set", ]
}

resource "azurerm_key_vault_key" "website_logs_key" {
  name         = "website-logs-key"
  key_vault_id = azurerm_key_vault.nscsecrets.id

  key_type = "RSA"
  key_size = 2048
  key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey", ]

  depends_on = [
    azurerm_key_vault_access_policy.client,
    azurerm_key_vault_access_policy.website_logs_storage_accesspolicy
  ]

}

CodePudding user response:

I tested this on my environment by doing some changes to your code and it worked for me .

  1. There is no need to provide these values in app settings i.e. "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL" & "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS" as when you are enabling logs they will get automatically populated.

  2. You should use

      connection_string {
        name  = "StorageAccount"
        type  = "Custom"
        value = azurerm_storage_account.website_log_storage.primary_connection_string
    
      }
    

    Instead of

      connection_string {
        name  = "StorageAccount"
        type  = "Custom"
        value = azurerm_storage_account.website_log_storage.primary_access_key
      }
    

So, after the modifications and some additions the overall code will be like below :

provider "azurerm" {
  features {}
}
provider "random"{}
provider "time" {}
resource "random_string" "myrandom" {
  length  = 6
  number  = false
  upper   = false
  special = false
}
data "azurerm_client_config" "current"{}
data "azurerm_resource_group" "Classroom_In_The_Cloud_Terraform"{
    name="yourresourcegroup"
}
variable "website_name" {
  default = "ansuman-app"
}

// This gets the Azure AD Tenant ID information to deploy for KeyVault. 
resource "azurerm_key_vault" "nscsecrets" {
  name                       = "${var.website_name}-${random_string.myrandom.id}"
  resource_group_name        = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name
  location                   = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  sku_name                   = "standard"
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days = 7

}

resource "azurerm_key_vault_access_policy" "client" { // This is for AD Users Logged into Azure to give them the right access when creating resources. 
  key_vault_id        = azurerm_key_vault.nscsecrets.id
  tenant_id           = data.azurerm_client_config.current.tenant_id
  object_id           = data.azurerm_client_config.current.object_id
  secret_permissions  = ["Backup", "Delete", "Get", "List", "Purge", "Recover", "Restore", "Set", ]
  key_permissions     = ["Backup", "Create", "Decrypt", "Delete", "Encrypt", "Get", "Import", "List", "Purge", "Recover", "Restore", "Sign", "UnwrapKey", "Update", "Verify", "WrapKey", ]
  storage_permissions = ["Backup", "Delete", "DeleteSAS", "Get", "GetSAS", "List", "ListSAS", "Purge", "Recover", "RegenerateKey", "Restore", "Set", "SetSAS", "Update", ]
}


resource "azurerm_key_vault_access_policy" "website_accesspolicy" {
  key_vault_id       = azurerm_key_vault.nscsecrets.id
  tenant_id          = azurerm_app_service.website_app.identity[0].tenant_id
  object_id          = azurerm_app_service.website_app.identity[0].principal_id
  secret_permissions = ["get"]
}

resource "azurerm_key_vault_access_policy" "website_logs_storage_accesspolicy" { // This is for the Storage Account for Website Logs. 
  key_vault_id       = azurerm_key_vault.nscsecrets.id
  tenant_id          = data.azurerm_client_config.current.tenant_id
  object_id          = azurerm_storage_account.website_log_storage.identity[0].principal_id
  key_permissions    = ["get", "create", "delete", "list", "restore", "recover", "unwrapkey", "wrapkey", "purge", "encrypt", "decrypt", "sign", "verify", ]
  secret_permissions = ["Backup", "Delete", "Get", "List", "Purge", "Recover", "Restore", "Set", ]
}

resource "azurerm_key_vault_key" "website_logs_key" {
  name         = "website-logs-key"
  key_vault_id = azurerm_key_vault.nscsecrets.id

  key_type = "RSA"
  key_size = 2048
  key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey", ]

  depends_on = [
    azurerm_key_vault_access_policy.client,
    azurerm_key_vault_access_policy.website_logs_storage_accesspolicy
  ]

}

resource "azurerm_storage_account" "website_log_storage" {
  name                     = "ansumanstorageacc12345"
  resource_group_name      = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name
  location                 = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  account_tier             = "Standard"
  account_replication_type = "GRS"

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_storage_container" "website_logs_container" {
  name                  = "${var.website_name}-cont"
  storage_account_name  = azurerm_storage_account.website_log_storage.name
}
resource "time_rotating" "main" {
  rotation_rfc3339 = null
  rotation_years   = 2

  triggers = {
    end_date = null
    years    = 2
  }
}

data "azurerm_storage_account_blob_container_sas" "website_logs_container_sas" {
  connection_string = azurerm_storage_account.website_log_storage.primary_connection_string
  container_name    = azurerm_storage_container.website_logs_container.name


  start  = timestamp()
  expiry = time_rotating.main.rotation_rfc3339

  permissions {
    read   = true
    add    = true
    create = true
    write  = true
    delete = true
    list   = true
  }

  cache_control       = "max-age=5"
  content_disposition = "inline"
  content_encoding    = "deflate"
  content_language    = "en-US"
  content_type        = "application/json"
}

resource "azurerm_app_service_plan" "websiteappserviceplan" {
  name                = "appserviceplan-dgyn27h2dfoyojc"
  location            = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  resource_group_name = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name

  sku {
    tier = "Standard"
    size = "B1"
  }
}

resource "azurerm_app_service" "website_app" {
  name                = var.website_name
  location            = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.location
  resource_group_name = data.azurerm_resource_group.Classroom_In_The_Cloud_Terraform.name
  app_service_plan_id = azurerm_app_service_plan.websiteappserviceplan.id

  app_settings = {
    "KEY_VAULT_URL"                        = azurerm_key_vault.nscsecrets.vault_uri
  }

  site_config {
  always_on = true
  dotnet_framework_version = "v5.0"
  app_command_line         = "dotnet EventManagement.Web.dll"
  
  }
  logs{
    detailed_error_messages_enabled = true
    failed_request_tracing_enabled = true
    application_logs {
      azure_blob_storage {
        level="Information"
        sas_url = format("https://${azurerm_storage_account.website_log_storage.name}.blob.core.windows.net/${azurerm_storage_container.website_logs_container.name}%s", data.azurerm_storage_account_blob_container_sas.website_logs_container_sas.sas)
        retention_in_days = 365
      }
    }
    http_logs {
      azure_blob_storage{
        sas_url=format("https://${azurerm_storage_account.website_log_storage.name}.blob.core.windows.net/${azurerm_storage_container.website_logs_container.name}%s", data.azurerm_storage_account_blob_container_sas.website_logs_container_sas.sas)
        retention_in_days = 365
      }
    }
  }

  connection_string {
    name  = "StorageAccount"
    type  = "Custom"
    value = azurerm_storage_account.website_log_storage.primary_connection_string
  }

  identity {
    type = "SystemAssigned"
  }
}

Output:

enter image description here

Note : The above is only for terraform modifications. So after the new app service is created and before you deploy the .net app sample code to the app service you have to do some other modifications to the app code like below.

enter image description here

  1. You have add the enter image description here

After this you should be able to see the files as below:

enter image description here

enter image description here

Reference for more information on logs:

Configuring Logging in Azure App Services | Blog (ardalis.com)

  • Related