VMware ESXi Modeling Rule

Modeling Rule

VMware ESXi

Details

IDVMware_ESXi_ModelingRule
From Version8.13.0
TagsVMwareESXi

Rules (XIF)

// ==========================================
// RULE 1: Classification
// ==========================================
// Classifies raw ESXi logs into event types using regex patterns for supporting Virtualization Story.

[RULE: esxi_event_classification]
alter
    is_command_execution = if(
        _raw_log ~= "(?i)user\s+\'\w+\'\s+running\s+command\s+\'.+\'" OR
        _raw_log ~= "\s+(?i)shell\[\d+\]\:\s+\[\w+\]\:\s.+" OR
        _raw_log ~= "\s+Console\s+log\s+content\s+for\s+the\s+command\s+.+" OR
        _raw_log ~= "started\s+from\s+\'\w+\'\s+\d+\s+with\s+cmdline\s+\'.+\'\,\s+parent\s+\d+" OR
        _raw_log ~= "crond\[\d+\]\:\s+USER\s+\w+\s+pid\s+\d+\s+cmd\s+.+"
        ),
    is_connection = if(
        _raw_log ~= "\s+(?i)SSH\s+session\s+was\s+opened\s+" OR
        _raw_log ~= "\s+(?i)SSH\s+login\s+has\s+failed\s+for\s+" OR
        _raw_log ~= "session\s+opened\s+for\s+(?:local)?\s*user\s+\w+\s+(?:on|from|by)\s+" OR
        _raw_log ~= "(?i:rejected|accepted)\s+connection\s+from\s+.*\:\d+" OR
        _raw_log ~= "Connect\s+from\s+remote\s+socket\s+\(.+\:\d+\)" OR
        _raw_log ~= "(?:Accepted|Failed|Postponed)\s+keyboard\-interactive\/pam\s+for\s+(?:invalid user |user )?\w+\s+from\s+.+\s+port\s+\d+\s+\w+"
    ),
    is_login = if(
        _raw_log ~= "Accepted\s+password\s+for\s+user\s+\w+\s+from\s+.+session" OR
        _raw_log ~= "User\s+\w*\@\S*\s+logged\s+in.*" OR
        _raw_log ~= "Cannot\s+login\s+(?:user\s)?\w*\@\S*"
    ),
    is_service_status_changed = if(
        _raw_log ~= "(?i)The\s+(?i)ESXi\s+command\s+line\s+shell\s+has\s+been\s+\w+" OR
        _raw_log ~= "Administrator\s+access\s+to\s+the\s+host\s+has\s+been\s+\w+" OR
        _raw_log ~= "SSH\s+access\s+has\s+been\s+\w+"
    ),
    is_fw_status_changed = if(
        _raw_log ~= "Firewall\s+configuration\s+has\s+changed\.\s+Operation\s+\S+\s+for\s+rule\s+set\s+" OR
        _raw_log ~= "Firewall\s+has\s+been\s+\w+"
    ),
    is_account_operations = if(
        _raw_log ~= "Password\s+was\s+changed\s+for\s+account\s+\w+\s+on\s+host\s+\S+" OR
        _raw_log ~= "Account\s+\w+\s+was\s+\w+\s+on\s+host\s+.+"
    ),
    is_file_or_datastore = if(
        _raw_log ~= "Deletion\s+of\s+file\s+or\s+directory\s+\S+\s+from\s+\S+\s+was\s+initiated\s+from" OR
        _raw_log ~= "File\s+upload\s+to\s+path\s+\'\[\S+\]\S+\'\s+was\s+initiated\s+from\s+\S+\s+and\s+completed"
    ),
    is_vm_operations = if(
        _raw_log ~= "Created\s+virtual\s+machine\s+\w+\s+on\s+\w+" OR
        _raw_log ~= "\w+\s+on\s+\w+\s+in\s+\S+\s+(?:has|is)?\s+powered\s+(?:on|off)" OR
        _raw_log ~= "Guest\s+OS\s+(?:reboot|shut down)?\s+for\s+\w+\s+on\s+\w+\s+in" OR
        _raw_log ~= "(?:Removed|Registered)\s+\w+\s+on\s+\S+\s+(?:in|from)\s+\S+"
    ),
    is_task = if(
        _raw_log ~= "Task\s+Completed\s+\:\s+\S+\-\d+\s+Status\s+\w+"
    )
| alter
    esxi_event_type = if(
        is_command_execution = TRUE, "Command Execution",
        is_connection = TRUE, "Connection",
        is_login = TRUE, "Login",
        is_service_status_changed = TRUE, "Service Status Changed",
        is_fw_status_changed = TRUE, "Firewall Status Changed",
        is_account_operations = TRUE, "Account operations",
        is_file_or_datastore = TRUE, "File/Datastore Event",
        is_vm_operations = TRUE, "VM Operations",
        is_task = TRUE, "Task Events"
    );

// ==========================================
// RULE 2: General Field Mapping
// ==========================================
// Extracts Syslog priority, facility and severity from the header, and maps them to XDM fields that are mutual to all event types.

[RULE: esxi_general_fields_mapping]
alter // Extract the <PRI> value from the syslog header
    syslog_priority = to_integer(parsed_fields -> pri)
| alter // Extract the syslog facility from the priority value by dividing it by 8
    syslog_facility = floor(divide(syslog_priority, 8))
| alter // Extract the severity remainder by subtracting (facility * 8) from the priority
    syslog_severity = subtract(syslog_priority, multiply(syslog_facility, 8))
