VAP
VAP

Reputation: 21

Deny Savings Plan Creation and Set Up Alerts for Policy Violations in Azure Using Terraform

Using Terraform, I want to deny the creation of savings plans outside of the singular Prod subscription and get an email whenever an attempt is made. We have more than 10 subscriptions in the tenant in different management groups.

Here's what I have in mind:

  1. Create a custom Azure Policy that denies Savings Plan creation outside the Prod subscription.
  2. Create Diagnostic Settings for every subscription to track when a policy denial occurs and send them to a Log Analytics Workspace in the Prod Subscription. For this example made in a non_prod subscription to test.
  3. Create a Scheduled Query Alert to run a query in Log Analytics to pull denial events (e.g., when the policy is triggered)
  4. Set up an Action Group to send email notifications containing the logs pulled via the query alert.

Here's what I have so far:

FYI I got the savings plan type from Azure Documentation. Not a 100% sure that it is correct, feel free to point out the correct one.

A custom policy to restrict Savings Plan creation:

resource "azurerm_policy_definition" "restrict_savings_plan" {
  name         = "restrict-savings-plan-creation"
  policy_type  = "Custom"
  mode         = "All"
  display_name = "Restrict Savings Plan Creation to Prod Subscription"

  policy_rule = <<POLICY
  {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Billing billingAccounts/savingsPlanOrders/savingsPlans"//Not sure if this is correct
        },
        {
          "value": "[subscription().Id]", //Kinda iffy about this
          "notEquals": "Prod-subscription-id"
        }
      ]
    },
    "then": {
      "effect": "deny"
    }
  }
  POLICY
}

Diagnostic settings to capture Activity Logs in Non-Prod for testing:

resource "azurerm_monitor_diagnostic_setting" "Non_Prod_subscription" {
  name               = "Non_Prod-log-analytics"
  target_resource_id = "/subscriptions/Non_Prod_subscription_id"
  log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id

  logs {
    category = "Administrative"
    enabled  = true
  }
}

An Action Group for email notifications:

resource "azurerm_monitor_action_group" "Non_Prod_email_action_group" {
  name                = "email-action-group"
  resource_group_name = "Non_Prod-resource-group"
  short_name          = "emailgroup"

  email_receiver {
    name          = "AdminEmail"
    email_address = "[email protected]"
    use_common_alert_schema = true
  }
}

Scheduled Query Alert to pull policy denials:

resource "azurerm_monitor_scheduled_query_rules_alert" "SavingsPlan_policy_violation_alert" {
  name                = "SavingsPlanPolicyViolationAlert"
  resource_group_name = "Non_prodRG"
  location            = "eastus"
  data_source_id      = azurerm_monitor_diagnostic_setting.Non_Prod_subscription.log_analytics_workspace_id

  description = "Alert for policy violations across all subscriptions"
  enabled     = true

  query = <<-QUERY
    AzureActivity
    | where Category == "Policy"
    | where OperationNameValue == "Microsoft.Authorization/policies/audit/action"
    | where ActivityStatusValue == "Failed"
    | where Properties contains "Request disallowed by policy"
    | summarize Count = count() by SubscriptionId
  QUERY

  severity    = 2
  frequency   = "60"
  time_window = "60"

  trigger {
    operator  = "GreaterThan"
    threshold = 1
  }

  action {
    action_group  = [azurerm_monitor_action_group.Non_Prod_email_action_group.id]
    email_subject = "Alert: Savings Plan Policy Violation Detected"
  }
}

Currently, savings plan creations are not being denied nor am I getting emails. I would appreciate it if someone could provide feedback on this code.

Upvotes: 0

Views: 65

Answers (1)

Jahnavi
Jahnavi

Reputation: 8018

The given terraform code and structure both looks good according to the requirement. Few adjustments are done to your code such as resource type for billing provider and also related to the monitor rule's as given below.

provider "azurerm"{
features{}
subscription_id = "b4xxxx14"
}
resource "azurerm_policy_definition" "restrict_savings_plan" {
  name         = "restrict-savings-plan-creation"
  policy_type  = "Custom"
  mode         = "All"
  display_name = "Restrict Savings Plan Creation to Prod Subscription"

  policy_rule = <<POLICY
  {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Billing/billingAccounts/savingsPlanOrders" 
        },
        {
          "value": "f7bxxxx32b014",
          "notEquals": "Prod-subscription-id"
        }
      ]
    },
    "then": {
      "effect": "deny"
    }
  }
  POLICY
}
data "azurerm_log_analytics_workspace" "example"{
name = "Customlogsws"
resource_group_name = "Jahnavi"
}
resource "azurerm_monitor_diagnostic_setting" "Non_Prod_subscription" {
  name               = "Non_Prod-log-analytics"
  target_resource_id = "/subscriptions/xxxxx" #Provide Nonproduction subscriptionID here
  log_analytics_workspace_id = data.azurerm_log_analytics_workspace.example.id

  enabled_log {
    category = "Administrative"
  }
}

resource "azurerm_monitor_action_group" "Non_Prod_email_action_group" {
  name                = "email-action-group"
  resource_group_name = "Jahnavi"
  short_name          = "emailgroup"

  email_receiver {
    name          = "AdminEmail"
    email_address = "[email protected]"
    use_common_alert_schema = true
  }
}
resource "azurerm_monitor_scheduled_query_rules_alert" "SavingsPlan_policy_violation_alert" {
  name                = "SavingsPlanPolicyViolationAlert"
  resource_group_name = "Jahnavi"
  location            = "WestUS"
  data_source_id      = azurerm_monitor_diagnostic_setting.Non_Prod_subscription.log_analytics_workspace_id

  description = "Alert for policy violations across all subscriptions"
  enabled     = true

  query = <<-QUERY
    AzureActivity
    | where Category == "Policy"
    | where OperationNameValue == "Microsoft.Authorization/policies/audit/action"
    | where ActivityStatusValue == "Failed"
    | where Properties contains "Request disallowed by policy"
    | summarize Count = count() by SubscriptionId
  QUERY

  severity    = 2
  frequency   = "60"
  time_window = "60"

  trigger {
    operator  = "GreaterThan"
    threshold = 1
  }

  action {
    action_group  = [azurerm_monitor_action_group.Non_Prod_email_action_group.id]
    email_subject = "Alert: Savings Plan Policy Violation Detected"
  }
}

Output:

enter image description here

enter image description here

enter image description here

enter image description here

Upvotes: 0

Related Questions