diff --git a/azure-collection-terraform/azure_resources.tf b/azure-collection-terraform/azure_resources.tf index 8b98e0cd..730d2996 100644 --- a/azure-collection-terraform/azure_resources.tf +++ b/azure-collection-terraform/azure_resources.tf @@ -42,6 +42,21 @@ resource "azurerm_eventhub_namespace" "namespaces_by_location" { sku = local.eventhub_sku_by_region[each.key].sku capacity = local.eventhub_sku_by_region[each.key].throughput_units + public_network_access_enabled = true + + network_rulesets { + default_action = "Deny" + public_network_access_enabled = true + trusted_service_access_enabled = true + + dynamic "ip_rule" { + for_each = var.whitelist_ips + content { + ip_mask = ip_rule.value + } + } + } + tags = { version = local.solution_version } @@ -157,6 +172,21 @@ resource "azurerm_eventhub_namespace" "activity_logs_namespace" { resource_group_name = azurerm_resource_group.rg.name sku = local.eventhub_sku_by_region[var.location].sku capacity = local.eventhub_sku_by_region[var.location].throughput_units + + public_network_access_enabled = true + + network_rulesets { + default_action = "Deny" + public_network_access_enabled = true + trusted_service_access_enabled = true + + dynamic "ip_rule" { + for_each = var.whitelist_ips + content { + ip_mask = ip_rule.value + } + } + } } resource "azurerm_eventhub_namespace_authorization_rule" "activity_logs_policy" { diff --git a/azure-collection-terraform/terraform.tfvars.example b/azure-collection-terraform/terraform.tfvars.example index eac578a2..81539183 100644 --- a/azure-collection-terraform/terraform.tfvars.example +++ b/azure-collection-terraform/terraform.tfvars.example @@ -100,6 +100,18 @@ eventhub_namespace_unsupported_locations = [ # resource groups that still contain nested resources (useful for integration tests). prevent_deletion_if_contains_resources = true +# Event Hub Network Security Configuration +# List of IP addresses or CIDR blocks to whitelist for Event Hub namespace access. +# Public network access is enabled but restricted to these selected networks only. +# Default action is "Deny" - you must explicitly add IPs to allow access. +# Azure trusted services (diagnostic settings, Azure Monitor) can always access regardless. +# +# Examples: +# whitelist_ips = ["203.0.113.0/24", "198.51.100.5"] # CIDR block and single IP +# whitelist_ips = ["10.0.0.0/16"] # Corporate network +# whitelist_ips = [] # Deny all public access (trusted services only) +whitelist_ips = [] + # Activity Log Configuration activity_log_export_name = "SumoLogicAzureActivityLogExport" activity_log_export_category = "azure/activity-logs" diff --git a/azure-collection-terraform/test/fixtures/whitelist-ips-cidr.tfvars b/azure-collection-terraform/test/fixtures/whitelist-ips-cidr.tfvars new file mode 100644 index 00000000..32582455 --- /dev/null +++ b/azure-collection-terraform/test/fixtures/whitelist-ips-cidr.tfvars @@ -0,0 +1,2 @@ +# Test fixture: CIDR block in whitelist_ips +whitelist_ips = ["10.0.0.0/16"] diff --git a/azure-collection-terraform/test/fixtures/whitelist-ips-empty.tfvars b/azure-collection-terraform/test/fixtures/whitelist-ips-empty.tfvars new file mode 100644 index 00000000..19eea0ea --- /dev/null +++ b/azure-collection-terraform/test/fixtures/whitelist-ips-empty.tfvars @@ -0,0 +1,2 @@ +# Test fixture: Empty whitelist_ips (deny all public access, trusted services only) +whitelist_ips = [] diff --git a/azure-collection-terraform/test/fixtures/whitelist-ips-invalid-cidr.tfvars b/azure-collection-terraform/test/fixtures/whitelist-ips-invalid-cidr.tfvars new file mode 100644 index 00000000..c9bad283 --- /dev/null +++ b/azure-collection-terraform/test/fixtures/whitelist-ips-invalid-cidr.tfvars @@ -0,0 +1,2 @@ +# Test fixture: Invalid CIDR format in whitelist_ips +whitelist_ips = ["10.0.0.0/33"] diff --git a/azure-collection-terraform/test/fixtures/whitelist-ips-invalid-format.tfvars b/azure-collection-terraform/test/fixtures/whitelist-ips-invalid-format.tfvars new file mode 100644 index 00000000..cab6eb0c --- /dev/null +++ b/azure-collection-terraform/test/fixtures/whitelist-ips-invalid-format.tfvars @@ -0,0 +1,2 @@ +# Test fixture: Invalid IP format in whitelist_ips +whitelist_ips = ["not-an-ip", "203.0.113.5"] diff --git a/azure-collection-terraform/test/fixtures/whitelist-ips-multiple.tfvars b/azure-collection-terraform/test/fixtures/whitelist-ips-multiple.tfvars new file mode 100644 index 00000000..cd7face4 --- /dev/null +++ b/azure-collection-terraform/test/fixtures/whitelist-ips-multiple.tfvars @@ -0,0 +1,2 @@ +# Test fixture: Multiple IPs and CIDR blocks in whitelist_ips +whitelist_ips = ["203.0.113.5", "198.51.100.0/24", "192.0.2.10"] diff --git a/azure-collection-terraform/test/fixtures/whitelist-ips-single.tfvars b/azure-collection-terraform/test/fixtures/whitelist-ips-single.tfvars new file mode 100644 index 00000000..55a4f0cc --- /dev/null +++ b/azure-collection-terraform/test/fixtures/whitelist-ips-single.tfvars @@ -0,0 +1,2 @@ +# Test fixture: Single IP address in whitelist_ips +whitelist_ips = ["203.0.113.5"] diff --git a/azure-collection-terraform/test/whitelist_ips_test.go b/azure-collection-terraform/test/whitelist_ips_test.go new file mode 100644 index 00000000..bde6c303 --- /dev/null +++ b/azure-collection-terraform/test/whitelist_ips_test.go @@ -0,0 +1,59 @@ +package test + +import ( + "path/filepath" + "testing" +) + +// TestWhitelistIPsValidation tests the whitelist_ips variable validation +func TestWhitelistIPsValidation(t *testing.T) { + tests := []struct { + name string + tfvarsFile string + expectError bool + description string + }{ + { + name: "ValidEmptyWhitelist", + tfvarsFile: filepath.Join("test", fixturesDir, "whitelist-ips-empty.tfvars"), + expectError: false, + description: "Empty whitelist_ips should pass validation", + }, + { + name: "ValidSingleIP", + tfvarsFile: filepath.Join("test", fixturesDir, "whitelist-ips-single.tfvars"), + expectError: false, + description: "Single valid IP address should pass validation", + }, + { + name: "ValidCIDR", + tfvarsFile: filepath.Join("test", fixturesDir, "whitelist-ips-cidr.tfvars"), + expectError: false, + description: "Valid CIDR block should pass validation", + }, + { + name: "ValidMultiple", + tfvarsFile: filepath.Join("test", fixturesDir, "whitelist-ips-multiple.tfvars"), + expectError: false, + description: "Multiple valid IPs and CIDR blocks should pass validation", + }, + { + name: "InvalidIPFormat", + tfvarsFile: filepath.Join("test", fixturesDir, "whitelist-ips-invalid-format.tfvars"), + expectError: true, + description: "Invalid IP format should fail validation", + }, + { + name: "InvalidCIDR", + tfvarsFile: filepath.Join("test", fixturesDir, "whitelist-ips-invalid-cidr.tfvars"), + expectError: true, + description: "Invalid CIDR format should fail validation", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + runValidationTest(t, tt.name, tt.tfvarsFile, tt.expectError, tt.description) + }) + } +} diff --git a/azure-collection-terraform/variables.tf b/azure-collection-terraform/variables.tf index 4ab262f7..a86e646c 100644 --- a/azure-collection-terraform/variables.tf +++ b/azure-collection-terraform/variables.tf @@ -493,4 +493,18 @@ variable "prevent_deletion_if_contains_resources" { EOT type = bool default = true +} + +variable "whitelist_ips" { + description = "List of IP addresses or CIDR blocks to whitelist for Event Hub namespace access. Public network access will be enabled from these selected networks only." + type = list(string) + default = [] + + validation { + condition = alltrue([ + for ip in var.whitelist_ips : + can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}(/[0-9]{1,2})?$", ip)) + ]) + error_message = "All whitelist IPs must be valid IPv4 addresses or CIDR blocks (e.g., '192.168.1.1' or '10.0.0.0/24')." + } } \ No newline at end of file