| alter // Convert the numeric severity result back to string for mapping compatibility
    severity = to_string(syslog_severity),
    msg = parsed_fields -> msg
| alter
    xdm.event.log_level = if(
        severity = "0", XDM_CONST.LOG_LEVEL_EMERGENCY ,
        severity = "1", XDM_CONST.LOG_LEVEL_ALERT ,
        severity = "2", XDM_CONST.LOG_LEVEL_CRITICAL,
        severity = "3", XDM_CONST.LOG_LEVEL_ERROR,
        severity = "4", XDM_CONST.LOG_LEVEL_WARNING,
        severity = "5", XDM_CONST.LOG_LEVEL_NOTICE,
        severity = "6", XDM_CONST.LOG_LEVEL_INFORMATIONAL,
        severity = "7", XDM_CONST.LOG_LEVEL_DEBUG,
        severity),
    xdm.event.description = msg,
    xdm.event.type = esxi_event_type,
    xdm.event.format = if(_raw_log ~= "\<\d+\>1", "RFC-5424", _raw_log ~= "\<\d+\>", "RFC-3164"),
    xdm.event.tags = arraycreate("VIRTUALIZATION");

// ==========================================
// Modeling Rule
// ==========================================
[MODEL: dataset="vmware_esxi_raw"]

// ------------------------------------------
// Virtualization event type 1: Command Execution
// ------------------------------------------

