diff --git a/templates_gold/exchange/template_exchange_gold_ptbr.yaml b/templates_gold/exchange/template_exchange_gold_ptbr.yaml index 2c67cd5..91a137f 100644 --- a/templates_gold/exchange/template_exchange_gold_ptbr.yaml +++ b/templates_gold/exchange/template_exchange_gold_ptbr.yaml @@ -277,33 +277,35 @@ zabbix_export: - uuid: c3c2d45be1f4439292678d07d36eb042 name: Descoberta de Serviços Exchange type: ZABBIX_ACTIVE - key: service.discovery + key: wmi.getall[root\cimv2,"SELECT Name, DisplayName, State FROM Win32_Service WHERE Name LIKE 'MSExchange%' AND StartMode='Auto'"] delay: 1h - filter: - evaltype: AND - conditions: - - macro: '{#SERVICE.NAME}' - value: ^(?!MSExchangeTransport$|MSExchangeSubmission$)MSExchange.* - operator: MATCHES_REGEX - - macro: '{#SERVICE.STARTUPNAME}' - value: automatic - operator: MATCHES_REGEX - description: Descobre serviços do Exchange que iniciam automaticamente. + description: Descobre serviços do Exchange que iniciam automaticamente via WMI (evita conflito com Template Windows). + lld_macro_paths: + - lld_macro: '{#SERVICE.NAME}' + path: $.Name + - lld_macro: '{#SERVICE.DISPLAYNAME}' + path: $.DisplayName item_prototypes: - uuid: dd3c489ce5f94540b50b6f2aa1aac65e name: 'Serviço: Status do {#SERVICE.DISPLAYNAME}' type: ZABBIX_ACTIVE - key: service.info["{#SERVICE.NAME}",state] + key: wmi.get[root\cimv2,"SELECT State FROM Win32_Service WHERE Name='{#SERVICE.NAME}'"] delay: 3m - description: Status atual do serviço {#SERVICE.DISPLAYNAME}. + description: Status atual do serviço {#SERVICE.DISPLAYNAME} via WMI. valuemap: name: Service state + preprocessing: + - type: JAVASCRIPT + parameters: + - | + var states = {'Running': 0, 'Paused': 1, 'Start Pending': 2, 'Pause Pending': 3, 'Continue Pending': 4, 'Stop Pending': 5, 'Stopped': 6}; + return states[value] !== undefined ? states[value] : 7; tags: - tag: component value: service trigger_prototypes: - uuid: 38cefb16c51448d69e05b322e08a495a - expression: last(/Microsoft Exchange Server 2016 by Zabbix agent active/service.info["{#SERVICE.NAME}",state])<>0 + expression: last(/Microsoft Exchange Server 2016 by Zabbix agent active/wmi.get[root\cimv2,"SELECT State FROM Win32_Service WHERE Name='{#SERVICE.NAME}'"])<>0 name: '🚨 Serviço Parado: {#SERVICE.DISPLAYNAME}' priority: HIGH description: O serviço {#SERVICE.DISPLAYNAME} deveria estar rodando (Auto) mas está parado. diff --git a/templates_gold/exchange/template_exchange_gold_ptbr_generated.md b/templates_gold/exchange/template_exchange_gold_ptbr_generated.md index 33fc466..8ecd54e 100644 --- a/templates_gold/exchange/template_exchange_gold_ptbr_generated.md +++ b/templates_gold/exchange/template_exchange_gold_ptbr_generated.md @@ -34,9 +34,9 @@ Desenvolvido por Arthur "O Farol" Mendes. ### Regras de Descoberta (LLD) -#### Descoberta de Serviços Exchange (`service.discovery`) +#### Descoberta de Serviços Exchange (`wmi.getall[root\cimv2,"SELECT Name, DisplayName, State FROM Win32_Service WHERE Name LIKE 'MSExchange%' AND StartMode='Auto'"]`) - **Protótipos de Itens:** - - Serviço: Status do {#SERVICE.DISPLAYNAME} (`service.info["{#SERVICE.NAME}",state]`) + - Serviço: Status do {#SERVICE.DISPLAYNAME} (`wmi.get[root\cimv2,"SELECT State FROM Win32_Service WHERE Name='{#SERVICE.NAME}'"]`) #### Descoberta de Bancos de Dados (`perf_instance.discovery["MSExchange Active Manager"]`) - **Protótipos de Itens:** - Banco [{#INSTANCE}]: Função da Cópia (`perf_counter_en["\MSExchange Active Manager({#INSTANCE})\Database Copy Role Active"]`) diff --git a/templates_gold/windows_active_agent/template_windows_gold_ptbr.yaml b/templates_gold/windows_active_agent/template_windows_gold_ptbr.yaml index d23dcff..457480d 100644 --- a/templates_gold/windows_active_agent/template_windows_gold_ptbr.yaml +++ b/templates_gold/windows_active_agent/template_windows_gold_ptbr.yaml @@ -545,6 +545,7 @@ zabbix_export: - uuid: ab5cc8d3c6734c7c9456c02dfecffd5d name: 'RDP: Sessões Ativas (Total)' key: perf_counter_en["\Terminal Services\Total Sessions"] + type: ZABBIX_ACTIVE delay: 1m value_type: FLOAT units: '' @@ -563,6 +564,7 @@ zabbix_export: description: Existem muitas sessões de terminal abertas. Isso pode consumir recursos ou indicar sessões "penduradas". - uuid: 44cf725b9c03464cac04df1103f51092 name: 'Disco: Tamanho da Fila (Queue Length)' + type: ZABBIX_ACTIVE key: perf_counter_en["\PhysicalDisk(_Total)\Current Disk Queue Length"] delay: 1m value_type: FLOAT @@ -581,6 +583,7 @@ zabbix_export: description: A fila de disco está constantemente alta. O armazenamento não está dando conta das requisições. - uuid: 141cc10cfa0b40c0a00e4737a965ef87 name: 'Segurança: Falhas de Login (Audit Failure)' + type: ZABBIX_ACTIVE key: eventlog[Security,,,,4625] delay: 1m value_type: LOG diff --git a/validate_zabbix_template.py b/validate_zabbix_template.py index ac0b0cb..186f9cf 100644 --- a/validate_zabbix_template.py +++ b/validate_zabbix_template.py @@ -322,10 +322,88 @@ def validate_zabbix_7_compliance(content): for i, item in enumerate(node): check_node(item, f"{path}[{i}]") + check_node(content) check_node(content) return errors, warnings +def validate_reserved_keys(content): + """ + Check for keys that are known to cause collisions or are reserved/monopolistic. + e.g. service.discovery, service.info with generic params. + """ + warnings = [] + + def check_keys(node, path="root"): + if isinstance(node, dict): + if 'key' in node: + key = node['key'] + # Check for service.discovery (without unique params, it collides generally) + if key == 'service.discovery': + warnings.append(f"[POTENTIAL COLLISION] Key 'service.discovery' found at {path}. This often conflicts with Windows OS templates. Consider 'wmi.getall'.") + + if str(key).startswith('service.info'): + warnings.append(f"[POTENTIAL COLLISION] Key '{key}' found at {path}. Ensure this does not conflict with standard Windows templates. Consider 'wmi.get'.") + + for k, v in node.items(): + check_keys(v, f"{path}.{k}") + elif isinstance(node, list): + for i, item in enumerate(node): + check_keys(item, f"{path}[{i}]") + + check_keys(content) + return warnings + + +def validate_active_agent_compliance(content): + """ + If template name suggests 'Active', ensure items have type: ZABBIX_ACTIVE. + """ + errors = [] + template_name = "" + + # Extract template name + if isinstance(content, dict) and 'zabbix_export' in content: + if 'templates' in content and isinstance(content['templates'], list) and len(content['templates']) > 0: + template_name = content['templates'][0].get('name', '') + + # Check case-insensitive 'active' in template name + if 'active' not in template_name.lower(): + return errors # Not an active agent template candidate, skip check + + def check_items(node, path="root"): + if isinstance(node, dict): + # Check if it looks like an item/prototype (has 'key' and 'name' and 'uuid') + # Avoiding false positives on non-item dicts + if 'key' in node and 'name' in node and 'type' in node: + item_type = node.get('type') + key = node['key'] + + # List of types that are NOT Agent checks (so don't need ZABBIX_ACTIVE) + # ZABBIX_PASSIVE is omitted because that's exactly what we want to FLAG if found. + allowed_non_agent_types = [ + 'ZABBIX_ACTIVE', + 'SIMPLE', 'SNMP', 'INTERNAL', 'TRAP', 'EXTERNAL', + 'DB_MONITOR', 'HTTP_AGENT', 'SSH', 'TELNET', 'CALCULATED', 'JMX', 'DEPENDENT', 'SCRIPT', 'BROWSER' + ] + + # If type is invalid or missing (None), or explicitly Passive (ZABBIX_PASSIVE/0) + if item_type not in allowed_non_agent_types: + # If it turns out to be Passive (which is default export if missing, or explicit) + errors.append(f"[ACTIVE AGENT MISMATCH] Item '{node['name']}' ({key}) at {path} is not ZABBIX_ACTIVE (found: '{item_type}'). Template '{template_name}' is marked Active.") + + # Recurse + for k, v in node.items(): + check_items(v, f"{path}.{k}") + + elif isinstance(node, list): + for i, item in enumerate(node): + check_items(item, f"{path}[{i}]") + + check_items(content) + return errors + + def check_duplicate_yaml_keys(file_path): """ @@ -510,6 +588,24 @@ def validate_yaml(file_path): if z7_warnings: warnings.extend(z7_warnings) + # ========== 8. Reserved Keys/Collision Check ========== + print("\n[8/9] Checking for Collision-Prone Keys...") + collision_warnings = validate_reserved_keys(content) + if collision_warnings: + warnings.extend(collision_warnings) + print(f" ⚠️ Found {len(collision_warnings)} potential key collisions") + else: + print(" ✅ Key safety check passed") + + # ========== 9. Active Agent Compliance ========== + print("\n[9/9] Checking Active Agent Compliance...") + active_errors = validate_active_agent_compliance(content) + if active_errors: + all_errors.extend(active_errors) + print(f" ❌ Found {len(active_errors)} items missing ZABBIX_ACTIVE type") + else: + print(" ✅ Active Agent compliance check passed") + # ========== Summary ========== print("\n" + "=" * 60)