I often find myself needing IIS log files for testing purposes. Whether I'm developing log analysis tools, testing monitoring scripts, or creating demonstrations for stakeholders, having realistic test data is crucial. However, real production log files contain sensitive information that shouldn't be used in development environments, and creating manual test data is time-consuming and unrealistic.
That's why I created a PowerShell script that generates authentic-looking IIS log files with customizable fake data. In this post, I'll walk you through the solution and show you how to use it for your own testing needs.
Why You Need Fake IIS Log Data?
There are several scenarios where you might need test IIS log files:
- Testing log analysis scripts without exposing real user data
- Developing monitoring and alerting systems that need realistic traffic patterns
- Creating demonstrations for security tools or dashboard applications
- Load testing log processing systems with known data volumes
- Training environments where you need realistic but safe data
The challenge is that manually creating realistic log files is tedious, and using real production logs poses privacy and security risks.
PowerShell IIS Log Generator
I developed a PowerShell script that generates authentic IIS W3C Extended Log Format files with customizable parameters. The script creates realistic traffic patterns while using completely fake data, including bear-themed usernames for a touch of fun.
Key Features
- Authentic IIS log format with proper headers and field structure
- Realistic traffic patterns with weighted distributions for HTTP methods and status codes
- Bear-themed fake data that's obviously fake but still realistic in structure
- Customizable HTTP methods - defaults to GET-only but can include POST, PUT, DELETE, HEAD
- Configurable entry counts for different testing scenarios
- Chronological timestamps that simulate realistic request timing
How the Script Works
Let me break down the key components of the script:
1. Parameter Configuration
The script starts with flexible parameters that control the output:
param(
[Parameter(Mandatory=$false)]
[int]$EntryCount = 100,
[Parameter(Mandatory=$false)]
[string]$OutputPath = ".\ex$(Get-Date -Format 'yyMMdd').log",
[Parameter(Mandatory=$false)]
[switch]$IncludePOST,
[Parameter(Mandatory=$false)]
[switch]$IncludePUT,
[Parameter(Mandatory=$false)]
[switch]$IncludeDELETE,
[Parameter(Mandatory=$false)]
[switch]$IncludeHEAD,
[Parameter(Mandatory=$false)]
[switch]$IncludeAllMethods
)
2. Customizable Data Sources
The script includes arrays of fake data that you can easily customize:
# Bear-themed usernames
$BearUsernames = @(
"grizzly_admin", "polar_bear", "kodiak_user", "panda_dev",
"brown_bear_svc", "teddy_bear", "honey_bear", "yogi_bear"
)
# Source IP ranges
$SourceIPRanges = @(
@{ Network = "192.168.1"; Range = 1..254 },
@{ Network = "10.0.0"; Range = 1..254 },
@{ Network = "203.0.113"; Range = 1..254 } # RFC 5737 test range
)
# Destination URLs
$DestinationURLs = @(
"/", "/login.aspx", "/api/bears", "/admin/users",
"/hibernate/status", "/salmon/fishing", "/den/secure"
)
3. Weighted Random Selection
The script uses realistic distributions for HTTP status codes and methods:
# HTTP status codes with realistic distribution
$StatusCodes = @(
@{ Code = 200; Weight = 75 }, # Most requests succeed
@{ Code = 304; Weight = 10 }, # Not modified
@{ Code = 404; Weight = 8 }, # Not found
@{ Code = 302; Weight = 3 }, # Redirect
@{ Code = 500; Weight = 2 } # Server error
)
4. Dynamic HTTP Method Selection
By default, the script generates only GET requests. You can add other methods using switches:
# Default: Only GET requests
$HttpMethods = @(@{ Method = "GET"; Weight = 100 })
# Add other methods based on parameters
if ($IncludePOST) {
$HttpMethods += @{ Method = "POST"; Weight = 25 }
$HttpMethods[0].Weight = 75 # Adjust GET weight
}
Usage Examples
Here's how to use the script in different scenarios:
Basic Usage (GET requests only)
.\Generate-IISLog.ps1
Including Other HTTP Methods
# Add POST requests for form submissions
.\Generate-IISLog.ps1 -IncludePOST
# Include PUT and DELETE for API testing
.\Generate-IISLog.ps1 -IncludePUT -IncludeDELETE
# Generate comprehensive logs with all HTTP methods
.\Generate-IISLog.ps1 -IncludeAllMethods
Custom Entry Counts
# Generate larger log files for load testing
.\Generate-IISLog.ps1 -EntryCount 1000 -IncludePOST
# Create small focused test files
.\Generate-IISLog.ps1 -EntryCount 50
Sample Output
Here's what the generated log file looks like when using GET requests only:
#Software: Microsoft Internet Information Services 10.0
#Version: 1.0
#Date: 2024-12-28 10:30:15
#Fields: s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
10.0.0.100 GET / - 80 grizzly_admin 192.168.1.45 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-" 200 0 0 2847 523 156
10.0.0.100 GET /api/bears ?page=1&size=10 80 polar_bear 10.0.0.127 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-" 200 0 0 15243 734 289
10.0.0.100 GET /images/grizzly.jpg - 80 - 172.16.1.203 "BearBot/1.0 (+http://example.com/bearbot)" "-" 404 0 0 1247 432 89
10.0.0.100 GET /hibernate/status - 80 honey_bear 198.51.100.89 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-" 200 0 0 3421 267 201
10.0.0.100 GET /den/secure - 80 kodiak_user 192.168.1.178 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0" "-" 302 0 0 567 189 45
How do I understand the Output Format?
Each log entry follows the IIS W3C Extended Log Format:
- s-ip: Server IP address (10.0.0.100)
- cs-method: HTTP method (GET in this example)
- cs-uri-stem: Requested path (/api/bears)
- cs-uri-query: Query string parameters or "-" if none
- s-port: Server port (80)
- cs-username: Authenticated username or "-" for anonymous
- c-ip: Client IP address (randomly generated from defined ranges)
- cs(User-Agent): Browser or client identification
- cs(Referer): Referring page ("-" in our examples)
- sc-status: HTTP status code (200, 404, etc.)
- sc-substatus & sc-win32-status: Additional status codes
- sc-bytes: Bytes sent by server
- cs-bytes: Bytes sent by client
- time-taken: Request processing time in milliseconds
Script : Generate-IISLog.ps1
Here's the complete PowerShell script, there are no variables in the script all the customisation comes from the how the command is run.
# IIS Log File Generator
# Generates authentic IIS W3C Extended Log Format files
param(
[Parameter(Mandatory=$false)]
[int]$EntryCount = 100,
[Parameter(Mandatory=$false)]
[string]$OutputPath = ".\ex$(Get-Date -Format 'yyMMdd').log",
[Parameter(Mandatory=$false)]
[switch]$IncludePOST,
[Parameter(Mandatory=$false)]
[switch]$IncludePUT,
[Parameter(Mandatory=$false)]
[switch]$IncludeDELETE,
[Parameter(Mandatory=$false)]
[switch]$IncludeHEAD,
[Parameter(Mandatory=$false)]
[switch]$IncludeAllMethods
)
# =============================================================================
# CUSTOMIZABLE VARIABLES - Modify these sections as needed
# =============================================================================
# Bear-themed usernames pool
$BearUsernames = @(
"grizzly_admin", "polar_bear", "kodiak_user", "panda_dev", "brown_bear_svc",
"teddy_bear", "bear_cub", "honey_bear", "black_bear", "spectacled_bear",
"sun_bear", "sloth_bear", "bear_necessities", "yogi_bear", "smokey_bear",
"berenstein_bear", "care_bear", "gummy_bear", "bear_grylls", "chicago_bear",
"build_a_bear", "mama_bear", "papa_bear", "goldilocks", "winnie_pooh"
)
# Source IP address ranges (customize these ranges)
$SourceIPRanges = @(
@{ Network = "192.168.1"; Range = 1..254 },
@{ Network = "10.0.0"; Range = 1..254 },
@{ Network = "172.16.1"; Range = 1..254 },
@{ Network = "203.0.113"; Range = 1..254 }, # RFC 5737 test range
@{ Network = "198.51.100"; Range = 1..254 }, # RFC 5737 test range
@{ Network = "172.20.10"; Range = 1..50 }
)
# Destination URLs/endpoints (customize these paths)
$DestinationURLs = @(
"/", "/index.html", "/login.aspx", "/dashboard.aspx", "/api/bears",
"/images/grizzly.jpg", "/css/styles.css", "/js/main.js", "/favicon.ico",
"/admin/users", "/api/honey/inventory", "/bears/profile", "/den/secure",
"/hibernate/status", "/salmon/fishing", "/berries/forage", "/cubs/nursery",
"/winter/prep", "/cave/entrance", "/forest/patrol", "/river/crossing"
)
# HTTP methods distribution - dynamically built based on parameters
$HttpMethods = @(@{ Method = "GET"; Weight = 100 }) # Default: Only GET
# Add other methods based on parameters
if ($IncludeAllMethods) {
$HttpMethods = @(
@{ Method = "GET"; Weight = 70 },
@{ Method = "POST"; Weight = 20 },
@{ Method = "PUT"; Weight = 5 },
@{ Method = "DELETE"; Weight = 3 },
@{ Method = "HEAD"; Weight = 2 }
)
} else {
if ($IncludePOST) {
$HttpMethods += @{ Method = "POST"; Weight = 25 }
$HttpMethods[0].Weight = 75 # Adjust GET weight
}
if ($IncludePUT) {
$HttpMethods += @{ Method = "PUT"; Weight = 10 }
$HttpMethods[0].Weight = 70 # Adjust GET weight
}
if ($IncludeDELETE) {
$HttpMethods += @{ Method = "DELETE"; Weight = 5 }
$HttpMethods[0].Weight = 65 # Adjust GET weight
}
if ($IncludeHEAD) {
$HttpMethods += @{ Method = "HEAD"; Weight = 5 }
$HttpMethods[0].Weight = 60 # Adjust GET weight
}
}
# HTTP status codes with realistic distribution
$StatusCodes = @(
@{ Code = 200; Weight = 75 },
@{ Code = 304; Weight = 10 },
@{ Code = 404; Weight = 8 },
@{ Code = 302; Weight = 3 },
@{ Code = 500; Weight = 2 },
@{ Code = 403; Weight = 1 },
@{ Code = 401; Weight = 1 }
)
# User agents
$UserAgents = @(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"BearBot/1.0 (+http://example.com/bearbot)",
"HoneyHarvester/2.1 (Bear Den Monitoring System)"
)
# =============================================================================
# HELPER FUNCTIONS
# =============================================================================
function Get-WeightedRandom {
param([array]$Items)
$totalWeight = ($Items | Measure-Object -Property Weight -Sum).Sum
$random = Get-Random -Minimum 1 -Maximum ($totalWeight + 1)
$currentWeight = 0
foreach ($item in $Items) {
$currentWeight += $item.Weight
if ($random -le $currentWeight) {
return $item
}
}
return $Items[0]
}
function Get-RandomIP {
$range = $SourceIPRanges | Get-Random
$lastOctet = $range.Range | Get-Random
return "$($range.Network).$lastOctet"
}
function Get-RandomTimestamp {
param([datetime]$BaseTime)
# Generate timestamps within the last 24 hours, but in chronological order-ish
$randomSeconds = Get-Random -Minimum 1 -Maximum 300 # Up to 5 minutes between entries
return $BaseTime.AddSeconds($randomSeconds)
}
function Format-IISLogEntry {
param(
[datetime]$Timestamp,
[string]$ClientIP,
[string]$Username,
[string]$Method,
[string]$UriStem,
[int]$StatusCode,
[string]$UserAgent
)
$date = $Timestamp.ToString("yyyy-MM-dd")
$time = $Timestamp.ToString("HH:mm:ss")
$serverIP = "10.0.0.100" # Your server IP
$serverPort = "80"
$uriQuery = if ($Method -eq "GET" -and (Get-Random -Maximum 10) -lt 3) { "?page=1&size=10" } else { "-" }
$scStatus = $StatusCode
$scSubStatus = "0"
$scWin32Status = "0"
$scBytes = Get-Random -Minimum 200 -Maximum 50000
$csBytes = Get-Random -Minimum 100 -Maximum 5000
$timeTaken = Get-Random -Minimum 15 -Maximum 5000
# IIS W3C Extended Log Format
return "$serverIP $Method $UriStem $uriQuery $serverPort $Username $ClientIP `"$UserAgent`" `"-`" $scStatus $scSubStatus $scWin32Status $scBytes $csBytes $timeTaken"
}
# =============================================================================
# MAIN SCRIPT EXECUTION
# =============================================================================
Write-Host "🐻 Bear Den IIS Log Generator Starting..." -ForegroundColor Yellow
Write-Host "Generating $EntryCount log entries..." -ForegroundColor Green
# Display which HTTP methods will be included
$methodsList = ($HttpMethods | ForEach-Object { $_.Method }) -join ", "
Write-Host "HTTP Methods: $methodsList" -ForegroundColor Cyan
# Create IIS log header
$logHeader = @"
#Software: Microsoft Internet Information Services 10.0
#Version: 1.0
#Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
#Fields: s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
"@
# Initialize log content
$logEntries = @()
$logEntries += $logHeader
# Generate timestamp base (start from 24 hours ago)
$baseTime = (Get-Date).AddHours(-24)
$currentTime = $baseTime
Write-Host "Base timestamp: $($baseTime.ToString())" -ForegroundColor Cyan
# Generate log entries
for ($i = 1; $i -le $EntryCount; $i++) {
# Show progress
if ($i % 50 -eq 0) {
Write-Host "Generated $i entries..." -ForegroundColor Gray
}
# Generate random data for this entry
$clientIP = Get-RandomIP
$username = if ((Get-Random -Maximum 10) -lt 7) { $BearUsernames | Get-Random } else { "-" }
$method = (Get-WeightedRandom -Items $HttpMethods).Method
$uriStem = $DestinationURLs | Get-Random
$statusCode = (Get-WeightedRandom -Items $StatusCodes).Code
$userAgent = $UserAgents | Get-Random
# Update timestamp (roughly chronological)
$currentTime = Get-RandomTimestamp -BaseTime $currentTime
# Create log entry
$logEntry = Format-IISLogEntry -Timestamp $currentTime -ClientIP $clientIP -Username $username -Method $method -UriStem $uriStem -StatusCode $statusCode -UserAgent $userAgent
$logEntries += $logEntry
}
# Write to file
try {
$logEntries | Out-File -FilePath $OutputPath -Encoding UTF8
Write-Host "✅ Successfully generated IIS log file: $OutputPath" -ForegroundColor Green
Write-Host "📊 Total entries: $EntryCount" -ForegroundColor Green
Write-Host "📁 File size: $([math]::Round((Get-Item $OutputPath).Length / 1KB, 2)) KB" -ForegroundColor Green
# Show sample entries
Write-Host "`n🔍 Sample log entries:" -ForegroundColor Yellow
$sampleEntries = Get-Content $OutputPath | Select-Object -Skip 4 -First 3
foreach ($entry in $sampleEntries) {
Write-Host " $entry" -ForegroundColor Gray
}
} catch {
Write-Error "❌ Failed to write log file: $($_.Exception.Message)"
exit 1
}
Write-Host "`n🎉 Bear Den IIS Log Generator Complete!" -ForegroundColor Yellow
# Usage examples
Write-Host "`n📖 Usage Examples:" -ForegroundColor Yellow
Write-Host " Default (GET only): .\Generate-IISLog.ps1" -ForegroundColor Gray
Write-Host " Include POST: .\Generate-IISLog.ps1 -IncludePOST" -ForegroundColor Gray
Write-Host " Include PUT & DELETE: .\Generate-IISLog.ps1 -IncludePUT -IncludeDELETE" -ForegroundColor Gray
Write-Host " All HTTP methods: .\Generate-IISLog.ps1 -IncludeAllMethods" -ForegroundColor Gray
Write-Host " Custom count: .\Generate-IISLog.ps1 -EntryCount 500 -IncludePOST" -ForegroundColor Gray
Customization Tips
If you need to customise the script you can do so for many of the values as outlined below:
- Modify usernames: Change the
$BearUsernames
array to use your own fake usernames - Update IP ranges: Adjust
$SourceIPRanges
to match your testing network topology - Customize URLs: Edit
$DestinationURLs
to reflect your application's actual endpoints - Adjust distributions: Modify the weights in
$StatusCodes
and$HttpMethods
for different traffic patterns