Posted on Leave a comment

Add log analytics workspace to Azure app service – Terraform

Most times you will need to store logs for your azure resources in order to troubleshoot when things do not work as expected. Diagnostic settings for an app service can be enabled from the pane under Monitoring.

Then you should configure the diagnostic settings that will point which logs should be forwarded.

You can choose from the available categories shown below.

Lets now discover how we can enable diagnostic settings for an app service using terraform.

Create a file for example diagnostic_settings.tf and apply. The below configuration will enable all diagnostic settings categories.

resource "azurerm_monitor_diagnostic_setting" "diag_settings" {
  name               = "diag-settings"
  target_resource_id = azurerm_windows_web_app.app_service1.id
  log_analytics_workspace_id = local.log_analytics_workspace_id
  
  log {
    category = "AppServiceHTTPLogs"
    enabled  = true

    retention_policy {
      enabled = false
    }
  }

    log {
    category = "AppServiceConsoleLogs"
    enabled  = true

    retention_policy {
      enabled = false
    }
  }

    log {
    category = "AppServiceAppLogs"
    enabled  = true

    retention_policy {
      enabled = false
    }
  }

    log {
    category = "AppServiceAuditLogs"
    enabled  = true

    retention_policy {
      enabled = false
    }
  }

    log {
    category = "AppServiceIPSecAuditLogs"
    enabled  = true

    retention_policy {
      enabled = false
    }
  }

     log {
    category = "AppServicePlatformLogs"
    enabled  = true

    retention_policy {
      enabled = false
    }
  }

  metric {
    category = "AllMetrics"

    retention_policy {
      enabled = false
      days = 30
    }
  }

}

You can also perform the same using a loop and a local variable in order to minimize code and make it more readable.

Assign a new variable inside your locals.tf file.

 log_analytics_log_categories     = ["AppServiceHTTPLogs", "AppServiceConsoleLogs","AppServiceAppLogs","AppServiceAuditLogs","AppServiceIPSecAuditLogs","AppServicePlatformLogs"]

Then perform terraform apply.

resource "azurerm_monitor_diagnostic_setting" "diag_settings" {
  name               = "diag-rule"
  target_resource_id = azurerm_windows_web_app.app_service1.id
  log_analytics_workspace_id = local.log_analytics_workspace_id
  
  dynamic "log" {
    iterator = entry
    for_each = local.log_analytics_log_categories
    content {
        category = entry.value
        enabled  = true

        retention_policy {
      enabled = false
        }
    }
   
  }

  metric {
    category = "AllMetrics"

    retention_policy {
      enabled = false
      days = 30
    }
  }

}

After applying terraform all the settings will be enabled.

Posted on Leave a comment

Enable Diagnostic settings for Azure App service using terraform loop

Imagine that you want to enable diagnostic settings for multiple app services on Azure using terraform. The required options can be located under Monitoring tab.

A appropriate rule option should be created to indicate where the logs should be sent. 

The available categories can be located below and I will instruct terraform to enable them all.

In order to accomplish that through terraform I used a loop. The depends_on keyword is used because firstly the app services should be created and then the diagnostic settings for them. Create a file like app_diagnostics.tf and place it inside your terraform working directory.

resource "azurerm_monitor_diagnostic_setting" "diag_settings_app" {
  depends_on = [ azurerm_windows_web_app.app_service1,azurerm_windows_web_app.app_service2 ]
  count = length(local.app_service_ids)
  name               = "diag-rule"
  target_resource_id = local.app_service_ids[count.index]
  log_analytics_workspace_id = local.log_analytics_workspace_id
  
  dynamic "log" {
    iterator = entry
    for_each = local.log_analytics_log_categories
    content {
        category = entry.value
        enabled  = true

        retention_policy {
      enabled = false
        }
    }
   
  }

  metric {
    category = "AllMetrics"

    retention_policy {
      enabled = false
      days = 30
    }
  }

}

Inside locals.tf I have created a variable that holds the app services ids, the log analytics workspace ID on which the logs will be sent and also the categories which I want to enable on Diagnostics. As shown on the first screenshot all the categories are selected.

locals {

 log_analytics_workspace_id = "/subscriptions/.../geralexgr-logs" 
 log_analytics_log_categories     = ["AppServiceHTTPLogs", "AppServiceConsoleLogs","AppServiceAppLogs","AppServiceAuditLogs","AppServiceIPSecAuditLogs","AppServicePlatformLogs"]

app_service_ids = [azurerm_windows_web_app.app_service1.id,azurerm_windows_web_app.app_service2.id]
}

