Skip to content

Start with Required Attributes

GlennChia edited this page Aug 13, 2021 · 2 revisions

It is advisable to first create the directories and files for the new module before developing the module and examples. This page covers the following:

  1. Directory structure
  2. Upgrade provider version if needed
  3. Add Examples
  4. Connect the files
  5. Implement the resource with the required attributes
  6. Integrate the output with the local combined object

Directory structure

The following structure shows all the files that are relevant for this feature. Some of these files already exist in the repository. Essentially the new files and directories that must be created are:

  1. examples/consumption_budget/100-consumption-budget-rg/configuration.tfvars
  2. modules/consumption_budget/*
  3. consumption_budgets.tf
├───.github
│   └───workflows
│       └───standalone-scenarios.json
├───examples
│   |───consumption_budget
│   |   └───100-consumption-budget-rg
|   |        └───configuration.tfvars
│   |───module.tf
│   └───variables.tf
│───modules
│   └───consumption_budget
│       └───resource_group
│               ├───main.tf
│               ├───output.tf
│               ├───resource_group_budget.tf
│               └───variables.tf
│───consumption_budgets.tf
│───main.tf
│───locals.combined_objects.tf
└───locals.tf

Upgrade provider version if needed

In the previous section, it was identified that the azurerm version needed to be 2.61.0. This is changed in the main.tf file at the root level

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 2.61.0"
    }
  }
}

Add Examples

Start with the examples/consumption_budget/100-consumption-budget-rg/configuration.tfvars file, implementing only the required attributes for a start. An example is:

global_settings = {
  default_region = "region1"
  regions = {
    region1 = "southeastasia"
  }
  random_length = 5
}

resource_groups = {
  test = {
    name = "test"
  }
}

consumption_budgets = {
  test_budget = {
    resource_group = {
      # accepts either id or key to get resource group id
      # id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1"
      key = "test"
    }
    name       = "example"
    amount     = 1000
    time_grain = "Monthly"
    time_period = {
      # uncomment to customize start_date
      # start_date = "2022-06-01T00:00:00Z"
    }
    notifications = {
      contact_email = {
        enabled   = true
        threshold = 90.0
        operator  = "EqualTo"
        contact_emails = [
          "foo@example.com",
          "bar@example.com",
        ]
      }
    }
  }
}

Notice that contact_email is added as an example although it is optional. This is because at apply time, Terraform produces an error if there isn't an instance of either contact_emails, contact_groups, or contact_roles.

Connect the files

examples

variables.tf

variable "consumption_budgets" {
  default = {}
}
  • Accepts the variables from the configuration.tfvars file defined earlier

module.tf

module "example" {
  source = "../.."
  # truncated
  shared_services = {
    consumption_budgets = var.consumption_budgets
    # truncated
  }
}

root

locals.tf

locals {
  # truncated
  shared_services = {
    consumption_budgets = try(var.shared_services.consumption_budgets, {})
    # truncated
  }
}

consumption_budgets.tf

module "consumption_budgets" {
  source   = "./modules/consumption_budget"
  for_each = local.shared_services.consumption_budgets

  resource_group_id = coalesce(
    try(local.combined_objects_resource_groups[try(each.value.resource_group.lz_key, local.client_config.landingzone_key)][each.value.resource_group.key].id, null),
    try(each.value.resource_group.id, null)
  )
  settings = each.value
}
  • The for_each iterates through each individual consumption_budget in consumption_budgets and maps those values to settings. This settings is important for referencing individual variables in the resource.
  • The pattern for resource_group_id allows the id to be retrieved from an attribute called resource_group.key or resource_group.id defined in the configuration.tfvars file

Implement the resource with the required attributes

This is done in the modules directory

modules/consumption_budget

variables.tf

variable "settings" {
  description = "Configuration object for the consumption budget resource group"
}

variable "resource_group_id" {
  description = "The ID of the Resource Group to create the consumption budget for in the form of /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1"
  type        = string
}

resource_group_budget.tf

resource "azurerm_consumption_budget_resource_group" "this" {
  name              = var.settings.name
  resource_group_id = var.resource_group_id

  amount     = var.settings.amount
  time_grain = var.settings.time_grain

  time_period {
    start_date = try(var.settings.time_period.start_date, join("", [formatdate("YYYY-MM", timestamp()), "-01T00:00:00Z"]))
    end_date   = try(var.settings.time_period.end_date, null)
  }

  dynamic "notification" {
    for_each = var.settings.notifications

    content {
      operator  = notification.value.operator
      threshold = notification.value.threshold

      contact_emails = try(notification.value.contact_emails, [])
      contact_roles  = try(notification.value.contact_roles, [])
      enabled        = try(notification.value.enabled, true)
    }
  }
}
  • Implement the attributes that allow the variables from the configuration.tfvars file to be injected. The injection comes from var.settings
  • time_period is specified in the Terraform Registry documentation as a single required block.
  • notification is defined as a required block that can have multiple instances

output.tf

output "id" {
  description = "The ID of the Resource Group Consumption Budget"
  value       = azurerm_consumption_budget_resource_group.this.id
}

Integrate the output with the locals.combined_objects.tf

With the output exported from the modules directory, export it at the root level as well.

consumption_budgets.tf

output "consumption_budgets_resource_groups" {
  value = module.consumption_budgets_resource_groups
}

With that exported, integrate it with locals.combined_objects.tf

locals.combined_objects.tf

locals {
  combined_objects_consumption_budgets_resource_groups = merge(tomap({ (local.client_config.landingzone_key) = module.consumption_budgets_resource_groups }), try(var.remote_objects.consumption_budgets_resource_groups, {}))
}
Clone this wiki locally