IAS NPS radius parser with dissect on filebeat

Hello People,

I was searching for a solution to parse our NPS logs via filebeat and dissect and could not seems to find a related topic for this.
I am using fleet and a custom log integration to collect the logs
So here is my way to do it:

# 2022.Sept.09 fs Parse NPS/IAS/Radius logs
#
tags:
- Windows
- aai-nps
logtype: nps
#include_lines: ['^[0-9]{4}']
#exclude_lines: ['healthcheck','HealthMailbox']
processors:
# See https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/cc771748(v=ws.10)?redirectedfrom=MSDN
# https://www.radiusreporting.com/IAS-Standard-Attribute-Table.html
# https://www.deepsoftware.com/iasviewer/attributeslist.html
  - dissect:
      description: 'initial dissect with ending in action and reason'
      tokenizer: '%{observer.ip},%{user.name},%{@timestamp},%{+@timestamp},%{service.name},%{observer.name},%{rest->}4136,%{event.action},4142,%{event.reason}'
      field: "message"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
#
  - dissect:
      description: 'smallest dissect first'
      tokenizer: '%{*p1},%{&p1},%{*p2},%{&p2},%{*p3},%{&p3},%{*p4},%{&p4},%{*p5},%{&p5},%{*p6},%{&p6},%{*p7},%{&p7},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
  - dissect:
      description: 'iterate dissects that contain comma'
      tokenizer: '%{*p1},%{&p1},%{rest->}'
      field: "rest"
      target_prefix: ""
      ignore_failure: true
      overwrite_keys: true
      ignore_missing: true
      when:
        regexp:
          rest: '^.*,.*,'
#
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''
#
# If you have more IAS fields, you might need to iterate more
#
  - if:
      has_fields: '4'
    then: 
      - copy_fields:
          fields:
            - from: "4"
              to: nps.nas-ip-address
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '8'
    then: 
      - copy_fields:
          fields:
            - from: "8"
              to: nps.framed-ip-address
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '11'
    then: 
      - copy_fields:
          fields:
            - from: "11"
              to: nps.filter-id
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '25'
    then: 
      - copy_fields:
          fields:
            - from: "25"
              to: nps.class
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '26'
    then: 
      - copy_fields:
          fields:
            - from: "26"
              to: nps.vendor-specific
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '30'
    then: 
      - copy_fields:
          fields:
            - from: "30"
              to: nps.called-station-id
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '31'
    then: 
      - copy_fields:
          fields:
            - from: "31"
              to: nps.calling-station-id
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '32'
    then: 
      - copy_fields:
          fields:
            - from: "32"
              to: nps.nas-identifier
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '40'
    then: 
      - copy_fields:
          fields:
            - from: "40"
              to: nps.acct-status-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '42'
    then: 
      - copy_fields:
          fields:
            - from: "42"
              to: nps.acct-input-octets
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '44'
    then: 
      - copy_fields:
          fields:
            - from: "44"
              to: nps.acct-session-id
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '50'
    then: 
      - copy_fields:
          fields:
            - from: "50"
              to: nps.acct-multi-ssn-id
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '61'
    then: 
      - copy_fields:
          fields:
            - from: "61"
              to: nps.nas-port-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '62'
    then: 
      - copy_fields:
          fields:
            - from: "62"
              to: nps.port-limit
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '64'
    then: 
      - copy_fields:
          fields:
            - from: "64"
              to: nps.tunnel-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '65'
    then: 
      - copy_fields:
          fields:
            - from: "65"
              to: nps.tunnel-medium-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '66'
    then: 
      - copy_fields:
          fields:
            - from: "66"
              to: nps.tunnel-client-endpt
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '77'
    then: 
      - copy_fields:
          fields:
            - from: "77"
              to: nps.connect-info
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '81'
    then: 
      - copy_fields:
          fields:
            - from: "81"
              to: nps.tunnel-pvt-group-id
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '126'
    then: 
      - copy_fields:
          fields:
            - from: "126"
              to: nps.ascend-route-preference
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4108'
    then: 
      - copy_fields:
          fields:
            - from: "4108"
              to: client.ip
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4116'
    then: 
      - copy_fields:
          fields:
            - from: "4116"
              to: nps.nas-manufacturer
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4120'
    then: 
      - copy_fields:
          fields:
            - from: "4120"
              to: nps.ms-chap-domain
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4127'
    then: 
      - copy_fields:
          fields:
            - from: "4127"
              to: nps.authentication-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4128'
    then: 
      - copy_fields:
          fields:
            - from: "4128"
              to: nps.client-friendly-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4129'
    then:
      - copy_fields:
          fields:
            - from: "4129"
              to: nps.sam-account-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4130'
    then:
      - copy_fields:
          fields:
            - from: "4130"
              to: nps.fully-qualified-user-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4132'
    then:
      - copy_fields:
          fields:
            - from: "4132"
              to: nps.eap-friendly-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4136'
    then:
      - copy_fields:
          fields:
            - from: "4136"
              to: event.action
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4142'
    then:
      - copy_fields:
          fields:
            - from: "4142"
              to: event.reason
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4149'
    then:
      - copy_fields:
          fields:
            - from: "4149"
              to: nps.np-policy-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4154'
    then: 
      - copy_fields:
          fields:
            - from: "4154"
              to: nps.proxy-policy-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '4155'
    then:
      - copy_fields:
          fields:
            - from: "4155"
              to: nps.provider-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '8132'
    then:
      - copy_fields:
          fields:
            - from: "8132"
              to: nps.ms-network-access-server-type
          fail_on_error: false
          ignore_missing: true
