100.1. Using PowerShell Scripts
PowerShell scripts can be used with NXLog for generating, processing, and forwarding logs, as well as for generating configuration content.
By default, Windows services run under the NT AUTHORITY\SYSTEM
user
account. Depending on the purpose of a PowerShell script, its operations may
require additional permissions. In this case, either change the NXLog
service account (see Running Under a Custom Account on Windows) or add permissions as
required to the SYSTEM
account.
100.1.1. Generating Logs
For generating logs, a PowerShell script can be used with the im_exec module. For another example, see Collecting Audit Logs via the SharePoint API.
This configuration uses the im_exec module to execute
powershell.exe
with the specified arguments, including the path to the
script. The script creates an event and writes it to standard output in JSON
format. The xm_json parse_json() procedure is used
to parse the JSON so all the fields are available in the event record.
The script shows header examples for running the script under a different architecture than the NXLog agent. Also, a simple file-based position cache is included to demonstrate how a script can resume from the previous position when the agent or module instance is stopped and started again.
Because the end value of one poll and the start value of the next poll are
equal, an actual source read should not include exact matches for both start
and end values (to prevent reading duplicate events). For example, either the
start value should be excluded ($start < $event ≤ $end
) or the end value
($start ≤ $event < $end
).
Note
|
This example requires PowerShell 3 or later to transport structured data in JSON format. If structured data is required with an earlier version of PowerShell, CSV format could be used instead; see the next example. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<Extension _json>
Module xm_json
</Extension>
envvar systemroot
<Input powershell>
Module im_exec
Command "%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe"
# Use "-Version" to select a specific PowerShell version.
#Arg "-Version"
#Arg "3"
# Bypass the system execution policy for this session only.
Arg "-ExecutionPolicy"
Arg "Bypass"
# Skip loading the local PowerShell profile.
Arg "-NoProfile"
# This specifies the path to the PowerShell script.
Arg "-File"
Arg "C:\ps_input.ps1"
# Any additional arguments are passed to the PowerShell script.
Arg "arg1"
<Exec>
# Parse JSON
parse_json();
# Convert $EventTime field to datetime
$EventTime = parsedate($EventTime);
</Exec>
</Input>
#Requires -Version 3
# Use this if you need 64-bit PowerShell (has no effect on 32-bit systems).
#if ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") {
# Write-Debug "Running 64-bit PowerShell."
# &"$env:SYSTEMROOT\SysNative\WindowsPowerShell\v1.0\powershell.exe" `
# -NonInteractive -NoProfile -ExecutionPolicy Bypass `
# -File "$($myInvocation.InvocationName)" $args
# exit $LASTEXITCODE
#}
# Use this if you need 32-bit PowerShell.
#if ($env:PROCESSOR_ARCHITECTURE -ne "x86") {
# Write-Debug "Running 32-bit PowerShell."
# &"$env:SYSTEMROOT\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" `
# -NonInteractive -NoProfile -ExecutionPolicy Bypass `
# -File "$($myInvocation.InvocationName)" $args
# exit $LASTEXITCODE
#}
# The position is saved in this file for resuming.
$CacheFile = 'C:\nxlog_position_cache.txt'
# The source is queried at this interval in seconds.
$PollInterval = 1
################################################################################
# Get position from the cache file (on the first run, create the file)
# This example uses a timestamp position, but some sources may use an id, etc.
function Get-Position {
param( $file )
if (Test-Path $file) {
# Read timestamp from file
$time = (Get-Date (Get-Content $file -First 1))
# Convert timestamp to UTC
$time = $time.ToUniversalTime()
# Round down to nearest second
$time = $time.AddTicks(-($time.Ticks % 10000000))
}
else {
# Get current time in UTC
$time = [System.DateTime]::UtcNow
# Round down to nearest second
$time = $time.AddTicks(-($time.Ticks % 10000000))
# Create position cache file with specified time
Save-Position $file $time
}
return $time
}
# Save position to cache file
function Save-Position {
param( $file, $time )
Out-File -FilePath $file -InputObject $time.ToString('o')
}
# Main
Try {
$start = Get-Position $CacheFile
$count = 0
$cpu = Get-WmiObject Win32_Processor | Select -First 1 -Expand datawidth
$os = [int]::Parse(((gwmi Win32_OperatingSystem).OSArchitecture -Split "-")[0])
$process = [IntPtr]::Size * 8
# Repeatedly read from the source
while($true) {
$count += 1
# Get current time in UTC and round down to the nearest second
$now = [System.DateTime]::UtcNow
$end = $now.AddTicks(-($now.Ticks % 10000000))
# Set event fields
$event = @{
Arguments = [system.String]::Join(", ", $args);
StartTime = $start.ToString('o');
EndTime = $end.ToString('o');
CPUBitness = $cpu;
OSBitness = $os;
ProcessBitness = $process;
Message = "Test event $count for this run";
}
# Return event as JSON
$event | ConvertTo-Json -Compress | Write-Output
# Save position to cache file and sleep until the next iteration
Save-Position $CacheFile $end
Start-Sleep -Seconds $PollInterval
$start = $end
}
}
Catch {
Write-Error "An unhandled exception occurred!"
exit 1
}
PowerShell 2 does not support JSON. Instead, events can be formatted as CSV and parsed with an xm_csv module instance.
In this example, the PowerShell script generates output strings in CSV format. The xm_csv parse_csv() procedure is used to parse the CSV strings into fields in the event record. Note that the fields must be provided, sorted by name, in the xm_csv Fields directive (and corresponding types should be provided via the FieldTypes directive).
Warning
|
For best results with structured data, use JSON with PowerShell 3 or later (see the previous example). |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Extension csv_parser>
Module xm_csv
Fields Arguments, EventTime, Message
FieldTypes string, datetime, string
</Extension>
envvar systemroot
<Input powershell>
Module im_exec
Command "%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe"
Arg "-Version"
Arg "2"
Arg "-ExecutionPolicy"
Arg "Bypass"
Arg "-NoProfile"
Arg "-File"
Arg "C:\ps2_input.ps1"
Exec csv_parser->parse_csv();
</Input>
#Requires -Version 2
$count = 0
while($true) {
$count += 1
$now = [System.DateTime]::UtcNow
# Set event fields
$event = @{
Arguments = [system.String]::Join(", ", $args);
EventTime = $now.ToString('o');
Message = "event$count";
}
# Return event as CSV
$row = New-Object PSObject
$event.GetEnumerator() | Sort-Object -Property Name | ForEach-Object {
$row | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.Value
}
$row | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Write-Output
Start-Sleep -Seconds 5
}
100.1.2. Forwarding Logs
For forwarding logs, a PowerShell script can be used with the om_exec module.
This configuration uses om_exec to execute powershell.exe
with
the specified arguments, including the path to the script. The script reads
events on standard input.
Note
|
This configuration requires PowerShell 3 or later for its JSON support and to correctly read lines from standard input. |
Tip
|
See the Using PowerShell to Generate Logs example above for more details
about powershell.exe arguments and PowerShell code for explicitly
specifying a 32-bit or 64-bit environment.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Extension _json>
Module xm_json
</Extension>
envvar systemroot
<Output powershell>
Module om_exec
Command "%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe"
Arg "-ExecutionPolicy"
Arg "Bypass"
Arg "-NoProfile"
Arg "-File"
Arg "C:\ps_output.ps1"
Exec to_json();
</Output>
100.1.3. Generating Configuration
A PowerShell script can be used with the include_stdout directive to generate dynamic NXLog configuration. NXLog will execute the script during parsing of the configuration file.
Because include_stdout does not support arguments, it is simplest to use a batch/PowerShell polyglot script for this purpose. For another example, see Automatic Retrieval of IIS Site Log Locations.
Tip
|
The Command Prompt may print '@' is not recognized if a Unicode byte
order mark (BOM) is included in the batch file. To fix this, use Notepad
and select the ANSI encoding when saving the file.
|
This configuration uses PowerShell code to generate the File directive for the Input instance.
@( Set "_= (
REM " ) <#
)
@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
set powershell=powershell.exe
REM Use this if you need 64-bit PowerShell (has no effect on 32-bit systems).
REM if defined PROCESSOR_ARCHITEW6432 (
REM set powershell=%SystemRoot%\SysNative\WindowsPowerShell\v1.0\powershell.exe
REM )
REM Use this if you need 32-bit PowerShell.
REM if NOT %PROCESSOR_ARCHITECTURE% == x86 (
REM set powershell=%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
REM )
%powershell% -ExecutionPolicy Bypass -NoProfile ^
-Command "iex ((gc '%~f0') -join [char]10)"
EndLocal & Exit /B %ErrorLevel%
#>
# PowerShell code starts here.
# To make NXLog return an error, write to standard error and exit 1
if ($false) {
[Console]::Error.WriteLine("This is an error")
exit 1
}
else {
# Anything written to standard output is used as configuration content
Write-Output "File 'C:\in.log'"
}