I'm trying to provision two Azure Virtual Machine Extensions, that have parameters associated to them:
Microsoft Antimalware
Site24x7 Agent for Windows Server Monitoring
I could not find much documentation, but I tried to extract the data from the azure portal under Automation script to see how it was setup in JSON template.
resource "azurerm_virtual_machine_extension" "test1" {
name = "IaaSAntimalware"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
virtual_machine_name = "${azurerm_virtual_machine.testapp.name}"
publisher = "Microsoft.Azure.Security"
type = "IaaSAntimalware"
type_handler_version = "1.5.5.1"
auto_upgrade_minor_version = "true"
settings = <<SETTINGS
{
"AntimalwareEnabled": true,
"RealtimeProtectionEnabled": "true",
"ScheduledScanSettings": {
"isEnabled": "true",
"day": "1",
"time": "120",
"scanType": "Quick"
},
"Exclusions": {
"Extensions": "",
"Paths": "",
"Processes": ""
}
}
SETTINGS
tags {
environment = "${var.tag_env}" }
}
Azure Portal Configuration for Antimalware Extension
azurerm_virtual_machine_extension.test1: 1 error(s) occurred:`
azurerm_virtual_machine_extension.test1: compute.VirtualMachineExtensionsClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="InvalidParameter" Message="The value of parameter typeHandlerVersion is invalid."
Does anyone know the proper syntax?
If you look at this line in the anti-malware-extension-windows-vm sample of azure-quickstart-templates: "typeHandlerVersion": "1.1", you see that you are passing the wrong value for the version.
Related
Working on a code to apply CIS policies set via code to track changes. What I am trying to do is create a custom policy set that contains the policies within CIS Microsoft Azure Foundation Benchmark v1.4.0 initiative definition. I am using the Terraform’s azurerm_policy_set_definition but repeatedly encounter a listofAllowedLocations issue.
Code:
data "azurerm_management_group" "Standard" {
display_name = "Standard"
}
resource "azurerm_policy_set_definition" "cis_benchmark" {
name = var.cis_policy_name
policy_type = "Custom"
display_name = var.cis_display_name
lifecycle {
create_before_destroy = true
}
parameters = local.parameters
metadata = local.metadata
policy_definition_reference {
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988"
parameter_values = <<VALUE
{
"allowedLocation": {"value": ["eastus"]}
}
VALUE
}
}
Error:
Error: creating Policy Set Definition "CIS Benchmark v1.4.0": policy.SetDefinitionsClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="MissingPolicyParameter" Message="The policy set definition 'CIS Benchmark v1.4.0' is missing the parameter(s) 'listOfAllowedLocations' as defined in the policy definition 'e765b5de-1225-4ba3-bd56-1ac6695af988'."
│
│ with azurerm_policy_set_definition.cis_benchmark,│ on cisbenchmark.tf line 22, in resource "azurerm_policy_set_definition" "cis_benchmark":
│ 22: resource "azurerm_policy_set_definition" "cis_benchmark" {
│
You misspelled the parameter name allowedLocation, which should be listOfAllowedLocations. Take a close look at the example below, this should solve your issue. I agree it is a bit confusing that the names differ.
resource "azurerm_policy_set_definition" "example" {
name = "testPolicySet"
policy_type = "Custom"
display_name = "Test Policy Set"
parameters = <<PARAMETERS
{
"allowedLocations": {
"type": "Array",
"metadata": {
"description": "The list of allowed locations for resources.",
"displayName": "Allowed locations",
"strongType": "location"
}
}
}
PARAMETERS
policy_definition_reference {
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988"
parameter_values = <<VALUE
{
"listOfAllowedLocations": {"value": "[parameters('allowedLocations')]"}
}
VALUE
}
}
I got this example from the official policy_set_definition documentation.
I am trying to code an Azure Data Factory in Terraform, but I am not sure how to code this REST dataset:
{
"name": "RestResource1",
"properties": {
"linkedServiceName": {
"referenceName": "API_Connection",
"type": "LinkedServiceReference"
},
"annotations": [],
"type": "RestResource",
"schema": []
},
"type": "Microsoft.DataFactory/factories/datasets"
}
I don't see one in the azurerm documentation. Can one instead use an azurerm_data_factory_dataset_http resource instead?
azurerm_data_factory_linked_service_rest - Does not currently exist.
azurerm_data_factory_linked_service_web - This only support a web
table and not a REST API endpoint and can't be used with the Azure
integrated runtime.
As I tried to create the linked service using rest and http it always redirected to create a web table using terraform. Hence, for now the fix for this is to use azurerm_data_factory_linked_custom_service.
Here, is the example: How to create a Custom Linked service :
provider "azurerm" {
features{}
}
data "azurerm_resource_group" "example" {
name = "Your Resource Group"
}
data "azurerm_data_factory" "example" {
name = "vipdashadf"
resource_group_name = data.azurerm_resource_group.example.name
}
resource "azurerm_data_factory_linked_custom_service" "example" {
name = "ipdashlinkedservice"
data_factory_id = data.azurerm_data_factory.example.id
type = "RestService"
description = "test for rest linked"
type_properties_json = <<JSON
{
"url": "http://www.bing.com",
"enableServerCertificateValidation": false,
"authenticationType": "Anonymous"
}
JSON
annotations = []
}
resource "azurerm_data_factory_dataset_http" "example" {
name = "apidataset"
resource_group_name = data.azurerm_resource_group.example.name
data_factory_name = data.azurerm_data_factory.example.name
linked_service_name = azurerm_data_factory_linked_custom_service.example.name
relative_url = "http://www.bing.com"
request_body = "foo=bar"
request_method = "POST"
}
Outputs:
Linked Service - ipdashlinkservice type Rest Connector
Dataset: apidataset
You could find the same stated in the GitHub discussion: Support for Azure Data Factory Linked Service for REST API #9431
I am attempting to create an Isolated App Service Environment (ASE) in Azure using Terraform. I have succeeded once and have an ASE running. Attempts to create a second ASE in the same subscription, but within a separate resource group, fail. The only error message available is "An error has occurred". Investigation within Azure Monitoring reveals that the create request which was delivered via an ARM template has encountered a 500 error (Internal Server Error) somewhere in the process of creating the ASE. However, no details are available in the log message to indicate where/when/how Azure encountered the 500 error.
Environment:
azure cli (2.26.1)
terraform (0.14.11)
hashicorp/azurerm provider (2.67.0)
Details
I am creating multiple environments for my project: dev, test, and staging. They are each in separate resource groups within the same Azure subscription. All resources in my terraform are constructed with names unique to the environment/resource group. The intended lifecycle is for infrastructure changes to be deployed to dev, then test, then staging (and eventually a prod environment in a separate subscription). The initial configuration and deployment to dev has succeeded. Attempts to deploy to test, or to deploy a different ASE to dev, fail abjectly with very little feedback.
The original dev ASE is a v1 ASE. I have attempted to create a second ASE in test using the same terraform code. I have also tried creating a v3 ASE in dev (because the v3 will be cheaper). If the v3 ASE deploys successfully I will cut over to it in dev and will use it as the basis for test and stage instead of the v1 ASE. Regardless whether I try to deploy a v1 ASE to a separate resource group, or whether I try to deploy a v3 ASE to the same resource group as the v1 ASE, I get the same error.
This is the Terraform for the v1 ASE, including the subnet which will host it:
resource "azurerm_subnet" "subnet" {
name = "${local.prefix}-subnet"
resource_group_name = var.resource_group_name
virtual_network_name = var.vnet_name
address_prefixes = var.cidrs
enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies
enforce_private_link_service_network_policies = var.enforce_private_link_service_network_policies
dynamic "delegation" {
for_each = var.delegations
content {
name = "${local.prefix}-delegation-${delegation.key}"
service_delegation {
name = delegation.value.name
actions = delegation.value.actions
}
}
}
// List of Service endpoints to associate with the subnet.
service_endpoints = var.service_endpoints
}
resource "azurerm_network_security_group" "nsg" {
name = "${local.prefix}-nsg"
location = var.resource_group_location
resource_group_name = var.resource_group_name
tags = merge(map("Name", "${local.prefix}-nsg"), local.tags)
}
resource "azurerm_subnet_network_security_group_association" "nsg_assoc" {
subnet_id = azurerm_subnet.subnet.id
network_security_group_id = azurerm_network_security_group.nsg.id
}
resource "azurerm_network_security_rule" "ase_mgmt" {
name = "${local.prefix}-ase-mgmt"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
source_address_prefix = "AppServiceManagement"
destination_port_range = "454-455"
destination_address_prefix = var.subnet_cidr
resource_group_name = var.resource_group_name
network_security_group_name = azurerm_network_security_group.nsg.name
}
resource "azurerm_network_security_rule" "ingress" {
for_each = {
for idx, cidr in var.ingress_cidrs : idx => cidr
}
name = "${local.prefix}-ingress-${each.key}"
priority = 200 + each.key
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
source_address_prefix = each.value
destination_port_range = "*"
destination_address_prefix = var.subnet_cidr
resource_group_name = var.resource_group_name
network_security_group_name = azurerm_network_security_group.nsg.name
}
resource "azurerm_app_service_environment" "env" {
name = "${local.prefix}-ase"
subnet_id = azurerm_subnet.subnet.id
pricing_tier = var.pricing_tier
front_end_scale_factor = var.front_scale_factor
internal_load_balancing_mode = "Web, Publishing"
allowed_user_ip_cidrs = var.allowed_user_ip_cidrs
cluster_setting {
name = "DisableTls1.0"
value = "1"
}
depends_on = [
azurerm_network_security_rule.ase_mgmt
]
}
The v3 ASE is configured identically, except for azurerm_app_service_environment.env, which is replaced with:
resource "azurerm_app_service_environment_v3" "env" {
name = "${local.prefix}-ase-v3"
resource_group_name = var.resource_group_name
subnet_id = azurerm_subnet.subnet.id
cluster_setting {
name = "DisableTls1.0"
value = "1"
}
depends_on = [
azurerm_network_security_rule.ase_mgmt
]
}
Results
Terraform generates this ARM request (identifiers have been redacted):
2021/07/19 09:07:44 [TRACE] dag/walk: vertex "root" is waiting for "meta.count-boundary (EachMode fixup)"
2021-07-19T09:07:45.121-0700 [DEBUG] plugin.terraform-provider-azurerm_v2.67.0_x5: AzureRM Request:
PUT /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxxxxx-dev-rg/providers/Microsoft.Web/hostingEnvironments/xxxxxxxx-dev-ase-v3?api-version=2020-06-01 HTTP/1.1
Host: management.azure.com
User-Agent: Go/go1.16.3 (amd64-darwin) go-autorest/v14.2.1 Azure-SDK-For-Go/v55.4.0 web/2020-06-01 HashiCorp Terraform/0.14.11 (+https://www.terraform.io) Terraform Plugin SDK/2.7.0 terraform-provider-azurerm/2.67.0 pid-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Content-Length: 382
Content-Type: application/json; charset=utf-8
X-Ms-Correlation-Request-Id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Accept-Encoding: gzip
{
"kind":"ASEV3",
"location":"centralus",
"properties":
{
"clusterSettings":[{
"name":"DisableTls1.0",
"value":"1"
}],
"name":"xxxxxxxx-dev-ase-v3",
"virtualNetwork":{
"id":"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxxxxx-dev-rg/providers/Microsoft.Network/virtualNetworks/xxxxxxxx-dev-vnet/subnets/xxxxxxxx-dev-ase-v3-ase-subnet",
"subnet":"xxxxxxxx-dev-ase-v3-ase-subnet"
}
},
"tags":{}
}
The error eventually reported by Terraform looks like this in the debug output:
2021/07/19 09:13:53 [DEBUG] azurerm_app_service_environment_v3.env: apply errored, but we're indicating that via the Error pointer rather than returning it: creating App Service Environment: (Hosting Environment Name "xxxxxxxx-dev-ase-v3" / Resource Group "xxxxxxxx-dev-rg"): web.AppServiceEnvironmentsClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: Code="Failed" Message="The async operation failed." AdditionalInfo=[{"Message":"An error has occurred."}]: creating App Service Environment: (Hosting Environment Name "xxxxxxxx-dev-ase-v3" / Resource Group "xxxxxxxx-dev-rg"): web.AppServiceEnvironmentsClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: Code="Failed" Message="The async operation failed." AdditionalInfo=[{"Message":"An error has occurred."}]
Reviewing the logs within Azure Monitor, I find a similarly vague error message. The message is summarized as InternalServerError. The JSON detail is included here for reference:
{
"authorization": {
"action": "Microsoft.Web/hostingEnvironments/write",
"scope": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/xxxxxxxx-dev-rg/providers/Microsoft.Web/hostingEnvironments/xxxxxxxx-dev-ase-v3"
},
"caller": "duffy.gillman#presencepg.com",
"channels": "Operation",
"claims": {
//REDACTED
},
"correlationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"description": "",
"eventDataId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"eventName": {
"value": "EndRequest",
"localizedValue": "End request"
},
"category": {
"value": "Administrative",
"localizedValue": "Administrative"
},
"eventTimestamp": "2021-07-19T15:51:45.4835627Z",
"id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/xxxxxxxx-dev-rg/providers/Microsoft.Web/hostingEnvironments/xxxxxxxx-dev-ase-v3/events/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/ticks/637623067054835627",
"level": "Error",
"operationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"operationName": {
"value": "Microsoft.Web/hostingEnvironments/write",
"localizedValue": "Create or Update App Service Environment"
},
"resourceGroupName": "xxxxxxxx-dev-rg",
"resourceProviderName": {
"value": "Microsoft.Web",
"localizedValue": "Azure Web Sites"
},
"resourceType": {
"value": "Microsoft.Web/hostingEnvironments",
"localizedValue": "Microsoft.Web/hostingEnvironments"
},
"resourceId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/xxxxxxxx-dev-rg/providers/Microsoft.Web/hostingEnvironments/xxxxxxxx-dev-ase-v3",
"status": {
"value": "Failed",
"localizedValue": "Failed"
},
"subStatus": {
"value": "InternalServerError",
"localizedValue": "Internal Server Error (HTTP Status Code: 500)"
},
"submissionTimestamp": "2021-07-19T15:52:29.177138Z",
"subscriptionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"properties": {
"statusCode": "InternalServerError",
"serviceRequestId": null,
"statusMessage": "{\"Message\":\"An error has occurred.\"}",
"eventCategory": "Administrative",
"entity": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/xxxxxxxx-dev-rg/providers/Microsoft.Web/hostingEnvironments/xxxxxxxx-dev-ase-v3",
"message": "Microsoft.Web/hostingEnvironments/write",
"hierarchy": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
},
"relatedEvents": []
}
This to me looks like you have hit some subscription limitations. If you try create the same ASE via the Azure Portal does it provision for you? It will be good to know if you get an error trying to do the same thing via UI, if you did hit the same error in the GUI it will give you a better error message.
I am using terraform version 0.15.5 (and also 0.15.4) to provision an Azure Web App resource. The following configuration works fine:
site_config {
http2_enabled = true
always_on = false
use_32_bit_worker_process = true
}
But when I use use_32_bit_worker_process = false to get the script provision a 64-bit web app, it fails and I get the following error message:
2021-06-03T18:06:55.6392592Z [31m│[0m [0m[1m[31mError: [0m[0m[1mError creating App Service "gfdemogatewayapp" (Resource Group "MASKED"): web.AppsClient#CreateOrUpdate: Failure sending request: StatusCode=0 -- Original Error: autorest/azure: Service returned an error. Status=<nil> <nil>[0m
2021-06-03T18:06:55.6411094Z [31m│[0m [0m
2021-06-03T18:06:55.6426506Z [31m│[0m [0m[0m with azurerm_app_service.gfgatewayapp,
2021-06-03T18:06:55.6427703Z [31m│[0m [0m on main.tf line 274, in resource "azurerm_app_service" "gfgatewayapp":
2021-06-03T18:06:55.6428766Z [31m│[0m [0m 274: resource "azurerm_app_service" "gfgatewayapp" [4m{[0m[0m
2021-06-03T18:06:55.6429584Z [31m│[0m [0m
2021-06-03T18:06:55.6430461Z [31m╵[0m[0m
2021-06-03T18:06:55.6534148Z ##[error]Error: The process '/opt/hostedtoolcache/terraform/0.15.4/x64/terraform' failed with exit code 1
2021-06-03T18:06:55.6548186Z ##[section]Finishing: Terraform approve and apply
Is there something that I am missing or terraform has an issue in provisioning the 64-bit web app resource on Azure?
UPDATE: The full source code
The tier is Standard and the SKU size is "F1".
resource "azurerm_app_service_plan" "gfwebappserviceplan" {
name = var.gatewayserviceplanname
location = "${azurerm_resource_group.gf.location}"
resource_group_name = "${azurerm_resource_group.gf.name}"
sku {
tier = var.gatewayserviceplanskutier
size = var.gatewayserviceplanskusize
}
}
resource "azurerm_app_service" "gfgatewayapp" {
name = var.gatewayappname
location = "${azurerm_resource_group.gf.location}"
resource_group_name = "${azurerm_resource_group.gf.name}"
app_service_plan_id = azurerm_app_service_plan.gfwebappserviceplan.id
app_settings = {
APPINSIGHTS_INSTRUMENTATIONKEY = "${azurerm_application_insights.gfapplicationinsights.instrumentation_key}"
}
site_config {
http2_enabled = true
always_on = false
use_32_bit_worker_process = false
}
}
output "gfgatewayhostname" {
value = "${azurerm_app_service.gfgatewayapp.default_site_hostname}"
description = "Gateway default host name"
}
resource "azurerm_template_deployment" "webapp-corestack" {
# This will make it .NET CORE for Stack property, and add the dotnet core logging extension
name = "AspNetCoreStack"
resource_group_name = "${azurerm_resource_group.gf.name}"
template_body = <<DEPLOY
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"siteName": {
"type": "string",
"metadata": {
"description": "The Azure App Service Name"
}
},
"extensionName": {
"type": "string",
"metadata": {
"description": "The Site Extension Name."
}
},
"extensionVersion": {
"type": "string",
"metadata": {
"description": "The Extension Version"
}
}
},
"resources": [
{
"apiVersion": "2018-02-01",
"name": "[parameters('siteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"properties": {
"name": "[parameters('siteName')]",
"siteConfig": {
"appSettings": [],
"metadata": [
{
"name": "CURRENT_STACK",
"value": "dotnetcore"
}
]
}
}
},
{
"type": "Microsoft.Web/sites/siteextensions",
"name": "[concat(parameters('siteName'), '/', parameters('extensionName'))]",
"apiVersion": "2018-11-01",
"location": "[resourceGroup().location]",
"properties": {
"version": "[parameters('extensionVersion')]"
}
}
]
}
DEPLOY
parameters = {
"siteName" = azurerm_app_service.gfgatewayapp.name
"extensionName" = "Microsoft.AspNetCore.AzureAppServices.SiteExtension"
"extensionVersion" = "3.1.7"
}
deployment_mode = "Incremental"
depends_on = [azurerm_app_service.gfgatewayapp]
}
Since this is the first google result you get when searching for
"AppsClient#CreateOrUpdate: Failure sending request: StatusCode=0"
and that was my error, I will try to help other people that might stumble over this problem.
What I did was migrating a function from azure provider version 2.x to 3.x. Since terraform actually changed the resource type from azurerm_function_app to azurerm_windows_function_app they also changed some properties.
What happened for me was that they added the properties application_insights_key and application_insights_connection_string to the site_config. Before you had to manually (in the app_settings) add a key called APPINSIGHTS_INSTRUMENTATIONKEY.
I used the new setting but forgot to get rid of the manually added key and my function creation was failing with the above (not very verbose if you ask me) error.
Took me a while to figure that out, so that's why I'm sharing this here.
You are getting this error because you are using a F1 tier app service plan. Free or Shared tiers do not have a 64 bit option.
Terraform AzureRM Registry
If setting use_32_bit_worker_process or use_32_bit_worker to true does not help, then try to run terraform with logging. Logging can be enabled by setting the TF_LOG environment variable, e.g.:
$ TF_LOG=debug terraform apply
This will produce a lot of logging, including the HTTP responses from Azure. One of the last logged responses should include more details about the cause. In my case it was because always_on is not supported on the free plan, but is enabled by default:
HTTP/2.0 409 Conflict
<snip>
{"Code":"Conflict","Message":"There was a conflict. AlwaysOn cannot be set for this site as the plan does not allow it. [...]}
Enabling VM diagnostics in Azure is such a pain. I've gotten it working using ARM templates, the Azure PowerShell SDK, and the Azure CLI. But I've been trying for days now to enable VM diagnostics for both Windows and Linux VMs using Terraform and the azurerm_virtual_machine_extension resource. Still not working, ugh!
Here's what I have so far (I've tweaked this a bit to simplify it for this post, so hope I didn't break anything with my manual edits):
resource "azurerm_virtual_machine_extension" "vm-linux" {
count = "${local.is_windows_vm == "false" ? 1 : 0}"
depends_on = ["azurerm_virtual_machine_data_disk_attachment.vm"]
name = "LinuxDiagnostic"
location = "${var.location}"
resource_group_name = "${var.resource_group_name}"
virtual_machine_name = "${local.vm_name}"
publisher = "Microsoft.Azure.Diagnostics"
type = "LinuxDiagnostic"
type_handler_version = "3.0"
auto_upgrade_minor_version = "true"
# The JSON file referenced below was created by running "az vm diagnostics get-default-config", and adding/verifying the "__DIAGNOSTIC_STORAGE_ACCOUNT__" and "__VM_RESOURCE_ID__" placeholders.
settings = <<SETTINGS
{
"ladCfg": "${base64encode(replace(replace(file("${path.module}/.diag-settings/linux_diag_config.json"), "__DIAGNOSTIC_STORAGE_ACCOUNT__", "${module.vm_storage_account.name}"), "__VM_RESOURCE_ID__", "${local.metricsresourceid}"))}",
"storageAccount": "${module.vm_storage_account.name}"
}
SETTINGS
# SAS token below: Do not include the leading question mark, as per https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/diagnostics-linux.
protected_settings = <<SETTINGS
{
"storageAccountName": "${module.vm_storage_account.name}",
"storageAccountSasToken": "${replace(data.azurerm_storage_account_sas.current.sas, "/^\\?/", "")}",
"storageAccountEndPoint": "https://core.windows.net/"
}
SETTINGS
}
resource "azurerm_virtual_machine_extension" "vm-win" {
count = "${local.is_windows_vm == "true" ? 1 : 0}"
depends_on = ["azurerm_virtual_machine_data_disk_attachment.vm"]
name = "Microsoft.Insights.VMDiagnosticsSettings"
location = "${var.location}"
resource_group_name = "${var.resource_group_name}"
virtual_machine_name = "${local.vm_name}"
publisher = "Microsoft.Azure.Diagnostics"
type = "IaaSDiagnostics"
type_handler_version = "1.9"
auto_upgrade_minor_version = "true"
# The JSON file referenced below was created by running "az vm diagnostics get-default-config --is-windows-os", and adding/verifying the "__DIAGNOSTIC_STORAGE_ACCOUNT__" and "__VM_RESOURCE_ID__" placeholders.
settings = <<SETTINGS
{
"wadCfg": "${base64encode(replace(replace(file("${path.module}/.diag-settings/windows_diag_config.json"), "__DIAGNOSTIC_STORAGE_ACCOUNT__", "${module.vm_storage_account.name}"), "__VM_RESOURCE_ID__", "${local.metricsresourceid}"))}",
"storageAccount": "${module.vm_storage_account.name}"
}
SETTINGS
protected_settings = <<SETTINGS
{
"storageAccountName": "${module.vm_storage_account.name}",
"storageAccountSasToken": "${data.azurerm_storage_account_sas.current.sas}",
"storageAccountEndPoint": "https://core.windows.net/"
}
SETTINGS
}
Notice that for both Linux and Windows I'm loading the diagnostics details from a JSON file within the code base, as per the comments. These are the default configs provided by Azure, so they should be valid.
When I deploy these, the Linux VM extension deploys successfully, but in the Azure portal the extension says "Problems detected in generated mdsd configuration". And if I look at the VM's "Diagnostic settings" it says "Error encountered: TypeError: Object doesn't support property or method 'diagnosticMonitorConfiguration'".
The Windows VM extension fails to deploy altogether, saying that it "Failed to read configuration". If I view the extension in the portal it displays the following error:
"code": "ComponentStatus//failed/-3",
"level": "Error",
"displayStatus": "Provisioning failed",
"message": "Error starting the diagnostics extension"
And if I look at the "Diagnostics settings" pane it just hangs with a never-ending ". . ." animation.
However, if I look at the "terraform apply" output for both VM extensions, the decoded settings look exactly as intended, matching the config files with the placeholders correctly replaced.
Any suggestions on how to get this working?
Thanks in advance!
I've gotten the Windows Diagnostics to work 100% so far in our environment. It seems the AzureRM API is very picky about the config being sent. We had been using powershell to enable it, and the same xmlCfg used in powershell DID NOT WORK with terraform.
So far this has worked for us: (The settings/protected_settings names are Case Sensitive! aka xmlCfg works, while xmlcfg does not)
main.cf
#########################################################
# VM Extensions - Windows In-Guest Monitoring/Diagnostics
#########################################################
resource "azurerm_virtual_machine_extension" "InGuestDiagnostics" {
name = var.compute["InGuestDiagnostics"]["name"]
location = azurerm_resource_group.VMResourceGroup.location
resource_group_name = azurerm_resource_group.VMResourceGroup.name
virtual_machine_name = azurerm_virtual_machine.Compute.name
publisher = var.compute["InGuestDiagnostics"]["publisher"]
type = var.compute["InGuestDiagnostics"]["type"]
type_handler_version = var.compute["InGuestDiagnostics"]["type_handler_version"]
auto_upgrade_minor_version = var.compute["InGuestDiagnostics"]["auto_upgrade_minor_version"]
settings = <<SETTINGS
{
"xmlCfg": "${base64encode(templatefile("${path.module}/templates/wadcfgxml.tmpl", { vmid = azurerm_virtual_machine.Compute.id }))}",
"storageAccount": "${data.azurerm_storage_account.InGuestDiagStorageAccount.name}"
}
SETTINGS
protected_settings = <<PROTECTEDSETTINGS
{
"storageAccountName": "${data.azurerm_storage_account.InGuestDiagStorageAccount.name}",
"storageAccountKey": "${data.azurerm_storage_account.InGuestDiagStorageAccount.primary_access_key}",
"storageAccountEndPoint": "https://core.windows.net"
}
PROTECTEDSETTINGS
}
tfvars
InGuestDiagnostics = {
name = "WindowsDiagnostics"
publisher = "Microsoft.Azure.Diagnostics"
type = "IaaSDiagnostics"
type_handler_version = "1.16"
auto_upgrade_minor_version = "true"
}
wadcfgxml.tmpl (I cut out some of the Perf counters for brevity)
<WadCfg>
<DiagnosticMonitorConfiguration overallQuotaInMB="5120">
<DiagnosticInfrastructureLogs scheduledTransferLogLevelFilter="Error"/>
<Metrics resourceId="${vmid}">
<MetricAggregation scheduledTransferPeriod="PT1H"/>
<MetricAggregation scheduledTransferPeriod="PT1M"/>
</Metrics>
<PerformanceCounters scheduledTransferPeriod="PT1M">
<PerformanceCounterConfiguration counterSpecifier="\Processor Information(_Total)\% Processor Time" sampleRate="PT60S" unit="Percent" />
<PerformanceCounterConfiguration counterSpecifier="\Processor Information(_Total)\% Privileged Time" sampleRate="PT60S" unit="Percent" />
<PerformanceCounterConfiguration counterSpecifier="\Processor Information(_Total)\% User Time" sampleRate="PT60S" unit="Percent" />
<PerformanceCounterConfiguration counterSpecifier="\Processor Information(_Total)\Processor Frequency" sampleRate="PT60S" unit="Count" />
<PerformanceCounterConfiguration counterSpecifier="\System\Processes" sampleRate="PT60S" unit="Count" />
<PerformanceCounterConfiguration counterSpecifier="\SQLServer:SQL Statistics\SQL Re-Compilations/sec" sampleRate="PT60S" unit="Count" />
</PerformanceCounters>
<WindowsEventLog scheduledTransferPeriod="PT1M">
<DataSource name="Application!*[System[(Level = 1 or Level = 2)]]"/>
<DataSource name="Security!*[System[(Level = 1 or Level = 2)]"/>
<DataSource name="System!*[System[(Level = 1 or Level = 2)]]"/>
</WindowsEventLog>
</DiagnosticMonitorConfiguration>
</WadCfg>
I finally got the Linux In-Guest Diagnostics to work (LAD). A few notable facts, unlike the windows diagnostics the settings need to be transmitted in json, no base64 encoding. Additionally LAD seems to require a SAS token with the storage account. The normal caveats around AzureRM API being picky about the config, and the settings being Case Sensitive still remain. Here is what is working for me so far..
# Locals
locals {
env = var.workspace[terraform.workspace]
# Use a set/static time to avoid TF from recreating the SAS token every apply, which would then cause it to
# modify/recreate anything that uses it. Not ideal, but the token is for a VERY long time, so it will do for now
sas_begintime = "2019-11-22T00:00:00Z"
sas_endtime = timeadd(local.sas_begintime, "873600h")
}
#########################################################
# VM Extensions - In-Guest Diagnostics
#########################################################
# We need a SAS token for the In-Guest Metrics
data "azurerm_storage_account_sas" "inguestdiagnostics" {
count = (contains(keys(local.env), "InGuestDiagnostics") ? 1 : 0)
connection_string = data.azurerm_storage_account.BootDiagStorageAccount.primary_connection_string
https_only = true
resource_types {
service = true
container = true
object = true
}
services {
blob = true
queue = true
table = true
file = true
}
start = local.sas_begintime
expiry = local.sas_endtime
permissions {
read = true
write = true
delete = true
list = true
add = true
create = true
update = true
process = true
}
}
resource "azurerm_virtual_machine_extension" "inguestdiagnostics" {
for_each = contains(keys(local.env), "InGuestDiagnostics") ? local.env["InGuestDiagnostics"] : {}
depends_on = [azurerm_virtual_machine_extension.dependencyagent]
name = each.value["name"]
location = azurerm_resource_group.resourcegroup.location
resource_group_name = azurerm_resource_group.resourcegroup.name
virtual_machine_name = azurerm_virtual_machine.compute["${each.key}"].name
publisher = each.value["publisher"]
type = each.value["type"]
type_handler_version = each.value["type_handler_version"]
auto_upgrade_minor_version = each.value["auto_upgrade_minor_version"]
settings = templatefile("${path.module}/templates/ladcfg2json.tmpl", { vmid = azurerm_virtual_machine.compute["${each.key}"].id, storageAccountName = data.azurerm_storage_account.BootDiagStorageAccount.name })
protected_settings = <<PROTECTEDSETTINGS
{
"storageAccountName": "${data.azurerm_storage_account.BootDiagStorageAccount.name}",
"storageAccountSasToken": "${replace(data.azurerm_storage_account_sas.inguestdiagnostics.0.sas, "/^\\?/", "")}"
}
PROTECTEDSETTINGS
}
# These variations didn't work for me ..
# "ladCfg": "${templatefile("${path.module}/templates/ladcfgjson.tmpl", { vmid = azurerm_virtual_machine.compute["${each.key}"].id, storageAccountName = data.azurerm_storage_account.BootDiagStorageAccount.name })}",
# - This one get's you Error: "settings" contains an invalid JSON: invalid character '\n' in string literal or Error: "settings" contains an invalid JSON: invalid character 'S' after object key:value pair
# "ladCfg": "${replace(data.local_file.ladcfgjson["${each.key}"].content, "/\\n/", "")}",
# - This one get's you Error: "settings" contains an invalid JSON: invalid character 'S' after object key:value pair
tfvars
workspace = {
TerraformWorkSpaceName = {
compute = {
# Add additional key/objects for additional Compute
computer01 = {
name = "computer01"
}
}
InGuestDiagnostics = {
# Add additional key/objects for each Compute you want to install the InGuestDiagnostics on
computer01 = {
name = "LinuxDiagnostic"
publisher = "Microsoft.Azure.Diagnostics"
type = "LinuxDiagnostic"
type_handler_version = "3.0"
auto_upgrade_minor_version = "true"
}
}
}
}
I couldn't get a template file to work without wrapping the WHOLE thing in jsonencode.
ladcfg2json.tmpl
${jsonencode({
"StorageAccount": "${storageAccountName}",
"ladCfg": {
"sampleRateInSeconds": 15,
"diagnosticMonitorConfiguration": {
"metrics": {
"metricAggregation": [
{
"scheduledTransferPeriod": "PT1M"
},
{
"scheduledTransferPeriod": "PT1H"
}
],
"resourceId": "${vmid}"
},
"eventVolume": "Medium",
"performanceCounters": {
"sinks": "",
"performanceCounterConfiguration": [
{
"counterSpecifier": "/builtin/processor/percentiowaittime",
"condition": "IsAggregate=TRUE",
"sampleRate": "PT15S",
"annotation": [
{
"locale": "en-us",
"displayName": "CPU IO wait time"
}
],
"unit": "Percent",
"class": "processor",
"counter": "percentiowaittime",
"type": "builtin"
}
]
},
"syslogEvents": {
"syslogEventConfiguration": {
"LOG_LOCAL0": "LOG_DEBUG"
}
}
}
}
})}
I hope this helps..
As the question was asked more than a year ago this is more for people like me who are trying this for the first time.
We only use linux vms so this advice applies to that:
protected settings should use PROTECTED_SETTINGS not SETTINGS (which you can see in #rv23 answer above)
From the documentation I am following https://learn.microsoft.com/en-gb/azure/virtual-machines/extensions/diagnostics-linux#protected-settings you can see you need to specify storageAccountSasToken not storageAccountKey:
Here is my redacted version of config (replace all bits in all caps with your own settings ):
resource "azurerm_virtual_machine_extension" "vm_linux_diagnostics" {
count = "1"
name = "NAME"
resource_group_name = "YOUR RESOURCE GROUP NAME"
location = "YOUR LOCATION"
virtual_machine_name = "TARGET MACHINE NAME"
publisher = "Microsoft.Azure.Diagnostics"
type = "LinuxDiagnostic"
type_handler_version = "3.0"
auto_upgrade_minor_version = "true"
settings = <<SETTINGS
{
"StorageAccount": "tfnpfsnhsuk",
"ladCfg": {
"sampleRateInSeconds": 15,
"diagnosticMonitorConfiguration": {
"metrics": {
"metricAggregation": [
{
"scheduledTransferPeriod": "PT1M"
},
{
"scheduledTransferPeriod": "PT1H"
}
],
"resourceId": "VM ID"
},
"eventVolume": "Medium",
"performanceCounters": {
"sinks": "",
.... MORE METRICS - THAT YOU REQUIRE
}
}
}
SETTINGS
protected_settings = <<PROTECTED_SETTINGS
{
"storageAccountName": "YOUR_ACCOUNT_NAME",
"storageAccountSasToken": "YOUR SAS TOKEN"
}
PROTECTED_SETTINGS
tags = "YOUR TAG"
}
Just got this working on a similar question:
Trying to add LinuxDiagnostic Azure VM Extension through terraform and getting errors
This includes getting the SAS token and reading from json files.