#
  - if:
      has_fields: '8138'
    then:
      - copy_fields:
          fields:
            - from: "8138"
              to: nps.ms-machine-name
          fail_on_error: false
          ignore_missing: true
#
  - if:
      equals:
        nps.ms-network-access-server-type: "0"
    then:
      - add_fields:
          target: 'nps'
          fields:
            ms-network-access-server-type: ["0","Unspecified"]
  - if:
      equals:
        nps.ms-network-access-server-type: "1"
    then:
      - add_fields:
          target: 'nps'
          fields:
            ms-network-access-server-type: ["1","RDP"]
  - if:
      equals:
        nps.ms-network-access-server-type: "2"
    then:
      - add_fields:
          target: 'nps'
          fields:
            ms-network-access-server-type: ["2","Remote Access Server (VPN-Dial up)"]
  - if:
      equals:
        nps.ms-network-access-server-type: "3"
    then:
      - add_fields:
          target: 'nps'
          fields:
            ms-network-access-server-type: ["3","DHCP Server"]
#
  - if:
      equals:
        nps.authentication-type: "1"
    then:
      - add_fields:
          target: 'nps'
          fields:
            authentication-type: ["1","PAP"]
    else:
      - if:
          equals:
            nps.authentication-type: "2"
        then:
          - add_fields:
              target: 'nps'
              fields:
                authentication-type: ["2","CHAP"]
        else:
          - if:
              equals:
                nps.authentication-type: "3"
            then:
              - add_fields:
                  target: 'nps'
                  fields:
                    authentication-type: ["3","MS-CHAP"]
            else:
              - if:
                  equals:
                    nps.authentication-type: "4"
                then:
                  - add_fields:
                      target: 'nps'
                      fields:
                        authentication-type: ["4","MS-CHAPv2"]
                else:
                  - if:
                      equals:
                        nps.authentication-type: "5"
                    then:
                      - add_fields:
                          target: 'nps'
                          fields:
                            authentication-type: ["5","EAP"]
                    else:
                      - if:
                          equals:
                            nps..authentication-type: "7"
                        then:
                          - add_fields:
                              target: 'nps'
                              fields:
                                authentication-type: ["7","Unauthenticated"]
                        else:
                          - if:
                              equals:
                                nps..authentication-type: "8"
                            then:
                              - add_fields:
                                  target: 'nps'
                                  fields:
                                    authentication-type: ["8","Extension"]
# There are other types not included above ^
#
  - if:
      equals:
        nps.provider-type: "1"
    then:
      - add_fields:
          target: 'nps'
          fields:
            provider-type: ["0","None"]
    else:
      - if:
          equals:
            nps.provider-type: "1"
        then:
          - add_fields:
              target: 'nps'
              fields:
                provider-type: ["1","Windows"]
        else:
          - if:
              equals:
                nps.provider-type: "2"
            then:
              - add_fields:
                  target: 'nps'
                  fields:
                    provider-type: ["2","RADIUS Proxy"]
#
  - if:
      equals:
        event.action: "1"
    then:
      - add_fields:
          target: 'event'
          fields:
            action: ["1","Accept-Request"]
    else:
      - if:
          equals:
            event.action: "2"
        then:
          - add_fields:
              target: 'event'
              fields:
                action: ["2","Access-Accept"]
        else:
          - if:
              equals:
                event.action: "3"
            then:
              - add_fields:
                  target: 'event'
                  fields:
                    action: ["3","Access-Reject"]
            else:
              - if:
                  equals:
                    event.action: "4"
                then:
                  - add_fields:
                      target: 'event'
                      fields:
                        action: ["4","Accounting-Request"]
# There are other types not included
# tunnel-types:
#1: Point-to-Point Tunneling Protocol (PPTP)
#2: Layer Two Forwarding (L2F)
#3: Layer Two Tunneling Protocol (L2TP)
#4: Ascend Tunnel Management Protocol (ATMP)
#5: Virtual Tunneling Protocol (VTP)
#6: IP Authentication Header in the Tunnel-mode (AH)
#7: IP-in-IP Encapsulation (IP-IP)
#8: Minimal IP-in-IP Encapsulation (MIN-IP-IP)
#9: IP Encapsulating Security Payload in the Tunnel-mode (ESP)
#10: Generic Route Encapsulation (GRE)
#11: Bay Dial Virtual Services (DVS)
#12: IP-in-IP Tunneling
#13: Virtual LANs (VLAN)
#79617: Secure Socket Tunneling Protocol (SSTP)
  - if:
      equals:
        nps.tunnel-type: "13"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            tunnel-type: ["13","Virtual LANs (VLAN)"]
