| page_title | deep_merge function - lara-utils |
|---|---|
| subcategory | |
| description | Deep merge objects |
Unlike Terraform's built-in merge() function which only performs shallow merging, provider::lara-utils::deep_merge() recursively traverses nested structures. The function accepts list of maps or objects, and returns a single merged result.
A distinctive feature of provider::lara-utils::deep_merge() is its use of configuration object to control merge strategies. Simply pass one or more configuration objects (merged together with later having precedence) after your merging list to change how the merge operates:
| Mode | Description | Use Case | Default |
|---|---|---|---|
override |
Later values replace earlier ones | Standard configuration layering | enabled |
null_override |
Null values will replace existing values | Removing Helm chart defaults | enabled |
append_list |
Lists are concatenated instead of replaced | Accumulating features, rules, or tags | disabled |
deep_copy_list |
Lists are deeply merged element by element using override | Merging of nested lists with complex structures | disabled |
union_lists |
Lists are merged as sets (unique elements) | Deduplicating tags, IPs, or identifiers | disabled |
locals {
config1 = { a = 1, b = { x = 10, y = 20 } }
config2 = { a = 2, b = { y = 30, z = 40 } }
result = provider::lara-utils::deep_merge([local.config1, local.config2])
# Result: { a = 2, b = { x = 10, y = 30, z = 40 } }
}locals {
defaults = { timeout = 30, retries = 3, debug = false }
user_config = { timeout = 60, debug = true, custom = "value" }
result = provider::lara-utils::deep_merge([local.defaults, local.user_config], { override = false })
# Result: { timeout = 30, retries = 3, debug = false, custom = "value" }
# Note: defaults are preserved, only new keys from user_config are added
}locals {
base = { name = "service", port = 8080, optional_setting = "enabled" }
overrides = { port = 9090, optional_setting = null }
result = provider::lara-utils::deep_merge([local.base, local.overrides], { null_override = false })
# Result: { name = "service", port = 9090, optional_setting = "enabled" }
# Note: null doesn't override the existing value
}locals {
base_rules = {
firewall = {
allowed_ports = [22, 80, 443]
denied_ips = ["192.168.1.100"]
}
}
additional_rules = {
firewall = {
allowed_ports = [8080, 9090]
denied_ips = ["192.168.1.101", "192.168.1.102"]
}
}
result = provider::lara-utils::deep_merge([local.base_rules, local.additional_rules], { append_list = true })
# Result: {
# firewall = {
# allowed_ports = [22, 80, 443, 8080, 9090]
# denied_ips = ["192.168.1.100", "192.168.1.101", "192.168.1.102"]
# }
# }
}locals {
base_tags = {
security = {
allowed_ports = [22, 80, 443]
tags = ["security", "prod", "critical"]
}
}
additional_tags = {
security = {
allowed_ports = [8080, 80, 9090]
tags = ["monitoring", "prod", "audit"]
}
}
result = provider::lara-utils::deep_merge([local.base_tags, local.additional_tags], { union_lists = true })
# Result: {
# security = {
# allowed_ports = [22, 80, 443, 8080, 9090] # Unique elements only
# tags = ["security", "prod", "critical", "monitoring", "audit"] # Duplicates removed
# }
# }
}locals {
# Base configuration for all environments
base_app_config = {
app = {
name = "web-service"
health_check = {
path = "/health"
interval = 30
timeout = 5
}
logging = {
level = "info"
format = "json"
}
}
}
# Environment-specific configurations
env_configs = {
dev = {
app = {
replicas = 1
logging = { level = "debug" }
}
}
staging = {
app = {
replicas = 2
health_check = { interval = 15 }
}
}
prod = {
app = {
replicas = 5
health_check = { interval = 10, timeout = 3 }
logging = { level = "warn" }
ssl = { enabled = true, strict = true }
}
}
}
# Get the final configuration for the current environment
final_config = provider::lara-utils::deep_merge([local.base_app_config, lookup(local.env_configs, var.environment, {})])
}locals {
# Base deployment specification
deployment_base = {
metadata = {
labels = {
app = "myapp"
version = "1.0.0"
managed_by = "terraform"
}
annotations = {
"deployment.kubernetes.io/revision" = "1"
}
}
spec = {
selector = {
match_labels = {
app = "myapp"
}
}
template = {
spec = {
containers = [{
name = "app"
image = "myapp:1.0.0"
ports = [{ container_port = 8080 }]
env = [
{ name = "LOG_LEVEL", value = "info" }
]
}]
}
}
}
}
# Production overrides with additional configurations
prod_overrides = {
metadata = {
labels = {
environment = "production"
tier = "backend"
}
annotations = {
"prometheus.io/scrape" = "true"
"prometheus.io/port" = "9090"
}
}
spec = {
replicas = 3
template = {
spec = {
containers = [{
resources = {
requests = { memory = "256Mi", cpu = "100m" }
limits = { memory = "512Mi", cpu = "200m" }
}
env = [
{ name = "DATABASE_URL", value = "postgres://prod-db:5432/myapp" },
{ name = "CACHE_ENABLED", value = "true" },
]
}]
}
}
}
}
# Merge with append mode for arrays
deployment = provider::lara-utils::deep_merge([local.deployment_base, local.prod_overrides], { append_list = true })
}locals {
# Base tags from organization standards
org_tags = {
global_tags = ["terraform", "managed", "compliance"]
security_tags = ["encrypted", "monitored"]
}
# Team-specific tags
team_tags = {
global_tags = ["backend", "api", "terraform"] # Some overlap
team_tags = ["payments", "critical"]
}
# Environment-specific tags
env_tags = {
global_tags = ["production", "managed"] # More overlap
security_tags = ["audited", "encrypted"] # More overlap
}
# Merge all tags with automatic deduplication
final_tags = provider::lara-utils::deep_merge([local.org_tags, local.team_tags, local.env_tags], { union_lists = true })
# Result: {
# global_tags = ["terraform", "managed", "compliance", "backend", "api", "production"]
# security_tags = ["encrypted", "monitored", "audited"]
# team_tags = ["payments", "critical"]
# }
}deep_merge(objects dynamic, options dynamic...) dynamic
objects(Dynamic) List of objects to merge
options(Variadic, Dynamic) Merging options