NLS for auditing

This support forum board is for support questions relating to Nagios Log Server, our solution for managing and monitoring critical log data.
User avatar
WillemDH
Posts: 2320
Joined: Wed Mar 20, 2013 5:49 am
Location: Ghent
Contact:

Re: NLS for auditing

Post by WillemDH »

Well it seems I'm almost there; i got it working on one server. But on another server I seem to experiencing an issue. The ps custom object is created, converted to json and sent to Logstash. I can see the json coming in with a tcpdump on the port I'm sending the data to. But NLS does not seem to be able to do something with the data of this second server. So it's not a network / firewall issue. I'm not sure what's going wrong, as it work perfectly on the other server. Any chance you guys want to test my script? I suspect it's something with the json format.

Code: Select all

# Script name:   	naf_initiate_ms_server_audit.ps1
# Version:			v1.6.16
# Created on:    	15/09/2014																			
# Author:        	D'Haese Willem
# Purpose:       	Initiates audit of Microsoft server
# On Github:		https://github.com/willemdh/naf_initiate_ms_server_audit
# On OutsideIT:		http://outsideit.net/naf-initiate-ms-server-audit
# Recent History:       	
#	10/06/2015 => First step to multiple outputs
#	11/06/2015 => Fixed admin, disk and service html
#	13/06/2015 => finalized ahres, services, admins
#	15/06/2015 => Finalized software and network adapters
#	16/06/2015 => Updated fieldnames and cleanup auditstruct
# Copyright:
#	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published
#	by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed 
#	in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
#	PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public 
#	License along with this program.  If not, see <http://www.gnu.org/licenses/>.

#Requires –Version 2.0

$AuditStruct = New-Object PSObject -Property @{
    audit_start = (Get-Date -Format 'yyyy/MM/dd HH:mm:ss'); 
	audit_end = '';
    audit_duration = '';
    audit_executer = ("$ENV:USERDOMAIN\$ENV:USERNAME")
	audit_exitcode = 3;
    hostname = ([System.Net.Dns]::GetHostByName((hostname.exe)).HostName).tolower();	
    win_test_ping = 'Unknown';
    win_test_wmi = 'Unknown';
    win_test_eventlog = 'Unknown';
    host_version ='';
    host_caption = '';
    host_service_pack = '';
    host_lastboot = '';
	host_domain_role = '';
	host_system_type = '';
	host_timezone = '';
	host_current_domain_controller = '';
	host_registry_size = '';
	host_registry_size_max = '';
    host_manufacturer = '';
    host_model = '';
    host_physical_memory = '';
    host_assettag = '';
    host_serial_number = '';
    host_processor_name = '';
    host_processor_speed = '';
    host_processor_voltage = '';
	host_dns_cache = '';
    hostentries = @()
}

$InitStruct = New-Object PSObject -Property @{
    Output = 'Html';
    Logstash ='';
    Port = '';
	outputfolder = "\\$($AuditStruct.Hostname)\C$\Nagios\NAF\NAF_Logs\Reports";
	outputdate = (Get-Date -Format 'yyyyMMdd.HHmmss');
	outputfile = '';
	AdminGroupMembers = @();
	ObjGroupName = '';
    Services = '';
    ServiceHtmlSvcs = '';
    ServiceHtmlDisks = '';
    ServiceHtmlSharePerm = '';
    ShareObjs = @();
    ShareColl = '';
    ServiceHtmlShareNtfs = '';
    ShareNtfsObjs = @();
    ShareNtfsColl = '';
    NetwAdapHtml = '';
    RegHklm = '2147483650';
    SoftwareHtml = '';
    softwareobjs = @();
    dnscacheobjs = @();
	installedsoftreg = @();
    DnsCacheHtml = '';
	HostsHtml = ''
}

$WmiStruct = New-Object PSObject -Property @{
    Win32_OperatingSystem = '';
    Win32_Timezone = '';
    Win32_ComputerSystem = '';
    Win32_Registry = '';
    Win32_SystemEnclosure = '';
    Win32_Processor = '';
    Win32_NetworkadapterConfiguration = '';
    Win32_LogicalDisk = ''
}

$ErrorActionPreference = 'SilentlyContinue'
$DebugPreference = 'SilentlyContinue'
$VerbosePreference = 'SilentlyContinue'

Write-Log Verbose "Audit started on $($AuditStruct.hostname)."

#region Functions

