Azure / terraform-azurerm-lz-vending

Terraform module to deploy landing zone subscriptions (and much more) in Azure
https://registry.terraform.io/modules/Azure/lz-vending/azurerm
MIT License
170 stars 79 forks source link

bug: Budgets provisioning fails with "RBACAccessDenied" #394

Closed nekdima closed 3 months ago

nekdima commented 4 months ago

Community Note

Versions

Please paste the output of terraform version command from within the initialized directory:

Terraform v1.8.5 on darwin_arm64

Please enter the module version that you are using:

Module 4.2.1

Description

Steps to Reproduce

Use this config in main.tf:

  budget_enabled = true
  budgets        = var.subscription_budgets

variables.tf file content(default one, from example):

variable "subscription_budgets" {
  type = map(object({
    amount            = number
    time_grain        = string
    time_period_start = string
    time_period_end   = string
    relative_scope    = optional(string, "")
    notifications = optional(map(object({
      enabled        = bool
      operator       = string
      threshold      = number
      threshold_type = optional(string, "Actual")
      contact_emails = optional(list(string), [])
      contact_roles  = optional(list(string), [])
      contact_groups = optional(list(string), [])
      locale         = optional(string, "en-us")
    })), {})
  }))
  default = {
    budget1 = {
      amount            = 150
      time_grain        = "Monthly"
      time_period_start = "2024-06-01T00:00:00Z"
      time_period_end   = "2027-12-31T23:59:59Z"
      notifications = {
        eightypercent = {
          enabled        = true
          operator       = "GreaterThan"
          threshold      = 80
          threshold_type = "Actual"
          contact_emails = ["john@contoso.com"]
        }
        budgetexceeded = {
          enabled        = true
          operator       = "GreaterThan"
          threshold      = 120
          threshold_type = "Forecasted"
          contact_groups = ["Owner"]
        }
      }
    }
  }
}

Screenshots

image

Additional context

SPN roles config: RBAC Owner on the parent MG EA: Subscription Creator as described here: https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/assign-roles-azure-service-principals#permissions-that-can-be-assigned-to-the-service-principal

Vending is being used on top of the latest ESLZ 6.0.0

matt-FFFFFF commented 4 months ago

Please can you log in using that SPN using az cli and try and create the budget:

https://learn.microsoft.com/azure/cost-management-billing/costs/tutorial-acm-create-budgets?tabs=clibudget#create-and-edit-budgets

Many thanks

nekdima commented 4 months ago

@matt-FFFFFF Az Cli and Poweshell based instructions for budgets deployments are outdated. I've ended up deploying using ARM template (while being logged in using SPN) and it worked.

deployment screenshot image

ARM template used(no filter option): https://learn.microsoft.com/en-gb/azure/cost-management-billing/costs/quick-create-budget-template?tabs=no-filter%2CPowerShell#review-and-deploy-the-template

matt-FFFFFF commented 4 months ago

What do you mean the instructions are outdated?

An ARM template deployment isn't the same as a REST API call to create a budget resource. Hence the ask.

Please could you post the contents of the ARM template?

nekdima commented 4 months ago

What do you mean the instructions are outdated?

The PowerShell cmdlets and CLI commands used in the script are outdated, the script provided in the article doesn't work. Despite puzzling for some time I didn't manage to get it working.

Please could you post the contents of the ARM template?

ARM template used(no filter option): https://learn.microsoft.com/en-gb/azure/cost-management-billing/costs/quick-create-budget-template?tabs=no-filter%2CPowerShell#review-and-deploy-the-template Please see below as well.

An ARM template deployment isn't the same as a REST API call to create a budget resource

Sure, it's not the same but in the end permissions utilised by SPN will be the same regardless of doing an ARM template deployment, CLI or anything else afaik. I think that's the most important in this case since the initial error message was referring to the missing RBAC permissions when running azapi resource creation.

Many thanks for the troubleshooting assistance!

ARM Template:

{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.26.54.24096",
      "templateHash": "10216192224502761212"
    }
  },
  "parameters": {
    "budgetName": {
      "type": "string",
      "defaultValue": "MyBudget",
      "metadata": {
        "description": "Name of the Budget. It should be unique within a resource group."
      }
    },
    "amount": {
      "type": "int",
      "defaultValue": 1000,
      "metadata": {
        "description": "The total amount of cost or usage to track with the budget"
      }
    },
    "timeGrain": {
      "type": "string",
      "defaultValue": "Monthly",
      "allowedValues": [
        "Monthly",
        "Quarterly",
        "Annually"
      ],
      "metadata": {
        "description": "The time covered by a budget. Tracking of the amount will be reset based on the time grain."
      }
    },
    "startDate": {
      "type": "string",
      "metadata": {
        "description": "The start date must be first of the month in YYYY-MM-DD format. Future start date should not be more than three months. Past start date should be selected within the timegrain preiod."
      }
    },
    "endDate": {
      "type": "string",
      "metadata": {
        "description": "The end date for the budget in YYYY-MM-DD format. If not provided, we default this to 10 years from the start date."
      }
    },
    "firstThreshold": {
      "type": "int",
      "defaultValue": 90,
      "metadata": {
        "description": "Threshold value associated with a notification. Notification is sent when the cost exceeded the threshold. It is always percent and has to be between 0.01 and 1000."
      }
    },
    "secondThreshold": {
      "type": "int",
      "defaultValue": 110,
      "metadata": {
        "description": "Threshold value associated with a notification. Notification is sent when the cost exceeded the threshold. It is always percent and has to be between 0.01 and 1000."
      }
    },
    "contactEmails": {
      "type": "array",
      "metadata": {
        "description": "The list of email addresses to send the budget notification to when the threshold is exceeded."
      }
    }
  },
  "resources": [
    {
      "type": "Microsoft.Consumption/budgets",
      "apiVersion": "2023-11-01",
      "name": "[parameters('budgetName')]",
      "properties": {
        "timePeriod": {
          "startDate": "[parameters('startDate')]",
          "endDate": "[parameters('endDate')]"
        },
        "timeGrain": "[parameters('timeGrain')]",
        "amount": "[parameters('amount')]",
        "category": "Cost",
        "notifications": {
          "NotificationForExceededBudget1": {
            "enabled": true,
            "operator": "GreaterThan",
            "threshold": "[parameters('firstThreshold')]",
            "contactEmails": "[parameters('contactEmails')]"
          },
          "NotificationForExceededBudget2": {
            "enabled": true,
            "operator": "GreaterThan",
            "threshold": "[parameters('secondThreshold')]",
            "contactEmails": "[parameters('contactEmails')]"
          }
        }
      }
    }
  ],
  "outputs": {
    "name": {
      "type": "string",
      "value": "[parameters('budgetName')]"
    },
    "resourceId": {
      "type": "string",
      "value": "[subscriptionResourceId('Microsoft.Consumption/budgets', parameters('budgetName'))]"
    }
  }
}
MoussaBangre commented 3 months ago

Facing the same issue also. Any update on this ?

steph409 commented 3 months ago

I had a look into it and it is actually an small error in the notifications. When looking at the official microsoft API documentation, there are three parameters: contactEmails, contactGroups and contactRoles. In the above example, and also in the official documentation of this module, contactGroups=["Owner"] is used when it should be contactRoles=["Owner"]

The following code works for me (note that I took the official example but replaced contactGroups with contactRoles):

variable "subscription_id" {

}

module "lz_vending" {
  source  = "Azure/lz-vending/azurerm"
  version = "4.1.2" # change this to your desired version, https://www.terraform.io/language/expressions/version-constraints

  # Set the default location for resources
  location = "westeurope"

  # subscription variables
  subscription_alias_enabled = false
  subscription_id            = var.subscription_id

  budget_enabled = true
  budgets = {
    budget1 = {
      amount            = 150
      time_grain        = "Monthly"
      time_period_start = "2024-10-01T00:00:00Z"
      time_period_end   = "2027-12-31T23:59:59Z"
      notifications = {
        eightypercent = {
          enabled        = true
          operator       = "GreaterThan"
          threshold      = 80
          threshold_type = "Actual"
          contact_emails = ["john@contoso.com"]
        }
        budgetexceeded = {
          enabled        = true
          operator       = "GreaterThan"
          threshold      = 120
          threshold_type = "Forecasted"

          contact_roles = ["Owner"]
        }
      }
    }
  }
}

provider "azurerm" {
  subscription_id = var.subscription_id
  features {}
}

Screenshot from the microsoft api documentation:

image

I will add a PR this afternoon to update the default of this module accordingly.