No attribute while using yamldecode in Terraform code

69 Views Asked by At

I am trying to bulk enable Analytcis rules in Sentinel using

azurerm_sentinel_alert_rule_scheduled

I am loading yaml files from local directory and decoding them with yamldecode function. I can access fields like each.value.severity, each.value.description, each.value.tactics (list), however fields queryFrequency and queryPeriod for some reason are not available.

locals {
  yaml_files = fileset("D:\\Terraform\\RG_LAW_SENTINEL\\AnalyticsYaml\\", "*.yaml")
  yaml_data  = [for f in local.yaml_files : yamldecode(file("D:\\Terraform\\RG_LAW_SENTINEL\\AnalyticsYaml\\${f}"))]
}



resource "azurerm_sentinel_alert_rule_scheduled" "test1" {
  for_each                   = { for f in local.yaml_data : f.name => f }
  name                       = each.value.name
  log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.sentinel-terraform.workspace_id
  display_name               = each.value.name
  severity                   = each.value.severity
  description                = each.value.description
  tactics                    = each.value.tactics
  query_frequency            = each.value.queryFrequency
  query                      = <<QUERY
  ${each.value.query}
QUERY

  event_grouping {
    aggregation_method = "AlertPerResult"

  }

  incident_configuration {
    create_incident = "true"

    grouping {
      enabled                = "true"
      lookback_duration      = "P1D"
      entity_matching_method = "AllEntities"

    }
  }

}

Code without "query_frequency = each.value.queryFrequency" is working properly. Yaml sample:

id: bb616d82-108f-47d3-9dec-9652ea0d3bf6
name: Account Created and Deleted in Short Timeframe
description: |
  'Search for user principal name (UPN) events. Look for accounts created and then deleted in under 24 hours. Attackers may create an account for their use, and then remove the account when no longer needed.
  Ref : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-account'
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
relevantTechniques:
  - T1078.004
tags:
  - AADSecOpsGuide
query: |
  let queryfrequency = 1h;
  let queryperiod = 1d;
  AuditLogs
  | where TimeGenerated > ago(queryfrequency)
  | where OperationName =~ "Delete user"
  //extend UserPrincipalName = tostring(TargetResources[0].userPrincipalName)
  | mv-apply TargetResource = TargetResources on 
    (
        where TargetResource.type == "User"
        | extend UserPrincipalName = extract(@'([a-f0-9]{32})?(.*)', 2, tostring(TargetResource.userPrincipalName))
    )
  | extend DeletedByUser = tostring(InitiatedBy.user.userPrincipalName), DeletedByIPAddress = tostring(InitiatedBy.user.ipAddress)
  | extend DeletedByApp = tostring(InitiatedBy.app.displayName)
  | project Deletion_TimeGenerated = TimeGenerated, UserPrincipalName, DeletedByUser, DeletedByIPAddress, DeletedByApp, Deletion_AdditionalDetails = AdditionalDetails, Deletion_InitiatedBy = InitiatedBy, Deletion_TargetResources = TargetResources
  | join kind=inner (
      AuditLogs
      | where TimeGenerated > ago(queryperiod)
      | where OperationName =~ "Add user"      
      | mv-apply TargetResource = TargetResources on 
        (
            where TargetResource.type == "User"
            | extend UserPrincipalName = trim(@'"',tostring(TargetResource.userPrincipalName))
        )
      | project-rename Creation_TimeGenerated = TimeGenerated
  ) on UserPrincipalName
  | extend TimeDelta = Deletion_TimeGenerated - Creation_TimeGenerated
  | where  TimeDelta between (time(0s) .. queryperiod)
  | extend CreatedByUser = tostring(InitiatedBy.user.userPrincipalName), CreatedByIPAddress = tostring(InitiatedBy.user.ipAddress)
  | extend CreatedByApp = tostring(InitiatedBy.app.displayName)
  | project Creation_TimeGenerated, Deletion_TimeGenerated, TimeDelta, UserPrincipalName, DeletedByUser, DeletedByIPAddress, DeletedByApp, CreatedByUser, CreatedByIPAddress, CreatedByApp, Creation_AdditionalDetails = AdditionalDetails, Creation_InitiatedBy = InitiatedBy, Creation_TargetResources = TargetResources, Deletion_AdditionalDetails, Deletion_InitiatedBy, Deletion_TargetResources
  | extend timestamp = Deletion_TimeGenerated, Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DeletedByIPAddress
version: 1.0.3
kind: Scheduled
 Error: Unsupported attribute
│
│   on analytics_mass.tf line 16, in resource "azurerm_sentinel_alert_rule_scheduled" "test1":
│   16:   query_frequency             = each.value.queryFrequency
│     ├────────────────
│     │ each.value is object with 11 attributes
│
│ This object does not have an attribute named "queryFrequency".
1

There are 1 best solutions below

0
Vinay B On

I tired Implementing and Verifying Azure Sentinel Alert Rules with Terraform

Thanks @Marko E for guiding in the right direction.

To add to this, I noticed some changes that need to be made in the YAML file, which cause an issue because Terraform is case-sensitive in attribute names. The attribute names in your YAML file do not match exactly those expected by the Terraform azurerm_sentinel_alert_rule_scheduled resource configuration. In particular, the YAML file uses queryFrequency and queryPeriod, but your Terraform code uses query_frequency and query_period.

This difference results in the error message you saw, which says that the object does not have an attribute named queryFrequency because Terraform requires the precise attribute name as defined in the schema of the Terraform provider's resource.

Using the Yaml file mentioned with some necessary changes case sensitive characters I was able to overcome the issues mentioned

My terraform configuration:

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "demorg-vk"
  location = "East US"
}

resource "azurerm_log_analytics_workspace" "example" {
  name                = "testvk-workspace"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku                 = "PerGB2018"
}

locals {
  yaml_files = fileset("${path.module}/AnalyticsYaml/", "*.yaml")
  yaml_data  = [for f in local.yaml_files : yamldecode(file("${path.module}/AnalyticsYaml/${f}"))]
}

resource "azurerm_sentinel_alert_rule_scheduled" "example" {
  for_each                   = { for idx, f in local.yaml_data : idx => f }
  name                       = each.value.name
  log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id
  display_name               = each.value.name
  severity                   = each.value.severity
  description                = each.value.description
  tactics                    = each.value.tactics
  query_frequency            = each.value.queryFrequency
  query_period               = each.value.queryPeriod
  query                      = each.value.query

  event_grouping {
    aggregation_method = "AlertPerResult"
  }

  incident_configuration {
    create_incident = true

    grouping {
      enabled                = true
      lookback_duration      = "P1D"
      entity_matching_method = "AllEntities"
    }
  }
}

Output:

enter image description here

enter image description here

enter image description here