#
  - if:
      equals:
        nps.nas-manufacturer: "0"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            nas-manufacturer: ["0","RADIUS Standard"]
#
  - if:
      equals:
        nps.tunnel-medium-type: "1"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            tunnel-medium-type: ["1","ipv4"]
  - if:
      equals:
        nps.tunnel-medium-type: "2"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            tunnel-medium-type: ["2","ipv6"]
  - if:
      equals:
        nps.tunnel-medium-type: "6"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            tunnel-medium-type: ["6","802 (includes all 802 media plus Ethernet canonical format)"]
#nas-port-type
  - if:
      equals:
        nps.nas-port-type: "5"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            nas-port-type: ["5","VPN"]
  - if:
      equals:
        nps.nas-port-type: "15"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            nas-port-type: ["15","Ethernet"]
  - if:
      equals:
        nps.nas-port-type: "18"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            nas-port-type: ["18","Wireless - Other"]
  - if:
      equals:
        nps.nas-port-type: "19"
    then: 
      - add_fields:
          target: 'nps'
          fields:
            nas-port-type: ["19","Wireless - IEEE 802.11"]
# Evt reason
  - if:
      equals:
        event.reason: "0"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["0","success"]
  - if:
      equals:
        event.reason: "1"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["1","internal_error"]
  - if:
      equals:
        event.reason: "2"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["2","access_denied"]
  - if:
      equals:
        event.reason: "3"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["3","malformed_request"]
  - if:
      equals:
        event.reason: "4"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["4","global_catalog_unavailable"]
  - if:
      equals:
        event.reason: "5"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["5","domain_unavailable"]
  - if:
      equals:
        event.reason: "6"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["6","server_unavailable"]
  - if:
      equals:
        event.reason: "7"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["7","no_such_domain"]
  - if:
      equals:
        event.reason: "8"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["8","no_such_user"]
  - if:
      equals:
        event.reason: "16"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["16","auth_failure"]
  - if:
      equals:
        event.reason: "21"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["21","An NPS extension dynamic link library (DLL) that is installed on the NPS server rejected the connection request."]
  - if:
      equals:
        event.reason: "34"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["34","account_disabled"]
  - if:
      equals:
        event.reason: "35"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["35","account_expired"]
  - if:
      equals:
        event.reason: "36"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["36","account_locked_out"]
  - if:
      equals:
        event.reason: "48"
    then: 
      - add_fields:
          target: 'event'
          fields:
            reason: ["48","no_policy_match"]

# acct-status-type
  - if:
      has_fields: "acct-status-type"
    then:
      - if:
          equals:
            nps.acct-status-type: "1"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["1","Start"]
      - if:
          equals:
            nps.acct-status-type: "2"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["2","Stop"]
      - if:
          equals:
            nps.acct-status-type: "3"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["3","Interim Update"]
      - if:
          equals:
            nps.acct-status-type: "7"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["7","Accounting-On"]
      - if:
          equals:
            nps.acct-status-type: "8"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["8","Accounting-Off"]
      - if:
          equals:
            nps.acct-status-type: "9"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["9","Tunnel-Start"]
      - if:
          equals:
            nps.acct-status-type: "10"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["10","Tunnel-Stop"]
      - if:
          equals:
            nps.acct-status-type: "11"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["11","Tunnel-Reject"]
      - if:
          equals:
            nps.acct-status-type: "12"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["12","Tunnel-Link-Start"]
      - if:
          equals:
            nps.acct-status-type: "13"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["13","Tunnel-Link-Stop"]
      - if:
          equals:
            nps.acct-status-type: "14"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["14","Tunnel-Link-Reject"]
      - if:
          equals:
            nps.acct-status-type: "15"
        then: 
          - add_fields:
              target: 'nps'
              fields:
                acct-status-type: ["15","Failed"]
#
  - copy_fields:
      fields:
        - from: client.ip
          to: source.ip
        - from: client.ip
          to: related.ip
        - from: nps.ms-machine-name
          to: related.ip
        - from: nps.framed-ip-address
          to: related.ip
        - from: user.name
          to: related.user
        - from: source.ip
          to: related.ip
      fail_on_error: false
      ignore_missing: true
#
# Drop unneeded fields
#
  - drop_fields:
      fields: ["4","8","11","25","26","30","31","32","40","44","50","61","62","64","65","66","77","81","126","4108","4116","4120","4127","4128","4129","4130","4132","4142","4149","4154","4155","8138"]
      ignore_missing: true
# Last
  - drop_fields:
      fields: "rest"
      ignore_missing: true
      when:
        equals:
          rest:  ''

I tried to keep close to ECS but not NPS fields have an equivalent.
I did not populated al fields since not all are used.
Any improvements or suggestions are welcomed
Also I would like to compare(performance related) to a GROK version