1 - Practical usage in a Domain Environment (GPO-driven collection and conversion)
This example demonstrates how to implement a GPO-driven workflow for collecting and converting DNS debug logs on domain controllers.
This document outlines a practical, end-to-end example of running DNSServer.DebugLogParser in an Active Directory domain environment, focused on converting Windows DNS Server debug logs on domain controllers.
This example is accompanied by a ZIP archive, which provide the policy artifacts used to implement the workflow described here.
The most relevant artifacts are backup manifest, GPO report, Files.xml, Set-DNSServerDebugLogging.ps1, and ScheduledTasks.xml.
Scenario
- Multiple domain controllers (DCs) host the DNS Server role.
- DNS debug logging is enabled and configured consistently on each DC via a scheduled task (writing log files to
C:\Administration\Logs\DNSServer). - A centrally managed process converts logs into CSV for:
- security analytics
- operational reporting
- troubleshooting
- compliance/retention
Target outcomes
- Consistent conversion settings across all DCs
- Predictable output location and naming
- Optional compression to reduce storage footprint
- Optional statistics outputs for quick daily rollups
- Minimal host-side risk, with explicit rerun and cleanup considerations
Suggested architecture
Collection model: local convert + central pull
- Each DC writes DNS debug logs to disk with rollover enabled.
- Each DC converts rotated
*.log files into a data CSV and statistics CSVs, then compresses the outputs into *.zip on a schedule (Task Scheduler). - Outputs are written next to the log files so the pipeline stays simple.
- A central server collects the
*.zip outputs, for example via file share ingestion, scheduled copy, SIEM forwarder, or an agent-based collector.
This model minimizes network reads of large raw log files and keeps parsing close to the data.
GPO workflow
The workflow is implemented through Group Policy (computer configuration) to ensure consistent settings across all domain controllers.
Reference implementation included in this repository (GPO report):
- Archive/report name:
T0-C-Analytics-DNSDebugLogging - Backup ID:
{2B6F16BC-0E7C-4787-83D7-2854FED882EE} - GPO link target:
corp.company.com/Domain Controllers - Item filter (used for both file deployment and scheduled tasks): only applies if
C:\Windows\System32\dns.exe exists
1) Prerequisites (folders + module)
The provided backup assumes these folders already exist. Add separate GPP items if you want the deployment to create them automatically:
C:\Administration\ScriptsC:\Administration\Logs\DNSServer
The provided backup also assumes Convert-DNSDebugLogFile is already available on the DCs. The conversion task starts Windows PowerShell 5.1 with -NoProfile and does not import the module explicitly, so the module must be installed in a machine-wide Windows PowerShell module path that is visible to LocalSystem. Recommended approaches:
- Install DNSServer.DebugLogParser on the DCs. For example, from PowerShell Gallery (if policy allows).
- Deploy the module via internal repo / file share so it is discoverable in
$env:PSModulePath - Add an explicit
Import-Module to the task action if you want deterministic loading behavior
2) Deploy the debug logging configuration script (GPP Files)
The GPO deploys the script:
- Source (in SYSVOL via GPP):
%GptPath%\Preferences\Files\Set-DNSServerDebugLogging.ps1 - Target (on each DC):
C:\Administration\Scripts\Set-DNSServerDebugLogging.ps1
This is implemented in the GPP Files preference item in Files.xml.
The GPO creates a scheduled task named Set-DNSServerDebugLogging.
- Security context in the supplied backup:
SYSTEM with logon type S4U - Trigger in the supplied backup: daily (start boundary
2025-03-01T00:00:01) - Action in ScheduledTasks.xml:
powershell.exe -ExecutionPolicy RemoteSigned -command " & { C:\Administration\Scripts\Set-DNSServerDebugLogging.ps1 }"- Working directory:
C:\Administration\Scripts
The linked Set-DNSServerDebugLogging.ps1 script configures DNS debug logging via Get-DnsServerDiagnostics / Set-DnsServerDiagnostics and (notably):
- Enables logging to file + rollover
- Writes to:
C:\Administration\Logs\DNSServer\DnsDebugLog_<COMPUTERNAME>.<Domain>_.log - Uses a 10 MB rollover size per file
- Captures primarily query-related activity (queries + notifications + updates + question transactions) and excludes full packet logging
4) Scheduled task: convert rotated debug logs to compressed CSV
The GPO creates a scheduled task named Convert-DNSDebugLogs.
- Security context in the supplied backup:
SYSTEM with logon type InteractiveToken - Trigger in the supplied backup: daily (start boundary
2026-01-01T00:30:00) - Working directory:
C:\Administration\Logs\DNSServer - Action in ScheduledTasks.xml (formatted for readability):
Get-ChildItem .\*.log |
Sort-Object lastwritetime, Name -Descending |
Select-Object -Skip 1 |
Convert-DNSDebugLogFile `
-ComputerName $env:COMPUTERNAME `
-Delimiter ';' `
-OutputType Both `
-ContextFilter Packet `
-OutputCulture sv-SE `
-CompressOutput
Design notes:
Select-Object -Skip 1 intentionally avoids processing the newest (active) log file. Convert-DNSDebugLogFile is able to read a log that DNS Server currently has open, but that file changes while it is being read: the output can miss the newest entries or end with a truncated record, and two runs do not produce identical results. Skipping the active file keeps the scheduled output reproducible.- That means current data is exported only after rollover; on low-volume DCs, the active log may stay unprocessed for more than a day. Lower the rollover size if that delay is too long for your use case.
- Because Convert-DNSDebugLogFile defaults
-OutputFile to “same folder, same name, .csv”, outputs land next to the *.log inputs. - With
-CompressOutput, each processed log produces a *.zip and the intermediate CSVs are removed. - Unless you archive or delete processed
*.log files, later runs will reprocess every non-active log again. - The task throws if
$Error.Count -gt 0 to signal a failed run.
5) Central ingestion
Options (pick one):
- File share ingestion: DC writes (or copies)
C:\Administration\Logs\DNSServer\*.zip to \\fileserver\share\dns\$env:COMPUTERNAME\... (grant share and NTFS rights to the DC computer accounts or the Domain Controllers group if the task runs as SYSTEM) - Pull model: central job reads
\\dc\C$\Administration\Logs\DNSServer\*.zip (least preferred; requires admin shares) - Agent forwarder: SIEM / log pipeline that ships the
*.zip outputs
Operational considerations
- Least privilege: tasks run as
SYSTEM; ensure the local folders are writable and that any UNC targets grant access to the computer account if used. - Signing policy: both tasks use
-ExecutionPolicy RemoteSigned; sign or unblock the deployed script and module according to your policy. - Disk usage:
-CompressOutput helps significantly, but this workflow does not remove source logs; plan retention and cleanup. - Re-processing behavior: unless you archive or delete processed logs, the conversion task will process every non-active
*.log file again on later runs. This is simple and robust, but it can overwrite outputs and create duplicate downstream ingestion if your collector does not deduplicate. - Multi-DC consolidation:
-ComputerName $env:COMPUTERNAME is included so consolidated datasets remain traceable. Note that -ComputerName only labels the output — it performs no remote connection. - Network shares: Convert-DNSDebugLogFile can read source logs from SMB/UNC paths, but converting locally and shipping the compressed results stays the better pattern for large raw logs. If you do use a UNC path, remember that a task running as
SYSTEM authenticates on the network as the computer account and needs share plus NTFS rights there. - Date format:
-OutputCulture sv-SE is used deliberately so timestamps are written as 2026-01-20 23:00:16. That format is read without a hint by SQL Server, Power BI and most import tooling, regardless of the locale of the DC that produced the log. - Validation: header validation remains enabled because the task does not use
-SkipHeaderValidation (recommended).
Validate and adapt (using the ZIP)
Use the ZIP archive as the reference implementation, then align the following aspects to your environment:
- Target scope: which DCs / OUs receive the policy
- Execution identity: confirm both scheduled tasks use the intended logon type (
S4U vs InteractiveToken) or normalize them to your standard - Folder creation: decide whether
C:\Administration\Scripts and C:\Administration\Logs\DNSServer are pre-staged elsewhere or should be created by additional GPP items - Paths: confirm
C:\Administration\Scripts and C:\Administration\Logs\DNSServer match your standards - Retention: decide whether to keep raw logs and for how long (especially if enabling cleanup options)
- Ingestion: confirm where CSV/ZIP outputs are written and how they are collected centrally
If you want to validate the exact GPO configuration without importing it, the authoritative sources in this repo are:
2 - Scheduled Task Example
This example demonstrates how to create a Windows Scheduled Task that runs a PowerShell script to process DNS debug logs daily.
This script creates a Windows Scheduled Task that runs daily at 2:00 AM. It imports the module and processes all the log file in the folder where the script is executed (in this example “C:\Administration\Logs\DNS”). The process does compress the output into ZIP-files and removes the original to maintain hygiene.
$actionParams = @{
Execute = "powershell.exe"
Argument = '-ExecutionPolicy RemoteSigned -Command "Import-Module DNSServer.DebugLogParser; Get-ChildItem .\*.log | Sort-Object lastwritetime, Name -Descending | Convert-DNSDebugLogFile -ComputerName $env:COMPUTERNAME -Delimiter \";\" -OutputType Both -ContextFilter Packet -OutputCulture sv-SE -CompressOutput"'
WorkingDirectory = "C:\Administration\Logs\DNS"
}
$Action = New-ScheduledTaskAction @actionParams
$Trigger = New-ScheduledTaskTrigger -Daily -At "2:00AM"
Register-ScheduledTask -TaskName "Process DNS Logs" -Action $Action -Trigger $Trigger -Description "Convert DNS debug logs to CSV daily"
3 - Security Analysis Workflow with SQL Server
Convert DNS debug logs to CSV with DNSServer.DebugLogParser, import the result into SQL Server, and run a simple detection query for suspicious TXT record activity.
This example shows a practical workflow for security analytics: parse a DNS
debug log with Convert-DNSDebugLogFile, import the generated CSV into SQL
Server, and run a query that highlights unusually high volumes of TXT record
lookups.
For best interoperability, this example writes timestamps in an ISO-like format
by using -OutputCulture 'sv-SE'.
Required modules
This example uses the following PowerShell modules:
DNSServer.DebugLogParserSqlServer
Install them if needed:
Install-Module -Name DNSServer.DebugLogParser -Scope CurrentUser
Install-Module -Name SqlServer -Scope CurrentUser
Scenario
Use this workflow when you want to move parsed DNS debug log data into SQL
Server so you can:
- search large datasets efficiently
- build repeatable detection queries
- correlate activity across multiple DNS servers
- retain normalized data for later investigation
Output of the conversion step
Convert-DNSDebugLogFile does not write directly to SQL Server. It first
creates a CSV file. That CSV file is the dataset imported into the database.
In this example:
- input log:
C:\Administration\Logs\DNS\dns.log - generated CSV:
C:\Administration\Logs\DNS\dns.csv - destination table:
dbo.DNSQueries
Create the target table
Run the following statement once in SQL Server to create the target table.
IF OBJECT_ID('dbo.DNSQueries', 'U') IS NULL
BEGIN
CREATE TABLE dbo.DNSQueries (
DateTime datetime2(0) NOT NULL,
ThreadId int NULL,
Context nvarchar(20) NULL,
PacketId int NULL,
Protocol nvarchar(10) NULL,
Direction nvarchar(10) NULL,
ClientIP nvarchar(64) NULL,
Xid nvarchar(16) NULL,
Type nvarchar(16) NULL,
Opcode nvarchar(16) NULL,
FlagsHex nvarchar(16) NULL,
FlagsChar nvarchar(16) NULL,
ResponseCode nvarchar(32) NULL,
QuestionType nvarchar(32) NULL,
QuestionName nvarchar(512) NULL,
Information nvarchar(max) NULL,
Details nvarchar(max) NULL,
ComputerName nvarchar(256) NULL
);
END;
Convert the log and import the CSV
The following PowerShell example performs the full workflow:
- imports the required modules
- converts the DNS debug log to CSV
- loads the generated CSV
- bulk imports the rows into SQL Server
# Requires -Modules DNSServer.DebugLogParser, SqlServer
Import-Module -Name DNSServer.DebugLogParser -ErrorAction Stop
Import-Module -Name SqlServer -ErrorAction Stop
$logPath = 'C:\Administration\Logs\DNS\dns.log'
$csvPath = 'C:\Administration\Logs\DNS\dns.csv'
$serverInstance = 'SQLServer'
$databaseName = 'DNSLogs'
$delimiter = ';'
$createTableSql = @'
IF OBJECT_ID('dbo.DNSQueries', 'U') IS NULL
BEGIN
CREATE TABLE dbo.DNSQueries (
DateTime datetime2(0) NOT NULL,
ThreadId int NULL,
Context nvarchar(20) NULL,
PacketId int NULL,
Protocol nvarchar(10) NULL,
Direction nvarchar(10) NULL,
ClientIP nvarchar(64) NULL,
Xid nvarchar(16) NULL,
Type nvarchar(16) NULL,
Opcode nvarchar(16) NULL,
FlagsHex nvarchar(16) NULL,
FlagsChar nvarchar(16) NULL,
ResponseCode nvarchar(32) NULL,
QuestionType nvarchar(32) NULL,
QuestionName nvarchar(512) NULL,
Information nvarchar(max) NULL,
Details nvarchar(max) NULL,
ComputerName nvarchar(256) NULL
);
END
'@
Convert-DNSDebugLogFile `
-InputFile $logPath `
-ComputerName 'DNS01' `
-OutputType CSV `
-OutputFile $csvPath `
-Delimiter $delimiter `
-OutputCulture 'sv-SE'
$rows = Import-Csv -Path $csvPath -Delimiter $delimiter
if (-not $rows) {
throw "The generated CSV file '$csvPath' does not contain any rows."
}
$dataTable = [System.Data.DataTable]::new()
foreach ($columnName in $rows[0].PSObject.Properties.Name) {
$null = $dataTable.Columns.Add($columnName, [string])
}
foreach ($row in $rows) {
$dataRow = $dataTable.NewRow()
foreach ($column in $dataTable.Columns) {
$columnName = $column.ColumnName
$dataRow[$columnName] = $row.$columnName
}
$null = $dataTable.Rows.Add($dataRow)
}
$connectionString = "Server=$serverInstance;Database=$databaseName;Integrated Security=True"
$connection = [System.Data.SqlClient.SqlConnection]::new($connectionString)
try {
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $createTableSql
$null = $command.ExecuteNonQuery()
$bulkCopy = [System.Data.SqlClient.SqlBulkCopy]::new($connection)
$bulkCopy.DestinationTableName = 'dbo.DNSQueries'
foreach ($column in $dataTable.Columns) {
$null = $bulkCopy.ColumnMappings.Add($column.ColumnName, $column.ColumnName)
}
$bulkCopy.WriteToServer($dataTable)
}
finally {
$connection.Dispose()
}
Query for suspicious TXT record activity
Once the data is in SQL Server, you can search for clients that issue unusually
high numbers of TXT record queries.
SELECT
ComputerName,
ClientIP,
QuestionName,
COUNT(*) AS QueryCount
FROM dbo.DNSQueries
WHERE QuestionType = 'TXT'
GROUP BY
ComputerName,
ClientIP,
QuestionName
HAVING COUNT(*) > 100
ORDER BY QueryCount DESC;
If you prefer to run the query from PowerShell, use Invoke-Sqlcmd from the
SqlServer module:
$query = @'
SELECT
ComputerName,
ClientIP,
QuestionName,
COUNT(*) AS QueryCount
FROM dbo.DNSQueries
WHERE QuestionType = 'TXT'
GROUP BY
ComputerName,
ClientIP,
QuestionName
HAVING COUNT(*) > 100
ORDER BY QueryCount DESC;
'@
Invoke-Sqlcmd `
-ServerInstance 'SQLServer' `
-Database 'DNSLogs' `
-Query $query
Why TXT queries are interesting
High volumes of TXT record lookups can be worth reviewing because they may
indicate:
- DNS tunneling
- data exfiltration over DNS
- abuse of TXT records by malware or tooling
- unusually noisy or misconfigured clients
This query is only a starting point. In production, you should tune the
threshold and add filters that match your environment.
Operational notes
Import-Csv reads the full file into memory. For very large log exports,
consider a streaming approach instead of building a full DataTable.- Keep
-ComputerName in the conversion step so records remain attributable
after central ingestion. - Use a consistent delimiter and culture during export and import.
- Validate retention, indexing, and access control in SQL Server before using
this workflow for long-term storage.