# Microsoft NPS Server Logfile Parser

**URL:** <https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523>\
**Category:** the NEW Marketplace\
**Created:** [April 21, 2023, 9:23am UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523 "2023-04-21T09:23:36Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![fsv](https://sea2.discourse-cdn.com/flex016/user_avatar/community.graylog.org/fsv/32/14679_2.png) [@fsv](https://community.graylog.org/u/fsv)\
**Post date:** [April 21, 2023, 9:23am UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/1 "2023-04-21T09:23:36Z")

</div>

Microsoft NPS Server creates logs via EventLog and logfiles.  
Capturing the Event Logs is pretty straight forward with a tool like NXLog, but parsing the Logfile is more complicated, so I want to share how I did it. I’m not using extractors because we use Graylog Forwarders in our environment and you can’t use them together. If you’re using NXLogEE you can use the nps extension and skip this guide, otherwise keep reading.

first step is to setup the NPS logfile format.  
 ![grafik](https://us1.discourse-cdn.com/flex016/uploads/graylog/original/2X/a/a3f4acfa738fef00500bdc59a374476bc25a7733.png)  
There you have to click on Logfile Properties and change the Format to ODBC(Legacy). This way the logfiles will be created in csv format and be compliant to the documentation here: [Interpret NPS Database Format Log Files | Microsoft Learn](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/cc771748(v=ws.10))

Next step is to send the Logfile to your Graylog environment. Create an index, input and stream for your NPS logs the way you do it normally in your environment. I’m using a seperate input, stream and index for the NPS logs but thats not mandatory.  
To send the logfiles to graylog I’m using NXLog.  
Here is the nxlog.conf

```auto
Panic Soft
#####################################################################
##Default Definitions################################################
#####################################################################
define ROOT C:\Program Files\nxlog
define CERTDIR %ROOT%\cert
define CONFDIR %ROOT%\conf
define LOGDIR %ROOT%\data
define LOGFILE %LOGDIR%\nxlog.log
LogFile %LOGFILE%
Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
#####################################################################
##Custom Definitions#################################################
#####################################################################
##Destination Server:
define DESTSRV 10.100.248.171
##Destination Port:
define DESTPORT 3510
define DESTPORTNAP 3515
#####################################################################
##Extensions#########################################################
#####################################################################
##Logrotation
<Extension _fileop>
    Module xm_fileop
    # Check the size of our log file hourly, rotate if larger than 5MB
    <Schedule>
        Every 1 hour
        Exec if (file_exists('%LOGFILE%') and \
                   (file_size('%LOGFILE%') >= 5M)) \
                    file_cycle('%LOGFILE%', 8);
    </Schedule>
    # Rotate our log file every week on Sunday at midnight
    <Schedule>
        When @weekly
        Exec if file_exists('%LOGFILE%') file_cycle('%LOGFILE%', 8);
    </Schedule>
</Extension>
##GELF Extension Module
<Extension _gelf>
    Module xm_gelf
</Extension>
#####################################################################
##Inputs#############################################################
#####################################################################
<Input EventLogs>
    Module im_msvistalog
    <QueryXML>
        <QueryList>
            <Query Id='0'>
                <Select Path='Application'>*</Select>
                <Select Path='Security'>*</Select>
                <Select Path='System'>*</Select>
                <Select Path='Setup'>*</Select>
            </Query>
        </QueryList>
    </QueryXML>
</Input>
<Input z000nap01_logfiles>
	Module im_file
	#File 'C:\\Program Files\\Microsoft Configuration Manager\\Logs\\*.log'
	File 'C:\\Windows\\System32\\LogFiles\\IN*.log'
	SavePos TRUE
	ReadFromLast TRUE
	PollInterval 1
	Exec $Message = $raw_event; $SyslogFacilityValue = 22;
	Exec $logname = file_basename(file_name());
</Input>
#####################################################################
##Outputs############################################################
#####################################################################
<Output graylog>
	Module om_tcp
	Host %DESTSRV%
	Port %DESTPORT%
	OutputType	GELF_TCP
</Output>
<Output graylogNAP>
	Module om_tcp
	Host %DESTSRV%
	Port %DESTPORTNAP%
	OutputType	GELF_TCP
</Output>
#####################################################################
##Routes#############################################################
#####################################################################
<Route graylog_route>
	Path EventLogs => graylog
</Route>
<Route graylog_route>
	Path z000nap01_logfiles => graylogNAP
</Route>

```

DESTPORT variable is the port for the windows event logs. Use the port you’re using for windows events input in your environment.  
DESTPORTNAP variable is the port for the nps logfile. Use the port for your newly created input.

Now your NPS server should send the logfiles to graylog, but they are not split into fields. to change that we need a pipeline rule and a pipeline.

Create a new rule under system/pipeline/manage rules/create rule.  
Here is the parser:

```auto
rule "MS NPS Logfile Parser"
// Parses Logfiles created via Microsoft NPS Server. 
// Logfile format has to be ODBC(Legacy)
when 
    has_field("message")
then
    let m = to_string($message.full_message);
    //add some empty csv entries for shorter logs that are not compliant to the
    //MS documentation and a character that does not get parsed to prevent out 
    //of bound errors
    let m = regex_replace(",$", to_string(m), ",,,x");
    let m = split(",", to_string(m));

    set_field("nps_computername", m[0]);
    set_field("nps_servicename", m[1]);
    set_field("nps_date", m[2]);
    set_field("nps_time", m[3]);
    set_field("nps_packettype", m[4]);
    set_field("nps_username", m[5]);
    set_field("nps_fqdn", m[6]);
    set_field("nps_calledstationID", m[7]);
    set_field("nps_callingstationID", m[8]);
    set_field("nps_callbacknumber", m[9]);
    set_field("nps_framedIP", m[10]);
    set_field("nps_NASidentifier", m[11]);
    set_field("nps_NASIP", m[12]);
    set_field("nps_NASport", m[13]);
    set_field("nps_clientvendor", m[14]);
    set_field("nps_clientIP", m[15]);
    set_field("nps_clientfriendlyname", m[16]);
    set_field("nps_eventtimestamp", m[17]);
    set_field("nps_portlimit", m[18]);
    set_field("nps_NASporttype", m[19]);
    set_field("nps_connectinfo", m[20]);
    set_field("nps_framedprotocol", m[21]);
    set_field("nps_servicetype", m[22]);
    set_field("nps_authenticationtype", m[23]);
    set_field("nps_policyname", m[24]);
    set_field("nps_reasoncode", m[25]);
    set_field("nps_class", m[26]); 
    set_field("nps_sessiontimeout", m[27]);
    set_field("nps_idletimeout", m[28]);
    set_field("nps_terminationaction", m[29]);
    set_field("nps_EAPfriendlyname", m[30]);
    set_field("nps_acctstatustype", m[31]);
    set_field("nps_acctdelaytime", m[32]);
    set_field("nps_acctinputoctets", m[33]);
    set_field("nps_acctoutputoctets", m[34]);
    set_field("nps_acctsessionID", m[35]);
    set_field("nps_acctauthentic", m[36]);
    set_field("nps_acctsessiontime", m[37]);
    set_field("nps_acctinputpackets", m[38]);
    set_field("nps_acctoutputpackets", m[39]);
    set_field("nps_acctterminatecause", m[40]);
    set_field("nps_acctmultissnID", m[41]);
    set_field("nps_acctlinkcount", m[42]); 
    set_field("nps_acctinteriminterval", m[43]);
    set_field("nps_tunneltype", m[44]);
    set_field("nps_tunnelmediumtype", m[45]);
    set_field("nps_tunnelclientendpt", m[46]);
    set_field("nps_tunnelserverendpt", m[47]);
    set_field("nps_accttunnelconn", m[48]); 
    set_field("nps_tunnelpvtgroupID", m[49]);
    set_field("nps_tunnelassignmentID", m[50]);
    set_field("nps_tunnelpreference", m[51]);
    set_field("nps_MSacctauthtype", m[52]);
    set_field("nps_MSacctEAPtype", m[53]);
    set_field("nps_MSRASversion", m[54]);
    set_field("nps_MSRASVendor", m[55]);
    set_field("nps_MSCHAPerror", m[56]);
    set_field("nps_MSCHAPdomain", m[57]);
    set_field("nps_MSMPPEencryptiontypes", m[58]);
    set_field("nps_MSMPPEencryptionpolicy", m[59]);
    set_field("nps_proxypolicyname", m[60]);
    set_field("nps_providertype", m[61]);
    set_field("nps_providername", m[62]);
    set_field("nps_remoteserveraddress", m[63]);
    set_field("nps_MSRASclientname", m[64]);
    set_field("nps_MSRASclientversion", m[65]);
end

```

Save the rule, create a new pipeline under manage pipelines/add new pipeline, connect the stream that contains the NPS logfiles with “edit connection”, edit Stage 0 and add your MS NPS Logfile Parser.

---

<div class="post-metadata">

**Author:** ![gsmith](https://sea2.discourse-cdn.com/flex016/user_avatar/community.graylog.org/gsmith/32/1222_2.png) [@gsmith](https://community.graylog.org/u/gsmith)\
**Post date:** [May 1, 2023, 11:25pm UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/2 "2023-05-01T23:25:03Z")

</div>

@fsv

Pretty awesome man, thank for sharing.

---

<div class="post-metadata">

**Author:** ![cyr0nk0r](https://sea2.discourse-cdn.com/flex016/user_avatar/community.graylog.org/cyr0nk0r/32/14861_2.png) [@cyr0nk0r](https://community.graylog.org/u/cyr0nk0r)\
**Post date:** [May 31, 2023, 4:13am UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/3 "2023-05-31T04:13:17Z")

</div>

Getting the following error for ingestion.

gl2\_processing\_error  
Error evaluating action for rule \<nps radius logfile parser/6476c46cd8511407b37dcf6f\> (pipeline \<nps radius logfiles/6476c4a4d8511407b37dcfe6\>) - In call to function ‘set\_field’ at 15:4 an exception was thrown: index (1) must be less than size (1)

---

<div class="post-metadata">

**Author:** ![subsonicrabbit](https://avatars.discourse-cdn.com/v4/letter/s/13edae/32.png) [@subsonicrabbit](https://community.graylog.org/u/subsonicrabbit)\
**Post date:** [June 13, 2023, 12:51pm UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/4 "2023-06-13T12:51:59Z")

</div>

In case this helps anyone in the future, I ran into the same error message as @cyr0nk0r and ended up changing the line

```auto
    let m = to_string($message.full_message);

```

to

```auto
    let m = to_string($message.message);

```

because when looking at the available fields in graylog there wasn’t a “full\_message” field but there was a “message” one. I’m also using filebeat to import my logs via sidecar instead of nxlog which may be the difference between my environment and @fsv’s.

 ![CleanShot 2023-06-13 at 07.48.55](https://us1.discourse-cdn.com/flex016/uploads/graylog/original/2X/2/21b8ba7aee8934717ad10233b0c641ec37c6f550.jpeg)

---

<div class="post-metadata">

**Author:** ![fsv](https://sea2.discourse-cdn.com/flex016/user_avatar/community.graylog.org/fsv/32/14679_2.png) [@fsv](https://community.graylog.org/u/fsv)\
**Post date:** [June 21, 2023, 11:06am UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/5 "2023-06-21T11:06:02Z")

</div>

Ah nice find.  
we are using Graylog Enterprise with Illuminate. Illuminate normalizes fields between different inputs, so I guess thats why they changed the default message field to full\_message in our environment.

---

<div class="post-metadata">

**Author:** ![Ueba3ba](https://sea2.discourse-cdn.com/flex016/user_avatar/community.graylog.org/ueba3ba/32/16884_2.png) [@Ueba3ba](https://community.graylog.org/u/Ueba3ba)\
**Post date:** [June 27, 2024, 3:27am UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/6 "2024-06-27T03:27:15Z")

</div>

Thanks for the parser.

Do I understand correctly: I have to create two inputs in Graylog?

One for NPS and one for the event logs?

---

<div class="post-metadata">

**Author:** ![kitkat0981](https://sea2.discourse-cdn.com/flex016/user_avatar/community.graylog.org/kitkat0981/32/18006_2.png) [@kitkat0981](https://community.graylog.org/u/kitkat0981)\
**Post date:** [June 4, 2025, 1:20pm UTC](https://community.graylog.org/t/microsoft-nps-server-logfile-parser/28523/7 "2025-06-04T13:20:28Z")

</div>

sorry for bringing this back from the dead, but I’m looking for this NPS specifically although, im running 6.0.14

Should this work with 6.0.14 and windows NPS 2019? I don’t see logs coming in..?!? I setup the events port to be 12201 and NPS events as 12202. Im not getting any logs in from either port, so somthing tells me this NXlog scrip may not be good anymore for today. and yes i did restart nxlog after changing the conf file.

just to be sure, the inputs are still GELF UDP right?