As a result the loop will enable for every app service you add on app_service_ids each Diagnostic category placed on log_analytics_log_categories variable.

Posted on Leave a comment

The for_each value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created.

When working with terraform loops you may encounter the error that is shown below.

The “for_each” value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created.

I faced this particular issue when I tried to dynamically create a azurerm_monitor_diagnostic_setting resource for multiple web apps.

The for_each code is shown below:

resource "azurerm_monitor_diagnostic_setting" "diag_settings_app" {
  depends_on = [ azurerm_windows_web_app.app_service1,azurerm_windows_web_app.app_service2 ]
  for_each = toset(local.app_service_ids)
  name               = "diag-rule"
  target_resource_id = each.value
  log_analytics_workspace_id = local.log_analytics_workspace_id
  
  dynamic "log" {
    iterator = entry
    for_each = local.log_analytics_log_categories
    content {
        category = entry.value
        enabled  = true

        retention_policy {
      enabled = false
        }
    }
   
  }

  metric {
    category = "AllMetrics"

    retention_policy {
      enabled = false
      days = 30
    }
  }

}

The local.app_service_ids defines the app services IDs.

app_service_ids = [azurerm_windows_web_app.app_service1.id,azurerm_windows_web_app.app_service2.id]

In order to override this issue I used count loop instead.

resource "azurerm_monitor_diagnostic_setting" "diag_settings_app" {
  depends_on = [ azurerm_windows_web_app.app_service1,azurerm_windows_web_app.app_service2 ]
  count = length(local.app_service_ids)
  name               = "diag-rule"
  target_resource_id = local.app_service_ids[count.index]
  log_analytics_workspace_id = local.log_analytics_workspace_id
  
  dynamic "log" {
    iterator = entry
    for_each = local.log_analytics_log_categories
    content {
        category = entry.value
        enabled  = true

        retention_policy {
      enabled = false
        }
    }
   
  }

  metric {
    category = "AllMetrics"

    retention_policy {
      enabled = false
      days = 30
    }
  }

}

terraform apply will then work:

Posted on Leave a comment

Creating Windows/Linux Web App terraform: (Site Name “” / Resource Group “”): web.AppsClient#CreateOrUpdate: Failure sending request: StatusCode=0 — Original Error: autorest/azure: Service returned an error. Status=

I was struggling to create an app service using Terraform with the error shown below. I could not find a way to resolve this as the error message was difficult to interpret.

Creating Windows/Linux Web App: (Site Name "" / Resource Group ""): web.AppsClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: autorest/azure: Service returned an error. Status=<nil> <nil>

The code for the service plan was simple enough and I was so confused about this behavior.

resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.resource_group_location
}

resource "azurerm_service_plan" "app_service_plan" {
  name                = var.app_service_plan_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  os_type             = "Windows"
  sku_name            = "F1"

}

resource "azurerm_windows_web_app" "app_service" {
  name                = var.app_service_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  service_plan_id = azurerm_service_plan.app_service_plan.id

  site_config {
    use_32_bit_worker = true
  }
}

In order to resolve I enabled detailed debugging on terraform. As I was using Windows I used on powershell

$Env:TF_LOG = "TRACE"

and then I tried to run terraform apply again.

Terraform will provide detailed information about the running commands, so that I was able to determine the error.

Voila!

AlwaysOn should be disabled as there is a conflict.

In the documentation of azure web app one can find that always on is disabled by default however it does not seem to be correct.

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/windows_web_app#site_config

In order to resolve you should add the always_one variable to false.

  site_config {
    use_32_bit_worker = true
    always_on = false
  }

Then you can run terraform apply again, and the resource will be created.

Enable debugging on terraform for your OS:

https://support.hashicorp.com/hc/en-us/articles/360001113727-Enabling-debug-and-trace-run-logs-in-Terraform-CLI-Cloud-or-Enterprise

Update

I perfomed a PR in order to correct the wrong documentation and it has been merged. As a result the documentation will be correct counting from the next deploy.

[Docs fix]Correct docs for resource azurerm_windows_web_app by geralexgr · Pull Request #17051 · hashicorp/terraform-provider-azurerm (github.com)