This document outlines the coding conventions and best practices for the DevFactory project.
Each resource module follows a consistent file structure:
module.tf- Contains the main resource definitionsvariables.tf- Contains input variable declarationsoutput.tf- Contains output variable declarations
-
Resource Naming
- All resources use the Azure CAF naming module for consistent naming. IMPORTANT: This includes prefixing names with "azurerm_".
- Standard prefixes are applied through global settings
- Resources are named using a combination of prefixes, resource type, and custom name
-
Variable Naming
- Variable names use snake_case
- Resource-specific variables are grouped under an object with the resource name
- Common variables like
global_settings,resource_group_name, andlocationare used consistently
-
Indentation
- Use 2 spaces for indentation
- Align resource attributes for readability
-
Comments
- Add descriptive comments for complex logic
- Comment blocks of related code
-
Resource Blocks
- Group related resources together
- Use consistent ordering of resource attributes (name, location, resource group first)
-
Global Settings
variable "global_settings" { description = "Global settings object" type = object({ prefixes = optional(list(string)) random_length = optional(number) passthrough = optional(bool) use_slug = optional(bool) }) }
-
Resource Configuration
variable "resource_name" { description = "Configuration object for the resource" type = object({ name = string # Resource-specific properties }) }
-
Resource References
# Direct reference resource_id = var.resource_id # Key-based reference resource = object({ key = string })
data "azurecaf_name" "resource" {
name = var.resource.name
resource_type = "azurerm_resource_type"
prefixes = var.global_settings.prefixes
random_length = var.global_settings.random_length
clean_input = true
passthrough = var.global_settings.passthrough
use_slug = var.global_settings.use_slug
}
resource "azurerm_resource" "resource" {
name = data.azurecaf_name.resource.result
location = var.location
resource_group_name = var.resource_group_name
tags = local.tags
# Resource-specific properties
}resource "azurerm_resource" "resource" {
# Required properties
name = data.azurecaf_name.resource.result
# Optional properties with fallbacks
property_a = try(var.resource.property_a, null)
property_b = try(var.resource.property_b, "default_value")
property_c = lookup(var.resource, "property_c", null)
# Conditional blocks
dynamic "block_name" {
for_each = try(var.resource.block_property, null) != null ? [1] : []
content {
# Block properties
}
}
}locals {
tags = merge(
var.tags,
try(var.resource.tags, {}),
{
"resource_type" = "Resource Type"
}
)
}- Direct ID Reference
resource "azurerm_child_resource" "resource" {
parent_id = var.parent_id
}- Key-Based Reference
resource "azurerm_child_resource" "resource" {
parent_id = module.parent_resources[var.resource.parent.key].id
}- Use the
try()function for handling optional parameters - Use
for_eachwith map objects for creating multiple instances of resources - Use
lookup()to safely access map values that might not exist - Use
can()to check if values can be determined before trying to access them - Use dynamic blocks for optional configuration sections
- Avoid hard-coded values in resource blocks; use variables instead
- Create reusable modules for common patterns
- Validate inputs using variable type constraints
- Use consistent output patterns across modules
- Apply tags consistently to all resources
NOTE: azurerm_* is just the resource naming convention. DO NOT use the azurerm provider. Use the azapi provider instead.