// Log pattern 1#: User '<user_name>' running command '<command_line>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Command Execution" AND _raw_log ~= "(?i)user\s+\'\w+\'\s+running\s+command\s+\'.+\'"
| alter // Extract fields
    process = arrayindex(regextract(parsed_fields -> process_pid, "(.+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "User\s+\'(\S+)\'\s+"),0),
    command_line = arrayindex(regextract(msg, "command\s+\'(.+)\'"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process,
    xdm.target.process.pid = to_integer(pid),
    xdm.target.user.username = user_name,
    xdm.target.process.command_line = command_line,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_EXECUTION,
    xdm.event.operation_sub_type = "User interactive";

// Log pattern 2#: (?i)shell\[<session_id>]: ?(\[<user_name>\]:) <command_line>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Command Execution" AND _raw_log ~= "\s+(?i)shell\[\d+\]\:\s+\[\w+\]\:\s.+"
| alter // Extract fields
    process = arrayindex(regextract(parsed_fields -> process_pid, "(.+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "\[(\w+)\]\:"),0),
    command_line = arrayindex(regextract(msg, "\[\w+\]\:\s(.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process,
    xdm.source.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.source.process.command_line = command_line,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_EXECUTION,
    xdm.event.operation_sub_type = "User interactive";

// Log pattern 3#: \[script_name\] :: Console log content for the command <command_line>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Command Execution" AND _raw_log ~= "\s+Console\s+log\s+content\s+for\s+the\s+command\s+.+"
| alter // Extract fields
    process = arrayindex(regextract(parsed_fields -> process_pid, "(.+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    script_name = arrayindex(regextract(msg, "\[\w+\]\s+\:+\s+\[(.+)\]"),0),
    command_line = arrayindex(regextract(msg, "command\s+(.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process,
    xdm.target.process.pid = to_integer(pid),
    xdm.target.virtualization.script_name = script_name,
    xdm.target.process.command_line = command_line,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_EXECUTION,
    xdm.event.operation_sub_type = "Script commands";

// Log format 4#: started from <process_name> <pid> with cmdline '<command_line>', parent <ppid>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Command Execution" AND _raw_log ~= "started\s+from\s+\'\w+\'\s+\d+\s+with\s+cmdline\s+\'.+\'\,\s+parent\s+\d+"
| alter // Extract fields
    process_name = arrayindex(regextract(msg, "started\s+from\s+\'(\w+)\'"),0),
    pid = arrayindex(regextract(msg, "started\s+from\s+\'\w+\'\s+(\d+)"),0),
    command_line = arrayindex(regextract(msg, "with\s+cmdline\s+\'(.+)\'"),0),
    ppid = arrayindex(regextract(msg, "parent\s+(\d+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process_name,
    xdm.source.process.pid = to_integer(pid),
    xdm.source.process.command_line = command_line,
    xdm.source.process.parent_id = ppid,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_PROCESS_START,
    xdm.event.operation_sub_type = "System process start";

// Log format 5#: crond\[<pid>\]: USER <user_name> pid <pid> cmd <command_line>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Command Execution" AND _raw_log ~= "crond\[\d+\]\:\s+USER\s+\w+\s+pid\s+\d+\s+cmd\s+.+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    src_pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "USER\s+(\w+)"),0),
    pid = arrayindex(regextract(msg, "pid\s+(\d+)"),0),
    command_line = arrayindex(regextract(msg, "cmd\s+(.+)"),0)
| alter
    exec_token = arrayindex(regextract(command_line, "^(\S+)"),0)
| alter    
    exec_name = arrayindex(regextract(exec_token, "([^\/]+)$"),0),
    exec_path = if(exec_token ~= "\/", exec_token, null)    
| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process_name,
    xdm.source.process.pid= to_integer(src_pid),
    xdm.source.user.username = user_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.target.process.command_line = command_line,
    xdm.target.process.name = exec_name,
    xdm.target.process.executable.path = exec_path,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_PROCESS_START,
    xdm.event.operation_sub_type = "crond process start";

// ------------------------------------------
// Virtualization event type 2: Connection
// ------------------------------------------

// Log format 1#: "Event \d+ : SSH session was opened ?for '<user_name>@<source_ip>'."
// Log format 2#: "Event \d+ : SSH login has failed for '<user_name>@<source_ip>'"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "\s+(?i)SSH\s+(?:session|login)\s+(?:was|has)\s+(?:opened|failed)\s+for\s+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\[*"),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "\'(\w+)\@"),0),
    source_ip = arrayindex(regextract(msg, "\'\w+\@(.+)?\'"),0),
    outcome_reason = arrayindex(regextract(msg, "SSH\s+.+"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = if(pid != null, to_integer(pid), null),
    xdm.target.user.username = user_name,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.event.outcome = if(
        msg ~= "SSH\s+session\s+was\s+opened",XDM_CONST.OUTCOME_SUCCESS,
        msg ~= "SSH\s+login\s+has\s+failed", XDM_CONST.OUTCOME_FAILED),
    xdm.event.outcome_reason = coalesce(outcome_reason,msg),
    xdm.network.application_protocol = "ssh",
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_AUTHENTICATION,
    xdm.event.operation_sub_type = if(
        msg ~= "SSH\s+session\s+was\s+opened", "ssh opened",
        msg ~= "SSH\s+login\s+has\s+failed", "ssh failed");

// Log format 3#: "session opened for ?local user <user_name> from [<source_ip>]"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "session\s+opened\s+for\s+(?:local)?\s*user\s+\w+\s+from\s+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\[*"),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "user\s+(\w+)\s+"),0),
    source_ip = arrayindex(regextract(msg, "user\s+\w+\s+from\s+\[(.+)?\]"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.source.user.scope = if(msg ~= "session\s+opened\s+for\s+local\s+user\s+", XDM_CONST.SCOPE_TYPE_LOCAL),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "session opened",
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = msg;

// Log format 4#: "session opened for ?local user <user_name> by (uid=<uid>)"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "session\s+opened\s+for\s+(?:local)?\s*user\s+\w+\s+by\s+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\[*"),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "user\s+(\w+)\s+"),0),
    uid = arrayindex(regextract(msg, "user\s+\w+\s+by\s+\(uid\=(\d+)"),0),
    outcome_reason = arrayindex(regextract(msg, "session\s+opened\s+for.+"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.source.user.identifier = uid,
    xdm.source.user.scope = if(msg ~= "session\s+opened\s+for\s+local\s+user\s+", XDM_CONST.SCOPE_TYPE_LOCAL),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "session opened",
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg);

// Log format 5#: "rejected connection from .*<source_ip>.*:<source_port>"
// Log format 6#: "accepted connection from .*<source_ip>.*:<source_port>"
// Log format 7#: "Accepted connection from <<ipv6>:<ipv4>>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "(?i:rejected|accepted)\s+connection\s+from\s+.*\:\d+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    source_ip = arrayindex(regextract(msg, "connection\s+from\s+[\'\"\<]*(.+?)\:"),0),
    source_ipv4 = arrayindex(regextract(msg, "[\<\"\'].+\:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\>|\"|\')"),0),
    source_ipv6 = arrayindex(regextract(msg, "[\<\"\'](.+)\:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:\>|\"|\')"),0),
    source_port = arrayindex(regextract(msg, "connection\s+from\s+.+\:(\d+)[\"\'\>]"),0),
    error = arrayindex(regextract(msg, "error\s+\"(.+?)\""),0),
    server_name = arrayindex(regextract(msg, "ServerName\s+\"([^\"]+)"),0),
    outcome_reason = arrayindex(regextract(msg, "((?i:rejected|accepted).+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = coalesce(server_name, parsed_fields -> hostname),
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip, is_ipv4(source_ipv4) = TRUE, source_ipv4),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip, is_ipv6(source_ipv6) = TRUE, source_ipv6),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip), is_known_private_ipv4(source_ipv4) = TRUE, arraycreate(source_ipv4)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip), is_known_private_ipv6(source_ipv6) = TRUE, arraycreate(source_ipv6)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip), is_known_private_ipv4(source_ipv4) = FALSE, arraycreate(source_ipv4)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip), is_known_private_ipv6(source_ipv6) = FALSE, arraycreate(source_ipv6)),
    xdm.source.port = to_integer(source_port),
    xdm.event.outcome_reason = coalesce(error, outcome_reason, msg),
    xdm.event.outcome = if(
        msg ~= "accepted", XDM_CONST.OUTCOME_SUCCESS,
        msg ~= "rejected", XDM_CONST.OUTCOME_FAILED),
    xdm.event.operation = if(
        msg ~= "rejected",XDM_CONST.OPERATION_TYPE_REJECT,
        msg ~= "accepted", XDM_CONST.OPERATION_TYPE_AUTHENTICATION),
    xdm.event.operation_sub_type = if(
        msg ~= "rejected","rejected connection",
        msg ~= "accepted", "accepted connection");

// Log format 8#: "Connect from remote socket (<source_ip>:<source_port>)."
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "Connect\s+from\s+remote\s+socket\s+\(.+\:\d+\)"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    source_ip = arrayindex(regextract(msg, "\((.+)?\:"),0),
    source_port = arrayindex(regextract(msg, "\(.+\:(\d+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.source.port = to_integer(source_port),
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = msg,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "remote socket connection";

// Log format 9#: "Accepted keyboard-interactive/pam for <user_name> from <source_ip> port <source_port> <protocol>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "Accepted\s+keyboard\-interactive\/pam\s+for\s+(?:invalid user |user )?\w+\s+from\s+.+\s+port\s+\d+\s+\w+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "for\s+(?:user |invalid user )?(\w+)?"),0),
    source_ip = arrayindex(regextract(msg, "from\s+(\S+)"),0),
    source_port = arrayindex(regextract(msg, "port\s+(\d+)"),0),
    protocol = arrayindex(regextract(msg, "port\s+\d+\s+(\w+)"),0)

| alter // Map XDM mapping
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.source.port = to_integer(source_port),
    xdm.network.application_protocol = protocol,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = msg,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "keyboard interactive session";

// Log format 10#: "Failed|Postponed keyboard-interactive/pam for ?invalid user <user_name> from <source_ip> port <source_port> <protocol>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Connection" AND _raw_log ~= "(?:Failed|Postponed)\s+keyboard\-interactive\/pam\s+for\s+(?:invalid user |user )?\w+\s+from\s+.+\s+port\s+\d+\s+\w+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "for\s+(?:user |invalid user )?(\w+)?"),0),
    source_ip = arrayindex(regextract(msg, "from\s+(\S+)"),0),
    source_port = arrayindex(regextract(msg, "port\s+(\d+)"),0),
    protocol = arrayindex(regextract(msg, "port\s+\d+\s+(\w+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.target.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.target.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.target.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.target.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.target.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.target.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.target.port = to_integer(source_port),
    xdm.network.application_protocol = protocol,
    xdm.event.outcome = XDM_CONST.OUTCOME_FAILED,
    xdm.event.outcome_reason = msg,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "interactive session failed";

// ------------------------------------------
// Virtualization event type 3: Login
// ------------------------------------------

// Log format 1#: "Accepted password for user <user_name> from <source_ip> - session=<session_id>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Login" AND _raw_log ~= "Accepted\s+password\s+for\s+user\s+\w+\s+from\s+.+session"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "user\s+(\S+)\s+"),0),
    source_ip = arrayindex(regextract(msg, "from\s+(\S+)\s+"),0),
    session_id = arrayindex(regextract(msg, "session\=(\S+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.network.session_id = session_id,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = "Accepted password",
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_AUTHENTICATION,
    xdm.event.operation_sub_type = "login success";

// Log format 2#: "Event \d+ : "Event \d+ : User <user_name>@<source_ip> logged in ?as ?<user_agent>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Login" AND _raw_log ~= "User\s+\w*\@\S*\s+logged\s+in.*"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "User\s+([^@]+)"),0),
    user_agent = arrayindex(regextract(msg, "logged\s+in\s+(?:as\s*)*(.+)"),0),
    source_ip = arrayindex(regextract(msg, "\@(\S+)\s+logged"),0),
    outcome_reason = arrayindex(regextract(msg, "Event\s+\d+\s+\:\s+(User.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.source.user_agent = user_agent,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_AUTH_LOGIN,
    xdm.event.operation_sub_type = "login success";

// Log format 3#: "Event \d+ : Cannot login ?user <user_name>@<source_ip> ?(: <PLAIN_TEXT_REASON>)"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Login" AND _raw_log ~= "Cannot\s+login\s+(?:user\s)?\w*\@\S*"
| alter // Extract fields
    process_name1 = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    process_name2 = arrayindex(regextract(msg, "(\w+)\["),0),
    pid1 = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    pid2 = arrayindex(regextract(msg, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "login\s+(?:user\s*)*([^@]+)"),0),
    source_ip = arrayindex(regextract(msg, "login.+\@([^:]+)"),0),
    reason = arrayindex(regextract(msg, "login.+\@\S+\:\s+(.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = coalesce(process_name2, process_name1),
    xdm.target.process.pid = coalesce(to_integer(pid2),to_integer(pid1)),
    xdm.source.user.username = user_name,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.event.outcome_reason = reason,
    xdm.event.outcome = XDM_CONST.OUTCOME_FAILED,
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_AUTH_LOGIN,
    xdm.event.operation_sub_type = "login failed";

// ------------------------------------------
// Virtualization event type 4: Service Status Changed
// ------------------------------------------

// Log format 1#: "(?i)The ESX?(i) command line shell has been <enabled_disabled>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Service Status Changed" AND _raw_log ~= "(?i)The\s+(?i)ESXi\s+command\s+line\s+shell\s+has\s+been\s+\w+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    command_line = arrayindex(regextract(msg, "command\s+line\s+(.+?)\s+has"),0),
    status = arrayindex(regextract(msg, "has\s+been\s+(\w+)"),0),
    outcome_reason =  arrayindex(regextract(msg, "\:\s(The.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process_name,
    xdm.source.process.pid  = to_integer(pid),
    xdm.source.process.command_line = command_line,
    xdm.event.operation_sub_type = status,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_UPDATE;

// Log format 2#: "Event \d+ : Administrator access to the host has been <enabled_disabled>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Service Status Changed" AND _raw_log ~= "Administrator\s+access\s+to\s+the\s+host\s+has\s+been\s+\w+"
| alter // Extract fields
    process_name1 = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    process_name2 = arrayindex(regextract(msg, "(\w+)\["),0),
    pid1 = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    pid2 = arrayindex(regextract(msg, "\[(\d+)"),0),
    status = arrayindex(regextract(msg, "has\s+been\s+(\w+)"),0),
    outcome_reason =  arrayindex(regextract(msg, "Event\s+\d+\s+\:\s+(.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = coalesce(process_name2, process_name1),
    xdm.source.process.pid = coalesce(to_integer(pid2), to_integer(pid1)),
    xdm.event.operation_sub_type = status,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_UPDATE;

// Log format 3#: "Event \d+ : SSH access has been <enabled_disabled>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Service Status Changed" AND _raw_log ~= "SSH\s+access\s+has\s+been\s+\w+"
| alter // Extract fields
    process_name1 = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    process_name2 = arrayindex(regextract(msg, "(\w+)\["),0),
    pid1 = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    pid2 = arrayindex(regextract(msg, "\[(\d+)"),0),
    status = arrayindex(regextract(msg, "has\s+been\s+(\w+)"),0),
    outcome_reason =  arrayindex(regextract(msg, "\:\s(SSH.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = coalesce(process_name2, process_name1),
    xdm.source.process.pid = coalesce(to_integer(pid2), to_integer(pid1)),
    xdm.event.operation_sub_type = status,
    xdm.network.application_protocol = "ssh",
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_UPDATE;

// ------------------------------------------
// Virtualization event type 5: Firewall Status Changed
// ------------------------------------------

// Log format 1#: "Firewall configuration has changed. Operation <operation> for rule set <rule_set_name> <succeeded_failed>."
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Firewall Status Changed" AND _raw_log ~= "Firewall\s+configuration\s+has\s+changed\.\s+Operation\s+\S+\s+for\s+rule\s+set\s+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    operation_name = arrayindex(regextract(msg, "Operation\s+\'(.+?)\'"),0),
    rule_set_name = arrayindex(regextract(msg,"rule\s+set\s+(\S+)\s+" ),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(Firewall.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process_name,
    xdm.source.process.pid = to_integer(pid),
    xdm.observer.action = operation_name,
    xdm.network.rule = rule_set_name,
    xdm.event.outcome = if(
        msg ~= "rule\s+set\s+\S+\s+succeeded", XDM_CONST.OUTCOME_SUCCESS,
        msg ~= "rule\s+set\s+\S+\s+fail", XDM_CONST.OUTCOME_FAILED),
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CONFIG_CHANGE,
    xdm.event.operation_sub_type = "firewall config change";

// Log format 2#: "Event \d+ : Firewall has been <enabled_disabled>."
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Firewall Status Changed" AND _raw_log ~= "Firewall\s+has\s+been\s+\w+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(Firewall.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process_name,
    xdm.source.process.pid = to_integer(pid),
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.observer.action = if(
        msg ~= "disabled", "disabled",
        msg ~= "enabled", "enabled"),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CONFIG_CHANGE,
    xdm.event.operation_sub_type = "firewall status change";

// ------------------------------------------
// Virtualization event type 6/7: Account Operations
// ------------------------------------------

// Log format 1#: "Event \d+ : Password was changed for account <user_name> on host <target_esxi_host>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Account operations" AND _raw_log ~= "Password\s+was\s+changed\s+for\s+account\s+\w+\s+on\s+host\s+\S+"
| alter // Extract fields
    process_name1 = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    process_name2 = arrayindex(regextract(msg, "(\w+)\["),0),
    pid1 = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    pid2 = arrayindex(regextract(msg, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "changed\s+for\s+account\s+(\S+)"),0),
    target_esxi_host = arrayindex(regextract(msg, "on\s+host\s+(\S+)"),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(Password.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = coalesce(target_esxi_host,parsed_fields -> hostname),
    xdm.source.process.name = coalesce(process_name2, process_name1),
    xdm.source.process.pid = coalesce(to_integer(pid2), to_integer(pid1)),
    xdm.source.user.username = user_name,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_UPDATE,
    xdm.event.operation_sub_type = "password change";

// Log format 2#: "Event \d+ : Account <user_name> was created on host <target_esxi_host>"
// Log format 3#: "Event \d+ : Account <user_name> was removed on host <target_esxi_host>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Account operations" AND _raw_log ~= "Account\s+\w+\s+was\s+\w+\s+on\s+host\s+.+"
| alter // Extract fields
    process_name1 = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    process_name2 = arrayindex(regextract(msg, "(\w+)\["),0),
    pid1 = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    pid2 = arrayindex(regextract(msg, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "Account\s+(\w+)"),0),
    target_esxi_host = arrayindex(regextract(msg, "on\s+host\s+(\S+)"),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(Account.+)"),0)

| alter // Map to XDM
    xdm.source.host.hostname = coalesce(target_esxi_host, parsed_fields -> hostname),
    xdm.source.process.name = coalesce(process_name2, process_name1),
    xdm.source.process.pid = coalesce(to_integer(pid2), to_integer(pid1)),
    xdm.source.user.username = user_name,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = if(
        msg ~= "was\s+created", XDM_CONST.OPERATION_TYPE_CREATE,
        msg ~= "was\s+removed" , XDM_CONST.OPERATION_TYPE_DELETE),
    xdm.event.operation_sub_type = if(
        msg ~= "was\s+created", "account created",
        msg ~= "was\s+removed" , "account removed");

// ------------------------------------------
// Virtualization event type 8: File/Datastore
// ------------------------------------------

// Log format 1#: "Event \d+ : Deletion of file or directory <file_path> from <location> was initiated from '<user_agent>@<source_ip>' and completed with status '<status>'"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "File/Datastore Event" AND _raw_log ~= "Deletion\s+of\s+file\s+or\s+directory\s+\S+\s+from\s+\S+\s+was\s+initiated\s+from"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    file_path = arrayindex(regextract(msg, "file\s+or\s+directory\s+(\S+)"),0),
    location = arrayindex(regextract(msg, "Deletion\s+of\s+file\s+or\s+directory\s+\S+\s+from\s+(\S+)\s+"),0),
    user_agent = arrayindex(regextract(msg, "initiated\s+from\s+\'(.+?)\@"),0),
    source_ip = arrayindex(regextract(msg, "initiated\s+from\s+\'.+?\@(.+?)\'"),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(Deletion.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.target.file.path = file_path,
    xdm.target.virtualization.data_store.name = location,
    xdm.source.user_agent = user_agent,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.event.outcome = if(
        msg ~= "completed\s+with\s+status\s+\'[Ss]uccess", XDM_CONST.OUTCOME_SUCCESS,
        msg ~= "completed\s+with\s+status\s+\'[Ff]ail", XDM_CONST.OUTCOME_FAILED),
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.is_completed = if(msg ~= "and\s+completed", TRUE),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_FILE_REMOVE,
    xdm.event.operation_sub_type = "file delete";

// Log format 2#: "Event \d+ : File upload to path '\[<location>\]<file_path>' was initiated from '<user_agent>@<source_ip>' and completed with status '<status>'"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "File/Datastore Event" AND _raw_log ~= "File\s+upload\s+to\s+path\s+\'\[\S+\]\S+\'\s+was\s+initiated\s+from\s+\S+\s+and\s+completed"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    location = arrayindex(regextract(msg, "File\s+upload\s+to\s+path\s+\'\[(\S+)\]"),0),
    file_path = arrayindex(regextract(msg, "\'\[.+?](\S+)\'"),0),
    user_agent = arrayindex(regextract(msg, "initiated\s+from\s+\'(.+?)\@"),0),
    source_ip = arrayindex(regextract(msg, "initiated\s+from\s+\'.*\@(.*?)\'"),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(File.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.target.virtualization.data_store.name = location,
    xdm.target.file.path = file_path,
    xdm.source.user_agent = user_agent,
    xdm.source.ipv4 = if(is_ipv4(source_ip) = TRUE, source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip) = TRUE, source_ip),
    xdm.source.host.ipv4_addresses = if(is_known_private_ipv4(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip) = TRUE, arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE,arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.event.outcome = if(
        msg ~= "completed\s+with\s+status\s+\'[Ss]uccess", XDM_CONST.OUTCOME_SUCCESS,
        msg ~= "completed\s+with\s+status\s+\'[Ff]ail", XDM_CONST.OUTCOME_FAILED),
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.is_completed = if(msg ~= "and\s+completed", TRUE),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_FILE_WRITE,
    xdm.event.operation_sub_type = "file upload";

// ------------------------------------------
// Virtualization event type 9: VM Operations
// ------------------------------------------

// Log format 1#: Event \d+ : Created virtual machine <vm_name> on <target_esxi_host> in <data_center_name>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "VM Operations" AND _raw_log ~= "Created\s+virtual\s+machine\s+\w+\s+on\s+\w+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user = arrayindex(regextract(msg, "user\=([^\]]+)"),0),
    vm_name = arrayindex(regextract(msg, "virtual\s+machine\s+(\S+)"),0),
    target_esxi_host = arrayindex(regextract(msg, "virtual\s+machine\s+\S+\s+on\s+([^,]+)"),0),
    data_center_name = arrayindex(regextract(msg, "virtual\s+machine\s+\S+\s+on\s+\S+\s+in\s+(\S+)"),0),
    outcome_reason = arrayindex(regextract(msg, "\:\s+(Created.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = coalesce(target_esxi_host, parsed_fields -> hostname),
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user,
    xdm.target.virtualization.vm.hostname = vm_name,
    xdm.target.virtualization.data_center.name = data_center_name,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason,msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "VM Created",
    xdm.event.original_event_type = "VM Created";

// Log format 2#: "Event \d+ : <vm_name> on <target_esxi_host> in <data_center> has powered on"
// Log format 3#: "Event \d+ : <vm_name> on <target_esxi_host> in <data_center> is powered off"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "VM Operations" AND _raw_log ~= "\w+\s+on\s+\w+\s+in\s+\S+\s+(?:has|is)?\s+powered\s+(?:on|off)"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    vm_name = arrayindex(regextract(msg, "\:\s+(\S+)"),0),
    target_esxi_host = arrayindex(regextract(msg, "on\s+(\S+)"),0),
    data_center_name = arrayindex(regextract(msg, "on\s+\S+\s+in\s+(\S+)"),0),
    outcome_reason = arrayindex(regextract(msg, "Event\s+\d+\s+\:\s+(.+)"),0)

| alter // Map to XDM
    xdm.target.host.hostname = coalesce(target_esxi_host, parsed_fields -> hostname),
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.target.virtualization.vm.hostname = vm_name,
    xdm.target.virtualization.data_center.name = data_center_name,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason,msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_STATUS_CHANGE,
    xdm.event.operation_sub_type = if(
        msg ~= "has\s+powered\s+on", "VM Power On",
        msg ~= "is\s+powered\s+off",  "VM Power Off"),
    xdm.event.original_event_type = if(
        msg ~= "has\s+powered\s+on", "VM Power On",
        msg ~= "is\s+powered\s+off",  "VM Power Off");

// Log format 4#: "Event \d+ : Guest OS reboot for <vm_name> on <target_esxi_host> in <data_center>"
// Log format 5#: "Event \d+ : Guest OS shut down for <vm_name> on <target_esxi_host> in <data_center>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "VM Operations" AND _raw_log ~= "Guest\s+OS\s+(?:reboot|shut down)?\s+for\s+\w+\s+on\s+\w+\s+in"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user = arrayindex(regextract(msg, "user\=([^\]]+)"),0),
    vm_name = arrayindex(regextract(msg, "Guest\s+OS\s+(?:reboot|shut down)?\s+for\s+(\w+)\s+"),0),
    target_esxi_host = arrayindex(regextract(msg, "Guest\s+OS\s+(?:reboot|shut down)?\s+for\s\w+\s+on\s+(\w+)\s+in"),0),
    data_center_name = arrayindex(regextract(msg, "Guest\s+OS\s+(?:reboot|shut down)?\s+for\s\w+\s+on\s+\w+\s+in\s+(\S+)"),0),
    outcome_reason = arrayindex(regextract(msg, "Guest\s+OS\s+.+"),0)

| alter // Map to XDM
    xdm.target.host.hostname = coalesce(target_esxi_host, parsed_fields -> hostname),
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user,
    xdm.target.virtualization.vm.hostname = vm_name,
    xdm.target.virtualization.data_center.name = data_center_name,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason,msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_STATUS_CHANGE,
    xdm.event.operation_sub_type = if(
        msg ~= "Guest\s+OS\s+reboot", "Guest OS VM Reboot",
        msg ~= "Guest\s+OS\s+shut down", "Guest OS VM Shutdown"),
    xdm.event.original_event_type = if(
        msg ~= "Guest\s+OS\s+reboot", "Guest OS VM Reboot",
        msg ~= "Guest\s+OS\s+shut down", "Guest OS VM Shutdown");


// Log format 6#: "Event \d+ : Removed <vm_name> on <target_esxi_host> from <data_center>"
// Log format 7#: "Event \d+ : Registered <vm_name> on <target_esxi_host> in <data_center>"
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "VM Operations" AND _raw_log ~= "(?:Removed|Registered)\s+\w+\s+on\s+\S+\s+(?:in|from)\s+\S+"
| alter // Extract fields
    target_esxi_host = arrayindex(regextract(msg, "(?:Removed|Registered)\s+\w+\s+on\s+(\S+)\s+"),0),
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user = arrayindex(regextract(msg, "user\=([^\]]+)"),0),
    vm_name = arrayindex(regextract(msg, "(?:Removed|Registered)\s+(\w+)\s+"),0),
    data_center_name = arrayindex(regextract(msg, "(?:Removed|Registered)\s+\w+\s+on\s+\S+\s+(?:in|from)\s+(\S+)"),0),
    outcome_reason = arrayindex(regextract(msg, "(?:Removed|Registered)\s+.+"),0)

| alter // Map to XDM
    xdm.target.host.hostname = coalesce(target_esxi_host, parsed_fields -> hostname),
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user,
    xdm.target.virtualization.vm.hostname = vm_name,
    xdm.target.virtualization.data_center.name = data_center_name,
    xdm.event.outcome = XDM_CONST.OUTCOME_SUCCESS,
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = if(
        msg ~= "Removed\s+.+", XDM_CONST.OPERATION_TYPE_DELETE,
        msg ~= "Registered\s+.+", XDM_CONST.OPERATION_TYPE_CONFIG_CHANGE),
    xdm.event.operation_sub_type = if(
        msg ~= "Removed\s+.+", "VM Removed",
        msg ~= "Registered\s+.+","VM Registered"),
    xdm.event.original_event_type = if(
        msg ~= "Removed\s+.+", "VM Removed",
        msg ~= "Registered\s+.+","VM Registered");

// ------------------------------------------
// Virtualization event type 10: Task Events
// ------------------------------------------

// Log format: Task Completed : <vcenter_event>-<vcenter_event_unique_task_id> Status <status>
call esxi_event_classification
| call esxi_general_fields_mapping
| filter esxi_event_type = "Task Events" AND _raw_log ~= "Task\s+Completed\s+\:\s+\S+\-\d+\s+Status\s+\w+"
| alter // Extract fields
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    user_name = arrayindex(regextract(msg, "user\=([^\]]+)"),0),
    vcenter_event = arrayindex(regextract(msg, "Task\s+Completed\s+\:\s+(\S+)\-\d+"),0),
    vcenter_event_unique_task_id = arrayindex(regextract(msg, "Task\s+Completed\s+\:\s+\S+\-(\d+)"),0),
    outcome_reason = arrayindex(regextract(msg, "Task\s+Completed.+"),0)

| alter // Map to XDM
    xdm.target.host.hostname = parsed_fields -> hostname,
    xdm.target.process.name = process_name,
    xdm.target.process.pid = to_integer(pid),
    xdm.source.user.username = user_name,
    xdm.target.virtualization.task.name = vcenter_event,
    xdm.target.virtualization.task.id = vcenter_event_unique_task_id,
    xdm.event.outcome = if(
        msg ~= "Status\s+[Ss]uccess", XDM_CONST.OUTCOME_SUCCESS,
        msg ~= "Status\s+[Fa]il", XDM_CONST.OUTCOME_FAILED),
    xdm.event.outcome_reason = coalesce(outcome_reason, msg),
    xdm.event.operation = XDM_CONST.OPERATION_TYPE_CREATE,
    xdm.event.operation_sub_type = "ESXi Task",
    xdm.event.is_completed = true;

call esxi_event_classification
| filter esxi_event_type = null
// Extract the <PRI> value from the syslog header
| alter
    syslog_priority = to_integer(parsed_fields -> pri)
// Extract the syslog facility from the priority value by dividing it by 8
| alter
    syslog_facility = floor(divide(syslog_priority, 8))
| alter
    syslog_severity = subtract(syslog_priority, multiply(syslog_facility, 8)), // Extract the severity remainder by subtracting (facility * 8) from the priority
    username_1 = arrayindex(regextract(_raw_log ,"User\s\'([a-zA-Z]+)\'"),0),
    username_2 = arrayindex(regextract(_raw_log ,"for\s+(\S+)\s+from"),0),
    username_3 = arrayindex(regextract(_raw_log , "[Uu]ser\s+(?:name\:\s+)*(\w+)"),0),
    command_line = arrayindex( regextract(_raw_log ,"command\s\'([^\']+)\'"),0),
    source_ip_1 = arrayindex( regextract(_raw_log ,"from\s(\d+\.\d+\.\d+\.\d+)\s"),0),
    source_ip_2 = arrayindex(regextract(_raw_log, "(\S+)\s+port"),0),
    source_port = arrayindex( regextract(_raw_log ,"port\s+(\d+)"),0),
    msg_1 = arrayindex( regextract(_raw_log ,"\:\s+(.*)$"),0),
    msg_2 = arrayindex( regextract(_raw_log ,"T\d+\:\d+\:\d+\.*\d*Z\|\s[a-zA-Z0-9\-\_]+\|\s\w+\:\s*(.*)"),0),
    msg_3 = arrayindex(regextract(_raw_log ,"line \d+\:(.*)$"),0),
    path_1 = arrayindex( regextract(_raw_log ,"path\:\s*(\S+)"),0),
    path_2 = arrayindex( regextract(_raw_log ,"on\s(\/\S+)"),0),
    mac_address = arrayindex( regextract(_raw_log , "MAC Address:\s([a-zA-Z0-9]+\:[a-zA-Z0-9]+\:[a-zA-Z0-9]+\:[a-zA-Z0-9]+\:[a-zA-Z0-9]+\:[a-zA-Z0-9]+)"),0)
| alter
    msg = coalesce(msg_1, msg_2, msg_3, parsed_fields -> msg),
    path = coalesce(path_1, path_2),
    username = coalesce(username_1, username_2, username_3),
    source_ip = coalesce(source_ip_1, source_ip_2),
    process_name = arrayindex(regextract(parsed_fields -> process_pid, "(\w+)\["),0),
    pid = arrayindex(regextract(parsed_fields -> process_pid, "\[(\d+)"),0),
    severity = to_string(syslog_severity) // Convert the numeric severity result back to string for mapping compatibility
| alter // XDM Mapping
    xdm.source.user.username = username,
    xdm.source.host.hostname = parsed_fields -> hostname,
    xdm.source.process.name = process_name,
    xdm.source.process.pid = to_integer(pid),
    xdm.target.process.command_line = command_line,
    xdm.source.ipv4 = if(is_ipv4(source_ip), source_ip),
    xdm.source.ipv6 = if(is_ipv6(source_ip), source_ip),
    xdm.source.host.ipv4_addresses = if( is_known_private_ipv4(source_ip), arraycreate(source_ip)),
    xdm.source.host.ipv6_addresses = if(is_known_private_ipv6(source_ip), arraycreate(source_ip)),
    xdm.source.host.ipv4_public_addresses = if(is_known_private_ipv4(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.source.host.ipv6_public_addresses = if(is_known_private_ipv6(source_ip) = FALSE, arraycreate(source_ip)),
    xdm.source.port = to_number(source_port),
    xdm.event.description = msg,
    xdm.event.format = if(_raw_log ~= "\<\d+\>1", "RFC-5424", _raw_log ~= "\<\d+\>", "RFC-3164"),
    xdm.event.log_level = if(
        severity = "0", XDM_CONST.LOG_LEVEL_EMERGENCY ,
        severity = "1", XDM_CONST.LOG_LEVEL_ALERT ,
        severity = "2", XDM_CONST.LOG_LEVEL_CRITICAL,
        severity = "3", XDM_CONST.LOG_LEVEL_ERROR,
        severity = "4", XDM_CONST.LOG_LEVEL_WARNING,
        severity = "5", XDM_CONST.LOG_LEVEL_NOTICE,
        severity = "6", XDM_CONST.LOG_LEVEL_INFORMATIONAL,
        severity = "7", XDM_CONST.LOG_LEVEL_DEBUG,
        severity),
    xdm.target.process.executable.path = path,
    xdm.source.host.mac_addresses = arraycreate(coalesce(mac_address ,""));

Schema

vmware_esxi_raw

Field Type Array?
_raw_log string
parsed_fields string
Raw JSON
{
  "vmware_esxi_raw": {
    "_raw_log": {
      "type": "string",
      "is_array": false
    },
    "parsed_fields": {
       "type": "string",
        "is_array": false
      }
  }
}