function Test-FileLock {
      param ([parameter(Mandatory=$true)][string]$Path)

  $oFile = New-Object System.IO.FileInfo $Path

  if ((Test-Path -Path $Path) -eq $false)
  {
    return $false
  }
  try
  {
      $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
      if ($oStream)
      {
        $oStream.Close()
      }
      $false
  }
  catch
  {
    # file is locked by a process.
    return $true
  }
}
function Write-Log {
    param (
	[parameter(Mandatory=$true)][string]$Log,
	[parameter(Mandatory=$true)][string]$Message
	)
	$Now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss,fff'
    If ($Log -eq 'Verbose') {
    	Write-Verbose "${Now}: $Message"
    }
    else {
        $Date = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
	    while (Test-FileLock $Log) {Start-Sleep (Get-Random -minimum 1 -maximum 10)}
	    "${Now}: $Message" | Out-File -filepath $Log -Append
    }
}
function Get-WmiTrees {
    Write-Log Verbose 'WMI Querying started on $($AuditStruct.Hostname)...'
    Write-Log Verbose 'Querying Win32_OperatingSystem...'
    $WmiStruct.Win32_OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $AuditStruct.Hostname
    Write-Log Verbose 'Querying Win32_Timezone...'
    $WmiStruct.Win32_Timezone = Get-WmiObject Win32_Timezone -computername $AuditStruct.Hostname 
    Write-Log Verbose 'Querying Win32_ComputerSystem...'
    $WmiStruct.Win32_ComputerSystem = Get-WmiObject Win32_ComputerSystem -computername $AuditStruct.Hostname
    Write-Log Verbose 'Querying Win32_Registry...'
    $WmiStruct.Win32_Registry = Get-WmiObject Win32_Registry -ComputerName $AuditStruct.Hostname
    Write-Log Verbose 'Querying Win32_SystemEnclosure...'
    $WmiStruct.Win32_SystemEnclosure = Get-WmiObject Win32_SystemEnclosure -ComputerName $AuditStruct.Hostname
    Write-Log Verbose 'Querying Win32_Processor...'
    $WmiStruct.Win32_Processor = Get-WmiObject Win32_Processor -ComputerName $AuditStruct.Hostname
    Write-Log Verbose 'Querying win32_NetworkadapterConfiguration...'
    $WmiStruct.Win32_NetworkadapterConfiguration = Get-WmiObject win32_NetworkadapterConfiguration -computername $AuditStruct.Hostname -Filter 'ipenabled = "true"'
    Write-Log Verbose 'Querying Win32_Logicaldisk...'
	$WmiStruct.Win32_LogicalDisk = Get-WmiObject Win32_Logicaldisk -ComputerName $AuditStruct.Hostname -Fi 'DriveType=3' | 
		Select-Object @{Name='Computername'; Expression={$_.SystemName}}, DeviceId,
		@{Name='SizeGB';Expression={'{0:N2}' -f ($_.Size/1GB)}},
		@{Name='FreeGB';Expression={'{0:N2}' -f ($_.Freespace/1GB)}},
		@{Name='UsedGB';Expression={'{0:N2}' -f (($_.Size-$_.FreeSpace)/1GB)}},
		@{Name='PerFree';Expression={'{0:P2}' -f ($_.FreeSpace/$_.Size)}}
    Write-Log Verbose 'WMI Querying end...'
}		
function Set-ProcessPriority { 
	param($ProcessName = $(throw 'Enter process name'), $Priority = 'Normal')
    Write-Log Verbose "Setting process priority of $ProcessName to $Priority..."
	Get-Process -processname $ProcessName | foreach { $_.PriorityClass = $Priority }
}
function Test-Paths { 
    Write-Log Verbose "Testing path for writing `"`b`b$($InitStruct.OutputFolder)\$($AuditStruct.Hostname)`.$($InitStruct.OutputDate).html`"..."
	$InitStruct.OutputFile = "\\$($InitStruct.OutputFolder)\$($AuditStruct.Hostname)`.$($InitStruct.OutputDate).html"
    if (!(Test-Path -path $InitStruct.OutputFolder)) {
        try {
			New-Item -Path $InitStruct.OutputFolder -Type directory -Force     
        }
        catch {
        	Write-Log Verbose "Error creating directory `"`b`b$($InitStruct.OutputFolder)`" on $($AuditStruct.Hostname)"  
        }
        if (!(Test-Path -path $InitStruct.OutputFolder)) {
            Write-Log Verbose "Error creating directory `"`b`b$($InitStruct.OutputFolder)`" on $($AuditStruct.Hostname)"        
        }
        else {
        	Write-Log Verbose "Directory `"`b`b$($InitStruct.OutputFolder)`" created on $($AuditStruct.Hostname)"   
        }
	}
}
Function Send-JsonOverTcp { 
 param ( [ValidateNotNullOrEmpty()] 
 [string] $NagiosLogServer, 
 [int] $Port, 
 $JsonObject) 
 $JsonString = $JsonObject -replace "`n",' ' -replace "`r",' '
 $Ip = [System.Net.Dns]::GetHostAddresses($NagiosLogServer) 
 $Address = [System.Net.IPAddress]::Parse($Ip) 
 $Socket = New-Object System.Net.Sockets.TCPClient($Address,$Port) 
 $Stream = $Socket.GetStream() 
 $Writer = New-Object System.IO.StreamWriter($Stream)
 $Writer.WriteLine($JsonString)
 $Writer.Flush()
 $Stream.Close()
 $Socket.Close()
}
function Get-HostEntries {
    [CmdletBinding()] 
    param (
		[Parameter(Mandatory=$false)][String]$Filter
	)
    function New-ObjectHostEntry{
        param(
            [string]$IP,
            [string]$DNS
        )
        New-Object PSObject -Property @{
            IP = $IP
            DNS = $DNS
        }
    }
    Write-Log Verbose 'Querying hosts file...'
    $Entries = $(get-content "$env:windir\System32\drivers\etc\hosts") | % {
        if (!$_.StartsWith('#') -and $_ -ne '') {
            $IP = ([regex]'(([2]([0-4][0-9]|[5][0-5])|[0-1]?[0-9]?[0-9])[.]){3}(([2]([0-4][0-9]|[5][0-5])|[0-1]?[0-9]?[0-9]))').match($_).value
            $DNS = ($_ -replace $IP, '') -replace  '\s+',''            
            if ($Filter -and (($IP -match $Filter) -or ($DNS -match $Filter))) {
                New-ObjectHostEntry -IP $IP -DNS $DNS
            }
            elseif ($Filter -eq '') {
                New-ObjectHostEntry -IP $IP -DNS $DNS
            }
        }  
    } 
    if ($Entries -ne $Null) {                
        $Entries      
    }
    else {
         Write-Log Verbose 'No entries found in host file...'
    }
    Write-Log Verbose 'Hosts file scanned.'
}

function Start-Audit {	
# Connectivity tests
	if ($AuditStruct.win_test_ping -eq '') {
    	$PingResult = Test-Connection -ComputerName $AuditStruct.Hostname -Count 1 -Quiet
	    if ($PingResult) {		
			$AuditStruct.win_test_ping = 'Succeeded'			
		} 
		else {
    		$AuditStruct.win_test_ping = 'Failed'
			Write-Host "CRITICAL: Ping to $Value failed! Please provide valid reachable hostname."
			exit 1
		}
	}
	Write-Log Verbose "Testing WMI on $($AuditStruct.Hostname)..."
 	$WmiTest = Get-WmiObject -Query "Select * from Win32_PingStatus where Address = '$($AuditStruct.Hostname)'"
	if($WmiTest) {
    	$AuditStruct.win_test_wmi = 'Succeeded'
	} 
	else {
    	$AuditStruct.win_test_wmi = 'Failed'
	}
	Write-Log Verbose "Testing Eventlog access on $($AuditStruct.Hostname)..."		
	$EventlogTest = Get-EventLog System -ComputerName $AuditStruct.Hostname -Newest 1
	if($EventlogTest) {
    	$AuditStruct.win_test_eventlog = 'Succeeded'
	} 
	else {
    	$AuditStruct.win_test_eventlog = 'Failed'
	}
# System information
	Write-Log Verbose 'Querying system information...'
    $AuditStruct.host_version = $WmiStruct.Win32_OperatingSystem.Version
    $AuditStruct.host_caption = $WmiStruct.Win32_OperatingSystem.Caption
    $AuditStruct.host_service_pack = $WmiStruct.Win32_OperatingSystem.ServicePackMajorVersion
	$AuditStruct.host_lastboot = ($WmiStruct.Win32_OperatingSystem.ConvertToDateTime($WmiStruct.Win32_OperatingSystem.LastBootUpTime)).ToString('dd/MM/yyyy HH:mm:ss')
	switch ($WmiStruct.Win32_ComputerSystem.DomainRole) {
		0 { $AuditStruct.host_domain_role = 'Standalone Workstation' }
		1 { $AuditStruct.host_domain_role = 'Member Workstation' }
		2 { $AuditStruct.host_domain_role = 'Standalone Server' }
		3 { $AuditStruct.host_domain_role = 'Member Server' }
		4 { $AuditStruct.host_domain_role = 'Domain Controller' }
		5 { $AuditStruct.host_domain_role = 'Domain Controller' }
		default { $AuditStruct.host_domain_role = 'Information not available' }
	}
    switch ($WmiStruct.Win32_ComputerSystem.PCSystemType) {
    	1 { $AuditStruct.host_system_type = 'Desktop' }
    	2 { $AuditStruct.host_system_type = 'Mobile / Laptop' }
    	3 { $AuditStruct.host_system_type = 'Workstation' }
 	    4 { $AuditStruct.host_system_type = 'Enterprise Server' }
   	 	5 { $AuditStruct.host_system_type = 'Small Office and Home Office (SOHO) Server' }
   	 	6 { $AuditStruct.host_system_type = 'Appliance PC' }
    	7 { $AuditStruct.host_system_type = 'Performance Server' }
    	8 { $AuditStruct.host_system_type = 'Maximum' }
    	default { $AuditStruct.host_system_type = 'Not a known Product Type' }
    } 
	$AuditStruct.host_timezone = $WmiStruct.Win32_Timezone.Description
	$AuditStruct.host_current_domain_controller = $env:LOGONSERVER -replace '\\', ''	
	$AuditStruct.host_registry_size = $WmiStruct.Win32_Registry.CurrentSize
	$AuditStruct.host_registry_size_max = $WmiStruct.Win32_Registry.MaximumSize
    if (!$AuditStruct.host_registry_size_max) {$AuditStruct.host_registry_size_max = 'Undefined'}
	$AuditStruct.host_manufacturer = $WmiStruct.Win32_ComputerSystem.Manufacturer
	$AuditStruct.host_model = $WmiStruct.Win32_ComputerSystem.Model
	$AuditStruct.host_physical_memory = $WmiStruct.Win32_ComputerSystem.TotalPhysicalMemory
	$AuditStruct.host_assettag = $WmiStruct.Win32_SystemEnclosure.SMBIOSAssetTag
	$AuditStruct.host_serial_number = $WmiStruct.Win32_SystemEnclosure.SerialNumber
    $AuditStruct.host_processor_name = $WmiStruct.Win32_Processor.Name
    $AuditStruct.host_processor_speed = $WmiStruct.Win32_Processor.CurrentClockSpeed
    $AuditStruct.host_processor_voltage = $WmiStruct.Win32_Processor.CurrentVoltage
    foreach ($HostLogicalDisk in $WmiStruct.Win32_LogicalDisk) {
        $DrvDeviceId = ($HostLogicalDisk.DeviceId -replace ':','').tolower()
    	$AuditStruct | Add-Member -type NoteProperty -name win_drv_${DrvDeviceId}_name -Value $DrvDeviceId
        $AuditStruct | Add-Member -type NoteProperty -name win_drv_${DrvDeviceId}_totalgb -Value $HostLogicalDisk.SizeGB
        $AuditStruct | Add-Member -type NoteProperty -name win_drv_${DrvDeviceId}_freegb -Value $HostLogicalDisk.FreeGB
        $AuditStruct | Add-Member -type NoteProperty -name win_drv_${DrvDeviceId}_usedgb -Value $HostLogicalDisk.UsedGB
        $AuditStruct | Add-Member -type NoteProperty -name win_drv_${DrvDeviceId}_freeperc -Value $HostLogicalDisk.PerFree
    	$InitStruct.ServiceHtmlDisks += "<tr><td>$($HostLogicalDisk.DeviceId)</td><td>$($HostLogicalDisk.SizeGB) GB</td>	<td>$($HostLogicalDisk.FreeGB) GB</td><td>$($HostLogicalDisk.UsedGB) GB</td>	<td>$($HostLogicalDisk.PerFree)</td>	</tr>"
	}
# Administrator information
	Write-Log Verbose 'Querying administrators group members...'   
	$ObjSID = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
	$Objgroup = $objSID.Translate( [System.Security.Principal.NTAccount])
	$InitStruct.ObjGroupName = ($objgroup.Value).Split('\')[1]
	$AdsiGroup =[ADSI]"WinNT://$($AuditStruct.Hostname)/$($InitStruct.ObjGroupName)" 
	$InitStruct.AdminGroupMembers = @($AdsiGroup.psbase.Invoke('Members'))
# Service information
	Write-Log Verbose 'Querying system services...'
	$InitStruct.Services = Get-Service -ComputerName $AuditStruct.Hostname
	foreach ($service in $InitStruct.Services) {
		$StartupType = Get-WmiObject -Query "Select StartMode From Win32_Service Where Name='$($service.Name)'"      
        $servicelower = ($service.Name -replace '\s','').ToLower()
		$servicestate = "$($Service.Status)"
        $AuditStruct | Add-Member -type NoteProperty -name win_svc_${servicelower}_name -Value $service.DisplayName
        $AuditStruct | Add-Member -type NoteProperty -name win_svc_${servicelower}_status -Value $servicestate
        $AuditStruct | Add-Member -type NoteProperty -name win_svc_${servicelower}_startuptype -Value $StartupType.Startmode
        $InitStruct.ServiceHtmlSvcs += "<tr><td>$($service.Name)</td>
		<td>$($service.DisplayName)</td>
		<td>$servicestate</td>
		<td>$($StartupType.StartMode)</td>
		</tr>"   
	}
# Share information
	Write-Log Verbose 'Querying system share permissions...'   
	$InitStruct.ShareObjs = Get-SharedFolderPermission $AuditStruct.Hostname
	$LastShare = ''
	foreach ($ShareObj in $InitStruct.ShareObjs){
		$CurrentShare = ($ShareObj.SharedFolderName -replace '\s','').ToLower()
		if (($LastShare -eq '') -or ($CurrentSHare -eq $LastShare)) {
			$InitStruct.ShareColl += "{$($ShareObj.SharedFolderName) {$($ShareObj.SecurityPrincipal)},{$($ShareObj.FileSystemRights)},{$($ShareObj.AccessControlType)}}"
		}
		else {
			$AuditStruct | Add-Member -type NoteProperty -name win_share_$LastShare -Value $InitStruct.ShareColl
			$InitStruct.ShareColl = ''
			$InitStruct.ShareColl += "{$($ShareObj.SharedFolderName) {$($ShareObj.SecurityPrincipal)},{$($ShareObj.FileSystemRights)},{$($ShareObj.AccessControlType)}}"
		}
		$InitStruct.ServiceHtmlSharePerm +=	"<tr>
		<td>$($ShareObj.SharedFolderName)</td>
		<td>$($ShareObj.SecurityPrincipal)</td>
		<td>$($ShareObj.FileSystemRights)</td>
		<td>$($ShareObj.AccessControlType)</td>
		</tr>"
		$LastShare = $CurrentShare
	}
	$AuditStruct | Add-Member -type NoteProperty -name win_share_$LastShare -Value $InitStruct.ShareColl
# Share NTFS information
	Write-Log Verbose 'Querying share NTFS permissions...'   
	$InitStruct.ShareNtfsObjs = Get-SharedFolderNTFSPermission $AuditStruct.Hostname
	$LastShareNtfs = ''
	foreach ($ShareNtfsObj in $InitStruct.ShareNtfsObjs){
		$CurrentShareNtfs = ($ShareNtfsObj.SharedFolderName -replace '\s','_').ToLower()
		if (($LastShareNtfs -eq '') -or ($CurrentShareNtfs -eq $LastShareNtfs)) {
			$InitStruct.ShareNtfsColl += "{$($ShareNtfsObj.SharedFolderName) {$($ShareNtfsObj.SecurityPrincipal)},{$($ShareNtfsObj.FileSystemRights)},{$($ShareNtfsObj.AccessControlType)},{$($ShareNtfsObj.AccessControlFlags)}}"
		}
		else {
			$AuditStruct | Add-Member -type NoteProperty -name win_share_ntfs_$LastShareNtfs -Value $InitStruct.ShareNtfsColl
			$InitStruct.ShareNtfsColl = ''
			$InitStruct.ShareNtfsColl += "{$($ShareNtfsObj.SharedFolderName) {$($ShareNtfsObj.SecurityPrincipal)},{$($ShareNtfsObj.FileSystemRights)},{$($ShareNtfsObj.AccessControlType)},{$($ShareNtfsObj.AccessControlFlags)}}"
		}
		$InitStruct.ServiceHtmlShareNtfs +=	"<tr>
		<td>$($ShareNtfsObj.SharedFolderName)</td>
		<td>$($ShareNtfsObj.SecurityPrincipal)</td>
		<td>$($ShareNtfsObj.FileSystemRights)</td>
		<td>$($ShareNtfsObj.AccessControlType)</td>
		<td>$($ShareNtfsObj.AccessControlFlags)</td>
		</tr>"
		$LastShareNtfs = $CurrentShareNtfs
	}
	$AuditStruct | Add-Member -type NoteProperty -name win_share_ntfs_$LastShareNtfs -Value $InitStruct.ShareNtfsColl
# Network adapter information	
	Write-Log Verbose 'Querying system network adapters...'   
	foreach ($HostAdapter in $WmiStruct.Win32_NetworkadapterConfiguration) {
		$CurrentAdapter = ($HostAdapter.Description -replace '\s','_').ToLower()
		$AuditStruct | Add-Member -type NoteProperty -name win_nic_${CurrentAdapter}_name -Value $($HostAdapter.Description)
		$AuditStruct | Add-Member -type NoteProperty -name win_nic_${CurrentAdapter}_ips -Value $($HostAdapter.IPAddress)
		$AuditStruct | Add-Member -type NoteProperty -name win_nic_${CurrentAdapter}_dhcp -Value $($HostAdapter.DHCPEnabled)
		$AuditStruct | Add-Member -type NoteProperty -name win_nic_${CurrentAdapter}_subnet -Value $($HostAdapter.Subnet)
		$AuditStruct | Add-Member -type NoteProperty -name win_nic_${CurrentAdapter}_gateway -Value $($HostAdapter.DefaultIPGateway)
        $InitStruct.NetwAdapHtml +=	"<tr>
		<td>$($HostAdapter.Description)</td>
		<td>Adapter IP Address</td>
		<td>$($HostAdapter.IPAddress)</td></tr></tbody>
	    <tbody><tr>
		<td>$($HostAdapter.Description)</td>
		<td>Adapter DHCP</td>
		<td>$($HostAdapter.DHCPEnabled)</td>
		</tr></tbody>
	    <tbody><tr>
		<td>$($HostAdapter.Description)</td>
		<td>Adapter Subnet Mask</td>
		<td>$($HostAdapter.IPSubnet)</td>
		</tr></tbody>
		<tbody><tr>
		<td>$($HostAdapter.Description)</td>
		<td>Adapter Gateway</td>
		<td>$($HostAdapter.DefaultIPGateway)</td>
		</tr></tbody>
	    <tbody><tr>
		<td>$($HostAdapter.Description)</td>
		<td>Adapter MAC Address</td>
		<td>$($HostAdapter.MACAddress)</td>
		</tr></tbody>
	    <tbody><tr>
		<td>$($HostAdapter.Description)</td>
		<td>Adapter DNS Servers</td>
		<td>$($HostAdapter.DNSServerSearchOrder)</td>
		</tr>"  
	}
# Software information	
	$Wmi = [wmiclass]"\\$($AuditStruct.Hostname)\root\default:stdRegProv"
	$RegClass = Get-WmiObject -Namespace 'Root\Default' -List -ComputerName $AuditStruct.Hostname | Where-Object { $_.Name -eq 'StdRegProv' }
	$IEKey = 'SOFTWARE\Microsoft\Internet Explorer'
	$IEVersion = ($regclass.GetStringValue($InitStruct.RegHklm,$IEKey,'Version')).sValue
	$InitStruct.SoftwareObjs += New-Object -TypeName PSCustomObject -Property @{
					            'Name' = 'Internet Explorer'
								'Method'= 'Registry Microsoft\Internet Explorer Key'
				                'Version' = $IEVersion
				                'Vendor' = 'Microsoft'
				                'InstallDate' = 'unknown'}
	$McAfeeKey='SOFTWARE\McAfee\AVEngine'
	$McAfeeDATVersion = ($RegClass.GetDWORDValue($InitStruct.RegHklm,$McAfeeKey,'AVDATVersion')).uValue
	$McAfeeEngineVerMajor = ($RegClass.GetDWORDValue($InitStruct.RegHklm,$McAfeeKey,'EngineVersionMajor')).uValue
	$McAfeeEngineVerMinor = ($RegClass.GetDWORDValue($InitStruct.RegHklm,$McAfeeKey,'EngineVersionMinor')).uValue
	$InitStruct.SoftwareObjs += New-Object -TypeName PSCustomObject -Property @{
					            'Name' = 'McAfee Antivirus DAT'
								'Method'= 'Registry McAfee\AVEngine Key'
				                'Version' = $McAfeeDATVersion
				                'Vendor' = 'McAfee'
				                'InstallDate' = 'unknown'
								}
	$InitStruct.SoftwareObjs += New-Object -TypeName PSCustomObject -Property @{
					            'Name' = 'McAfee Antivirus Engine'
								'Method'= 'Registry McAfee\AVEngine Key'
				                'Version' = $McAfeeEngineVerMajor
				                'Vendor' = 'McAfee'
				                'InstallDate' = 'unknown'
								}
	$InitStruct.InstalledSoftReg = get-installedsoftware $AuditStruct.Hostname
	foreach ($InstalledSoftware in $InitStruct.InstalledSoftReg) {
		$InitStruct.SoftwareObjs += New-Object -TypeName PSCustomObject -Property @{
					            'Name' = $InstalledSoftware.Name
								'Method'= 'Registry'
				                'Version' = $InstalledSoftware.Version
				                'Vendor' = $InstalledSoftware.Publisher
				                'InstallDate' = $InstalledSoftware.InstallDate
								'Size' = $InstalledSoftware.EstimatedSize
								}
	}
	$InitStruct.SoftwareObjs = $InitStruct.SoftwareObjs | Sort-Object Name, Method
	foreach ($SoftwareObj in $InitStruct.SoftwareObjs) {
		$CurrentSoftware = ($SoftwareObj.Name -replace '\s','_').ToLower()
		$AuditStruct | Add-Member -type NoteProperty -name win_prog_${CurrentSoftware}_name -Value "{{$($SoftwareObj.Name)}{$($SoftwareObj.Version)}{$($SoftwareObj.Size)}}"
		$InitStruct.SoftwareHtml += "<tr>
		<td>$($SoftwareObj.Name)</td>
		<td>$($SoftwareObj.Method)</td>
		<td>$($SoftwareObj.Version)</td>
		<td>$($SoftwareObj.Vendor)</td>
		<td>$($SoftwareObj.InstallDate)</td>
		<td>$($SoftwareObj.Size) MB</td>
		</tr>"
	}
# DNS Cache
	$InitStruct.DnsCacheObjs = Get-DNSClientCache
	foreach ($DnsCacheObj in $InitStruct.DnsCacheObjs) {
		$AuditStruct.host_dns_cache += "((Name:$($DnsCacheObj.Name))(Section:$($DnsCacheObj.Section))(TTL:$($DnsCacheObj.TTL))(Type:$($DnsCacheObj.Type))(Length:$($DnsCacheObj.Length))(Hostrecord:$($DnsCacheObj.HostRecord))) "
		$InitStruct.DnsCacheHtml += "<tr>
		<td>$($DnsCacheObj.Name)</td>
		<td>$($DnsCacheObj.Section)</td>
		<td>$($DnsCacheObj.TTL)</td>
		<td>$($DnsCacheObj.Type)</td>
		<td>$($DnsCacheObj.Length)</td>
		<td>$($DnsCacheObj.HostRecord)</td>
		</tr></tbody>"
	}

# Hosts File Entries
	$AuditStruct.HostEntries = Get-HostEntries
	foreach ($HostEntry in $AuditStruct.HostEntries) {
		$InitStruct.HostsHtml += "<tr>
		<td>$($HostEntry.IP)</td>
		<td>$($HostEntry.DNS)</td>
		</tr>"
	}
    $AuditStruct.audit_end = (Get-Date -Format 'yyyy/MM/dd HH:mm:ss')
    $AuditDuration = New-TimeSpan –Start $AuditStruct.audit_start –End $AuditStruct.audit_end
    $AuditStruct.audit_duration = '{0:HH:mm:ss}' -f ([datetime]$AuditDuration.Ticks)   
}

Function Get-SharedFolderPermission {
    Param([String]$Computername)  
	$PingResult = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet
	if($PingResult)
	{
		if($Credential)
		{
			$SharedFolderSecs = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -ComputerName $ComputerName -Credential $Credential -ErrorAction SilentlyContinue
		}
		else
		{
			$SharedFolderSecs = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -ComputerName $ComputerName -ErrorAction SilentlyContinue
		}
		
		foreach ($SharedFolderSec in $SharedFolderSecs) 
		{ 
		    $Objs = @() 				
	        $SecDescriptor = $SharedFolderSec.GetSecurityDescriptor()
	        foreach($DACL in $SecDescriptor.Descriptor.DACL)
			{  
				$DACLDomain = $DACL.Trustee.Domain
				$DACLName = $DACL.Trustee.Name
				if($DACLDomain -ne $null)
				{
	           		$UserName = "$DACLDomain\$DACLName"
				}
				else
				{
					$UserName = "$DACLName"
				}
				$Properties = @{'ComputerName' = $ComputerName
								'ConnectionStatus' = 'Success'
								'SharedFolderName' = $SharedFolderSec.Name
								'SecurityPrincipal' = $UserName
								'FileSystemRights' = [Security.AccessControl.FileSystemRights]$($DACL.AccessMask -as [Security.AccessControl.FileSystemRights])
								'AccessControlType' = [Security.AccessControl.AceType]$DACL.AceType}
				$SharedACLs = New-Object -TypeName PSObject -Property $Properties
				$Objs += $SharedACLs
	        }
			$Objs|Select-Object ComputerName,ConnectionStatus,SharedFolderName,SecurityPrincipal,FileSystemRights,AccessControlType
	    }  
	}
	else
	{
		$Properties = @{'ComputerName' = $ComputerName
						'ConnectionStatus' = 'Fail'
						'SharedFolderName' = 'Not Available'
						'SecurityPrincipal' = 'Not Available'
						'FileSystemRights' = 'Not Available'
						'AccessControlType' = 'Not Available'}
		$SharedACLs = New-Object -TypeName PSObject -Property $Properties
		$Objs += $SharedACLs
		$Objs|Select-Object ComputerName,ConnectionStatus,SharedFolderName,SecurityPrincipal,FileSystemRights,AccessControlType
	}
}

Function Get-SharedFolderNTFSPermission {
    Param([String]$Computername)  
	$PingResult = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet
	if($PingResult)
	{
		#check the credential whether trigger
		if($Credential)
		{
			$SharedFolders = Get-WmiObject -Class Win32_Share `
			-ComputerName $ComputerName -Credential $Credential -ErrorAction SilentlyContinue
		}
		else
		{
			$SharedFolders = Get-WmiObject -Class Win32_Share `
			-ComputerName $ComputerName -ErrorAction SilentlyContinue
		}

		foreach($SharedFolder in $SharedFolders)
		{
			$Objs = @()
			
			$SharedFolderPath = [regex]::Escape($SharedFolder.Path)
			if($Credential)
			{	
				$SharedNTFSSecs = Get-WmiObject -Class Win32_LogicalFileSecuritySetting `
				-Filter "Path='$SharedFolderPath'" -ComputerName $ComputerName  -Credential $Credential
			}
			else
			{
				$SharedNTFSSecs = Get-WmiObject -Class Win32_LogicalFileSecuritySetting `
				-Filter "Path='$SharedFolderPath'" -ComputerName $ComputerName
			}
			
			$SecDescriptor = $SharedNTFSSecs.GetSecurityDescriptor()
			foreach($DACL in $SecDescriptor.Descriptor.DACL)
			{  
				$DACLDomain = $DACL.Trustee.Domain
				$DACLName = $DACL.Trustee.Name
				if($DACLDomain -ne $null)
				{
	           		$UserName = "$DACLDomain\$DACLName"
				}
				else
				{
					$UserName = "$DACLName"
				}
				
				#customize the property
				$Properties = @{'ComputerName' = $ComputerName
								'ConnectionStatus' = 'Success'
								'SharedFolderName' = $SharedFolder.Name
								'SecurityPrincipal' = $UserName
								'FileSystemRights' = [Security.AccessControl.FileSystemRights]$($DACL.AccessMask -as [Security.AccessControl.FileSystemRights])
								'AccessControlType' = [Security.AccessControl.AceType]$DACL.AceType
								'AccessControlFlags' = [Security.AccessControl.AceFlags]$DACL.AceFlags}
								
				$SharedNTFSACL = New-Object -TypeName PSObject -Property $Properties
	            $Objs += $SharedNTFSACL
	        }
			$Objs |Select-Object ComputerName,ConnectionStatus,SharedFolderName,SecurityPrincipal,FileSystemRights,AccessControlType,AccessControlFlags -Unique
		}
	}
	else
	{
		$Properties = @{'ComputerName' = $ComputerName
						'ConnectionStatus' = 'Fail'
						'SharedFolderName' = 'Not Available'
						'SecurityPrincipal' = 'Not Available'
						'FileSystemRights' = 'Not Available'
						'AccessControlType' = 'Not Available'
						'AccessControlFlags' = 'Not Available'}
					
		$SharedNTFSACL = New-Object -TypeName PSObject -Property $Properties
	    $Objs += $SharedNTFSACL
		$Objs |Select-Object ComputerName,ConnectionStatus,SharedFolderName,SecurityPrincipal,FileSystemRights,AccessControlType,AccessControlFlags -Unique
	}
} 

Function Get-InstalledSoftware{ 
    Param([String[]]$Computers)  
    If (!$Computers) {$Computers = $ENV:ComputerName} 
    $Base = New-Object PSObject; 
    $Base | Add-Member Noteproperty ComputerName -Value $Null; 
    $Base | Add-Member Noteproperty Name -Value $Null; 
    $Base | Add-Member Noteproperty Publisher -Value $Null; 
    $Base | Add-Member Noteproperty InstallDate -Value $Null; 
    $Base | Add-Member Noteproperty EstimatedSize -Value $Null; 
    $Base | Add-Member Noteproperty Version -Value $Null; 
    $Base | Add-Member Noteproperty Wow6432Node -Value $Null; 
    $Results =  New-Object System.Collections.Generic.List[System.Object]; 
 
    ForEach ($ComputerName in $Computers){ 
        $Registry = $Null; 
        Try{$Registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$ComputerName);} 
        Catch{Write-Host -ForegroundColor Red "$($_.Exception.Message)";} 	         
        If ($Registry){ 
            $UninstallKeys = $Null; 
            $SubKey = $Null; 
            $UninstallKeys = $Registry.OpenSubKey('Software\Microsoft\Windows\CurrentVersion\Uninstall',$False); 
            $UninstallKeys.GetSubKeyNames()|%{ 
                $SubKey = $UninstallKeys.OpenSubKey($_,$False); 
                $DisplayName = $SubKey.GetValue('DisplayName'); 
                If ($DisplayName.Length -gt 0){ 
                    $Entry = $Base | Select-Object * 
                    $Entry.ComputerName = $ComputerName; 
                    $Entry.Name = $DisplayName.Trim();  
                    $Entry.Publisher = $SubKey.GetValue('Publisher');  
                    [ref]$ParsedInstallDate = Get-Date                     
                    $Entry.InstallDate = $SubKey.GetValue('InstallDate') 	                    
                    $Entry.EstimatedSize = [Math]::Round($SubKey.GetValue('EstimatedSize')/1KB,1); 
                    $Entry.Version = $SubKey.GetValue('DisplayVersion'); 
                    [Void]$Results.Add($Entry); 
                } 
            } 
             
                If ([IntPtr]::Size -eq 8){ 
                $UninstallKeysWow6432Node = $Null; 
                $SubKeyWow6432Node = $Null; 
                $UninstallKeysWow6432Node = $Registry.OpenSubKey('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall',$False); 
                    If ($UninstallKeysWow6432Node) { 
                        $UninstallKeysWow6432Node.GetSubKeyNames()|%{ 
                        $SubKeyWow6432Node = $UninstallKeysWow6432Node.OpenSubKey($_,$False); 
                        $DisplayName = $SubKeyWow6432Node.GetValue('DisplayName'); 
                        If ($DisplayName.Length -gt 0){ 
                            $Entry = $Base | Select-Object * 
                            $Entry.ComputerName = $ComputerName; 
                            $Entry.Name = $DisplayName.Trim();  
                            $Entry.Publisher = $SubKeyWow6432Node.GetValue('Publisher');  
                            [ref]$ParsedInstallDate = Get-Date                      
                            $Entry.InstallDate = $SubKeyWow6432Node.GetValue('InstallDate')
                            $Entry.EstimatedSize = [Math]::Round($SubKeyWow6432Node.GetValue('EstimatedSize')/1KB,1); 
                            $Entry.Version = $SubKeyWow6432Node.GetValue('DisplayVersion'); 
                            $Entry.Wow6432Node = $True; 
                            [Void]$Results.Add($Entry); 
                            } 
                        } 
                    } 
                } 
	        } 
	    } 
	    $Results 
}
Function Get-DNSClientCache{ 
	$DNSCache = @() 
	Invoke-Expression 'IPConfig /DisplayDNS' | 
	Select-String -Pattern 'Record Name' -Context 0,5 | 
	    %{ 
	        $Record = New-Object PSObject -Property @{ 
	        Name=($_.Line -Split ':')[1] 
	        Type=($_.Context.PostContext[0] -Split ':')[1] 
	        TTL=($_.Context.PostContext[1] -Split ':')[1] 
	        Length=($_.Context.PostContext[2] -Split ':')[1] 
	        Section=($_.Context.PostContext[3] -Split ':')[1] 
	        HostRecord=($_.Context.PostContext[4] -Split ':')[1] 
	        } 
	        $DNSCache +=$Record 
	    } 
	    return $DNSCache 
}
Function Initialize-Args {
    Param ( 
        [Parameter(Mandatory=$True)]$Args
    )
	
    try {
        For ( $i = 0; $i -lt $Args.count; $i++ ) { 
		    $CurrentArg = $Args[$i].ToString()
            if ($i -lt $Args.Count-1) {
				$Value = $Args[$i+1];
				If ($Value.Count -ge 2) {
					foreach ($Item in $Value) {
						Test-Strings $Item | Out-Null
					}
				}
				else {
	                $Value = $Args[$i+1];
					Test-Strings $Value | Out-Null
				}	                             
            } else {
                $Value = ''
            };

            switch -regex -casesensitive ($CurrentArg) {
                "^(-H|--Hostname)$" {
					if ($Value -ne ([System.Net.Dns]::GetHostByName((hostname.exe)).HostName).tolower() -and $Value -ne 'localhost') {
						$PingResult = Test-Connection -ComputerName $Value -Count 1 -Quiet
						if ($PingResult) {			
							$AuditStruct.Hostname = $Value
							$AuditStruct.win_test_ping = 'Succeeded'
							$i++						
		    			} 
						else {
    						$AuditStruct.win_test_ping = 'Failed'
		    				Write-Host "CRITICAL: Ping to $Value failed! Please provide valid reachable hostname."
							exit 1
		    			}
					}
					else {
						$AuditStruct.Hostname = $Value
						$i++
					}
						
                }
                "^(-O|--Output)$" {
                    if (($value -match 'Html' -or $value -match 'Logstash')) {
                        $InitStruct.Output = $value
                    } else {
                        throw "Some issue with output value. Value given is $value."
                    }
                    $i++
                 }
                "^(-L|--Logstash)$" {
                    if ($value -match "^[a-zA-Z0-9_.]+$") {
                        $InitStruct.Logstash = $value
                    } else {
                        throw "Some issues with logstash server value. Value given is $value."
                    }
                    $i++
                 }
          		"^(-p|--Port)$" {
                    if (($value -match "^[0-9]+$")-and ([int]$value -lt 65000)) {
                        $InitStruct.port = $value
                    } else {
                        throw "Some issues with port value. Value given is $value."
                    }
                    $i++
                 }

                "^(-h|--Help)$" {
                    Write-Help
                }
                default {
                    throw "Illegal arguments detected: $_"
                }
            }
        }
    } catch {
		Write-Host "UNKNOWN: $_"
        Exit 2
	}	
}
Function Test-Strings {
    Param ( [Parameter(Mandatory=$True)][string]$String )
    $BadChars=@("``", '|', ';', "`n")
    $BadChars | ForEach-Object {
        If ( $String.Contains("$_") ) {
            Write-Host 'Unknown: String contains illegal characters.'
            Exit 2
        }
    }
    Return $true
} 
Function Write-Help {
	Write-Host @"
naf_initiate_ms_server_audit.ps1:
This script is designed to audit a MS server and output result to html.
Arguments:
    -H  | --Hostname     => Optional hostname of remote system, default is localhost, not yet tested on remote host.
    -O  | --Output  		 => Audit output, currently to html and to Logstash or equivalents
    -L  | --Logstash     => Logstash server to send audit to
    -p  | --Port			 => Logstash server port to send audit to
    -h  | --Help         => Print this help output.
"@
    Exit 3
} 


#endregion Functions


# Main

Set-ProcessPriority scripteditor BelowNormal

if($Args.count -ge 1){
	Initialize-Args $Args
}
Test-Paths
Get-WmiTrees
Start-Audit



# Write-Host $AuditJson

if ($InitStruct.Output -eq 'Html') {
    	"<!DOCTYPE html>
	<html>
	<head>
	<meta charset=""utf-8"">
	<meta name=""description"" content=""This generated html page will display available information for the server passed as a parameter."">
	<style type=""text/css"">
	table {
	    border: 5px solid grey ;
	}
	th, td {
		padding: 5px 10px;
		border: 1px solid #999;
		text-align: left;
	}
	th {
	    background-color: #eee;
	}
	</style>
	</head>
	<body>
	<h1>NAF - Server Audit</h1>
	<br><hr>
    <h3>Audit information</h3>
	<table><thead><tr>
	<th>Start Date</th>
	<th>End Date</th>
	<th>Duration</th>
	<th>Executer</th>
	</tr></thead>
	<tbody><tr>
	<td>$($AuditStruct.audit_start)</td>
	<td>$($AuditStruct.audit_end)</td>
	<td>$($AuditStruct.audit_duration)</td>
	<td>$($AuditStruct.audit_executer)</td>
	</tr></tbody>
	</table>
	<br><hr>
	<h3>Connectivity Test</h3> 
	<table><thead><tr>
	<th>Test</th>
	<th>Result</th>
	</tr></thead>
	<tbody><tr>
	<td>Ping</td>
	<td>$($AuditStruct.win_test_ping)</td>
	</tr></tbody>
	<tbody><tr>
	<td>WMI</td>
	<td>$($AuditStruct.win_test_wmi)</td>
	</tr></tbody>
	<tbody><tr>
	<td>Eventlog</td>
	<td>$($AuditStruct.win_test_eventlog)</td>
	</tr></tbody>
	</table>
	<br><hr>
	<h3>System Information</h3>
	<table>
	<thead><tr><th>Query</th><th>Result</th></tr></thead>
	<tbody><tr><td>System Version </td><td>$($AuditStruct.host_version)</td></tr></tbody>
	<tbody><tr><td>System Caption </td><td>$($AuditStruct.host_caption)</td></tr></tbody>
	<tbody><tr><td>System Service Pack </td><td>$($AuditStruct.host_service_pack)</td></tr></tbody>
	<tbody><tr><td>System Last Boot </td><td>$($AuditStruct.host_lastboot)</td></tr></tbody>
	<tbody><tr><td>System Domain Role </td><td>$($AuditStruct.host_domain_role)</td></tr></tbody>
	<tbody><tr><td>System Type </td><td>$($AuditStruct.host_system_type)</td></tr></tbody>
	<tbody><tr><td>System Time Zone </td><td>$($AuditStruct.host_timezone)</td></tr></tbody>
	<tbody><tr><td>System Domain Controller</td><td>$($AuditStruct.host_current_domain_controller)</td></tr></tbody>
	<tbody><tr><td>System Current Registry Size</td><td>$($AuditStruct.host_registry_size)</td></tr></tbody>
	<tbody><tr><td>System Maximum Registry Size</td><td>$($AuditStruct.host_registry_size_max)</td></tr></tbody>
	<tbody><tr><td>System Manufacturer</td><td>$($AuditStruct.host_manufacturer)</td></tr></tbody>
	<tbody><tr><td>System Model</td><td>$($AuditStruct.host_model)</td></tr></tbody>
	<tbody><tr><td>System Total Physical Memory</td><td>$($AuditStruct.host_physical_memory)</td></tr></tbody>
	<tbody><tr><td>System Asset Tag</td><td>$($AuditStruct.host_assettag)</td></tr></tbody>
	<tbody><tr><td>System Serial Number</td><td>$($AuditStruct.host_serial_number)</td></tr></tbody>
	<tbody><tr><td>System Processor Name</td><td>$($AuditStruct.host_processor_name)</td></tr></tbody>
	<tbody><tr><td>System Processor Speed</td><td>$($AuditStruct.host_processor_speed)</td></tr></tbody>
	<tbody><tr><td>System Processor Voltage</td><td>$($AuditStruct.host_processor_voltage)</td></tr></tbody>
	</table>
	<br>	<hr>
	<h3>Disk Information</h3>
	<table><thead><tr>
	<th>Drive Name</th>
	<th>Total Size (GB)</th>
	<th>Free GB</th><th>Used GB</th>
	<th>Percentage Free</th>
	</tr></thead>
    <tbody>
	$($InitStruct.ServiceHtmlDisks)
    </tbody>
	</table>
	<br><hr>
	<h3>Local Group Members</h3>
	<table><thead><tr>
	<th>Group Name</th>
	<th>Group Members</tr>
	</thead>" | out-file -Append $InitStruct.OutputFile
	$InitStruct.AdminGroupMembers | foreach {
	 	$obj = new-object psobject -Property @{LocalAdmin = $_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)}
	 	"<tbody><tr>
		<td>$($InitStruct.ObjGroupName)</td>
		<td>$($obj.LocalAdmin)</td>
		</tr></tbody>" | out-file -Append $InitStruct.OutputFile
	} 
	"</table><br><hr>
    <h3>Service Configuration</h3>
	<table><thead><tr>
	<th>Service Name</th>
	<th>Display Name</th><th>Status</th>
	<th>Startup Type</th>
	</tr></thead><tbody>
    $($InitStruct.ServiceHtmlSvcs)
    </tbody></table><br><hr>
    <h3>Share Permissions</h3>
	<table><thead><tr><th>Sharename</th>
	<th>Security Principal</th>
	<th>File System Rights</th>
	<th>Access Control Type</th>
	</tr></thead>
	<tbody>
	$($InitStruct.ServiceHtmlSharePerm)
	</tobdy>
	</table><br><hr>
	<h3>Share NTFS Permissions</h3>
	<table><thead><tr>
	<th>Sharename</th>
	<th>Security Principal</th>
	<th>File System Rights</th>
	<th>Access Control Type</th>
	<th>Access Control Flags</th>
	</tr></thead>
	<tbody>
	$($InitStruct.ServiceHtmlShareNtfs)
	</tbody>
	</table><br><hr>
	<h3>Network Adapters</h3>
	<table><thead><tr>
	<th>Adapter Name</th>
	<th>Adapter Query</th>
	<th>Adapter Query Result</th>
	</tr></thead>
	<tbody>
	$($InitStruct.NetwAdapHtml)
	</tbody>
	</table><br><hr>
	<h3>Software Information</h3>
	<table><thead><tr>
	<th>Software Name</th>
	<th>Method</th>
	<th>Version</th>
	<th>Vendor</th>
	<th>Install Date</th>
	<th>Estimated Size</th>
	</tr></thead><tbody>
	$($InitStruct.SoftwareHtml)
	</tbody></table><br><hr>
	<h3>DNS Cache</h3>
	<table><thead><tr>
	<th>Name</th>
	<th>Section</th>
	<th>TTL</th>
	<th>Type</th>
	<th>Length</th>
	<th>Host Record</th>
	</tr></thead><tbody>
	$($InitStruct.DnsCacheHtml)
	</tbody>
	</table><br><hr>
	<h4>Hosts file</h3>
	<table><thead><tr>
	<th>IP</th>
	<th>DNS</th>
	</tr></thead>
	<tbody>
	$($InitStruct.HostsHtml)	
	</tbody>
	</table><br><hr>
	</html>" | out-file -append $InitStruct.OutputFile
}
if ($InitStruct.Output -eq 'Logstash') {
    $AuditJson = $AuditStruct | ConvertTo-Json
	Send-JsonOverTcp $InitStruct.Logstash $InitStruct.Port $Auditjson
}



Set-ProcessPriority scripteditor Normal

Write-Host "OK: Audit of $($AuditStruct.Hostname) succeeded in $($AuditStruct.audit_duration) time."
exit 0
using this input:

Code: Select all

tcp {
    type => 'powershell-audit'
    port => 5600
    codec => json {
        charset => 'UTF-8'
    }
}
And calling the script like this:

./naf_initiate_ms_server_audit.ps1 -L <nlsserver> -p 5600 -O Logstash

Any help or tip is welcome. One script run takes about 25 seconds.

Grtz

Willem
Nagios XI 5.8.1
https://outsideit.net
User avatar
WillemDH
Posts: 2320
Joined: Wed Mar 20, 2013 5:49 am
Location: Ghent
Contact:

Re: NLS for auditing

Post by WillemDH »

Hmm,

Seem like some fields are not getting parsed on some systems. When the system max registry size cannot be found, I'm setting it to undefined with this piece of code:

Code: Select all

if (!$AuditStruct.host_registry_size_max) {$AuditStruct.host_registry_size_max = "Undefined"}
The elasticsearch logs tell me this:

Code: Select all

org.elasticsearch.index.mapper.MapperParsingException: failed to parse [host_registry_size_max]
        at org.elasticsearch.index.mapper.core.AbstractFieldMapper.parse(AbstractFieldMapper.java:414)
        at org.elasticsearch.index.mapper.object.ObjectMapper.serializeValue(ObjectMapper.java:648)
        at org.elasticsearch.index.mapper.object.ObjectMapper.parse(ObjectMapper.java:501)
        at org.elasticsearch.index.mapper.DocumentMapper.parse(DocumentMapper.java:534)
        at org.elasticsearch.index.mapper.DocumentMapper.parse(DocumentMapper.java:483)
        at org.elasticsearch.index.shard.service.InternalIndexShard.prepareCreate(InternalIndexShard.java:376)
        at org.elasticsearch.action.bulk.TransportShardBulkAction.shardIndexOperation(TransportShardBulkAction.java:430)
        at org.elasticsearch.action.bulk.TransportShardBulkAction.shardOperationOnPrimary(TransportShardBulkAction.java:158)
        at org.elasticsearch.action.support.replication.TransportShardReplicationOperationAction$AsyncShardOperationAction.performOnPrimary(TransportShardReplicationOperationAction.java:522)
        at org.elasticsearch.action.support.replication.TransportShardReplicationOperationAction$AsyncShardOperationAction$1.run(TransportShardReplicationOperationAction.java:421)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NumberFormatException: For input string: "Undefined"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Long.parseLong(Long.java:441)
        at java.lang.Long.parseLong(Long.java:483)
        at org.elasticsearch.common.xcontent.support.AbstractXContentParser.longValue(AbstractXContentParser.java:145)
        at org.elasticsearch.index.mapper.core.LongFieldMapper.innerParseCreateField(LongFieldMapper.java:296)
        at org.elasticsearch.index.mapper.core.NumberFieldMapper.parseCreateField(NumberFieldMapper.java:223)
        at org.elasticsearch.index.mapper.core.AbstractFieldMapper.parse(AbstractFieldMapper.java:404)
        ... 12 more
Could it be that Logstash made this field 'numeric' as the first host did have a max reg size?

After changing the code to

Code: Select all

if (!$AuditStruct.host_registry_size_max) {$AuditStruct.host_registry_size_max = 0}
It suddenly worked.. So this would imply once a field has a certain type, it can no longer accept other types for that field? As I'm not really planning to graph this particular field, how can I reset the type for this field to a string, so I can leave the value to Undefined if no max registry size was set, preferably without having to set a filter?

Grtz
Nagios XI 5.8.1
https://outsideit.net
jolson
Attack Rabbit
Posts: 2560
Joined: Thu Feb 12, 2015 12:40 pm

Re: NLS for auditing

Post by jolson »

We cannot change a field type after that data has been indexed. When elasticsearch sees an initial type in its database, information that enters that field afterward retains that same type. This is by and large one of the biggest difficulties of working with elasticsearch on a deeper level.

To explain further, I'd like to reference a stack exchangepost.
The mapping for fields in an index is set when the first document is inserted in the index. Changing the mapping will not update any old documents in the index, nor affect any new documents that are inserted into that index.

If you're in development, the easiest thing is to drop the index (thus deleting your earlier data). Any new documents would then use your new mapping.

If you can't toss the old data, you can wait for tomorrow, when you'll get a new index.

If necessary, you can also rebuild the index, but I've always felt it to be a pain.
Our most painless option will be to wait for tomorrow - otherwise a reindexing will need to occur. To ensure that the data is properly inserted tomorrow, you can add a suffix to your logstash filter. By default, everything is stored as a string. You can convert to an integer or float using the following syntax.

%{NUMBER:day:int}
%{NUMBER:month:float}
Twits Blog
Show me a man who lives alone and has a perpetually clean kitchen, and 8 times out of 9 I'll show you a man with detestable spiritual qualities.
Locked