Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Generating Realistic IIS Log Files for Testing


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:

  1. Modify usernames: Change the $BearUsernames array to use your own fake usernames
  2. Update IP ranges: Adjust $SourceIPRanges to match your testing network topology
  3. Customize URLs: Edit $DestinationURLs to reflect your application's actual endpoints
  4. Adjust distributions: Modify the weights in $StatusCodes and $HttpMethods for different traffic patterns
Previous Post Next Post

نموذج الاتصال