نصب خودکار درایورها با PowerShell در WDS - بخش نهم سری آموزش سرویس WDS

در نهمین قسمت از سری آموزش‌های سرویس توزیع ویندوز، به یکی از پیچیده‌ترین چالش‌های نصب انبوه می‌پردازیم: نصب خودکار درایورهای مناسب هر دستگاه. این مشکل به ویژه در شرکت‌های مونتاژ که با مدل‌های مختلف لپ‌تاپ کار می‌کنند، بسیار جدی است.



چالش اصلی: تنوع سخت‌افزاری

مشکل: هر برند و مدل لپ‌تاپ درایورهای خاص خود را دارد:

  • ASUS مدل X با درایورهای مخصوص
  • HP مدل Y با درایورهای متفاوت
  • Dell مدل Z با درایورهای خاص خود

روش سنتی (غیرعملی):

  1. نصب ویندوز
  2. شناسایی دستی مدل
  3. پیدا کردن درایورها
  4. نصب یک‌به‌یک

مشکلات روش سنتی:

  • بسیار زمان‌بر
  • نیاز به تخصص
  • احتمال اشتباه بالا
  • غیرقابل مقیاس‌پذیری

راه‌حل: نصب خودکار با PowerShell در محیط Audit

قدم اول: چرا از محیط Audit استفاده می‌کنیم؟

Audit Mode چیست؟

Audit Mode حالتی در ویندوز که قبل از ایجاد حساب کاربری نهایی، با دسترسی Administrator اجرا می‌شود

مزایای Audit Mode: ✅ دسترسی کامل برای نصب درایورها 🔒 هیچ حساب کاربری یا داده‌ای هنوز ساخته نشده 🧹 بعد از اتمام، سیستم تمیز و آماده تحویل به کاربر نهایی

تفاوت با OOBE:

  • OOBE: محیط نهایی برای کاربر
  • Audit: محیط تکنیسین برای سفارشی‌سازی

قدم دوم: فعال‌سازی Audit Mode در Answer File

در فایل unattend.xml:

xml

<settings pass="oobeSystem">
    <component name="Microsoft-Windows-Deployment" 
               processorArchitecture="amd64" 
               publicKeyToken="31bf3856ad364e35" 
               language="neutral" 
               versionScope="nonSxS">
        <Reseal>
            <Mode>Audit</Mode>
        </Reseal>
    </component>
</settings>

نتیجه: بعد از نصب ویندوز، به جای صفحه خوش‌آمدگویی (OOBE)، مستقیماً وارد Audit Mode می‌شوید.

قدم سوم: اجرای خودکار اسکریپت

تنظیمات FirstLogonCommands:

xml

<settings pass="oobesystem">
    <component name="Microsoft-Windows-Shell-Setup" 
               processorArchitecture="amd64" 
               publicKeyToken="31bf3856ad364e35" 
               language="neutral" 
               versionScope="nonSxS">
        <LogonCommands>
            <AsynchronousCommand wcm:action="add">
<Order>1</Order> <Description>Auto Driver Install</Description> <CommandLine>cmd.exe /c net use Z: \\192.168.2.1\reminst Test123 /user:Administrator && powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -NoExit -File "Z:\cmd\AutoDriver.ps1"</</CommandLine> </AsynchronousCommand> </LogonCommands> </component> </settings>

پارامترهای مهم:

  • -ExecutionPolicy Bypass: اجازه اجرا بدون محدودیت
  • -NoExit: پنجره باز بماند تا خروجی را ببینیم
  • -File: مسیر اسکریپت روی سرور

قدم چهارم: ساختار پوشه‌های سرور

مسیرهای استاندارد:

 
path: \\192.168.2.1\reminst
├── cmd\
│   └── AutoDriver.ps1        ← اسکریپت اصلی
├── Drivers\
│   ├── ASUS\
│   │   └── F15\
│   │       └── Intel\
│   │           ├── Windows10\
│   │           │   └── *.inf
│   │           └── Windows11\
│   │               └── *.inf
│   ├── Dell\
│   ├── HP\
│   └── Lenovo\
└── Logs\
    └── DriverInstallLogs\    ← لاگ‌های نصب

مسیرها:

  • اسکریپت:

path: \\192.168.2.1\reminst\cmd\AutoDriver.ps1

  • درایورها:
path: \\192.168.2.1\reminst\Drivers
  • لاگ‌ها:

path: \\192.168.2.1\reminst\Logs\DriverInstallLogs


قدم پنجم: دریافت مشخصات سیستم

سازنده دستگاه:

powershell

$manufacturer = (Get-WmiObject -Class Win32_ComputerSystem).Manufacturer
Write-Host "Manufacturer: $manufacturer"

خروجی ممکن:

Manufacturer: ASUSTeK COMPUTER INC.

مشکل: نام‌های طولانی و نامرتب

مدل دستگاه:

powershell

$model = (Get-WmiObject -Class Win32_ComputerSystem).Model
Write-Host "Model: $model"

نوع CPU:

powershell

$cpuFolder = if ((Get-WmiObject -Class Win32_Processor).Name -match "Intel") { 
    "Intel" 
} else { 
    "AMD" 
}
Write-Host "CPU: $cpuFolder"

نسخه ویندوز:

powershell

$osFolder = if ((Get-WmiObject -Class Win32_OperatingSystem).Caption -match "Windows 11") { 
    "Windows11" 
} else { 
    "Windows10" 
}
Write-Host "OS: $osFolder"

قدم ششم: نرمال‌سازی نام سازنده

مشکل: نام‌های سازندگان ممکن است متفاوت باشند:

  • "ASUSTeK COMPUTER INC."
  • "ASUS"
  • "Dell Inc."

راه‌حل - کد نرمال‌سازی:

powershell

switch -Regex ($manufacturer) {
    "ASUSTeK"     { $manufacturer = "ASUS" }
    "ASUS"        { $manufacturer = "ASUS" }
    "Dell"        { $manufacturer = "Dell" }
    "Hewlett|HP"  { $manufacturer = "HP" }
    "Lenovo"      { $manufacturer = "Lenovo" }
    "Acer"        { $manufacturer = "Acer" }
    "MSI"         { $manufacturer = "MSI" }
    "Gigabyte"    { $manufacturer = "Gigabyte" }
    "VMware"      { $manufacturer = "VMware" }
    "VirtualBox"  { $manufacturer = "VirtualBox" }
    default       { $manufacturer = $manufacturer.Trim() }
}

نتیجه: نام پوشه‌ها ساده و یکپارچه می‌شوند.

قدم هفتم: نصب درایورها با pnputil

pnputil: ابزار داخلی ویندوز برای مدیریت درایورها

فرآیند نصب:

1. شمارش فایل‌های INF:

powershell

$infFiles = Get-ChildItem -Path $driverPath -Recurse -Include *.inf
$total = $infFiles.Count
$success = 0
$failed = 0

Write-Host "Found $total driver packages." -ForegroundColor Yellow
Add-Content -Path $logFile -Value "Found $total driver packages."

2. نصب تک‌به‌تک با لاگ:

powershell

foreach ($inf in $infFiles) {
    Write-Host "Installing: $($inf.FullName)" -ForegroundColor Cyan
    Add-Content -Path $logFile -Value "`nInstalling: $($inf.FullName)"
    
    try {
        $output = & pnputil /add-driver "$($inf.FullName)" /install
        Add-Content -Path $logFile -Value $output
        
        if ($output -match "successfully" -or $output -match "oem\d+\.inf") {
            $success++
        } else {
            $failed++
        }
    } catch {
        $failed++
        $errMsg = "Error installing: $($inf.FullName) - $($_.Exception.Message)"
        Write-Host $errMsg -ForegroundColor Red
        Add-Content -Path $logFile -Value $errMsg
    }
}

پارامترهای pnputil:

  • /add-driver: افزودن درایور به Driver Store
  • /install: نصب خودکار درایور

قدم هشتم: لاگ‌گیری با UUID

UUID چیست؟

Universally Unique Identifier شناسه یکتای جهانی برای هر سیستم

دریافت UUID:powershell

$uuid = (Get-WmiObject -Class Win32_ComputerSystemProduct).UUID
$logFile = Join-Path $serverLogDir ("DriverLog_" + $uuid + ".log")

مزایا:

  • هر سیستم لاگ جداگانه دارد
  • قابل پیگیری
  • مفید برای سیستم‌های متعدد

قدم نهم: گزارش نهایی

خلاصه نصب:

powershell

Write-Host ""
Write-Host "===== SUMMARY =====" -ForegroundColor Green
Write-Host "Total: $total"
Write-Host "Success: $success"
Write-Host "Failed: $failed"

Add-Content -Path $logFile -Value "`n===== SUMMARY ====="
Add-Content -Path $logFile -Value "Total: $total"
Add-Content -Path $logFile -Value "Success: $success"
Add-Content -Path $logFile -Value "Failed: $failed"

if ($failed -eq 0) {
    Write-Host "`n✅ All drivers installed successfully!" -ForegroundColor Green
} else {
    Write-Host "`n⚠ Some drivers failed. Check log file: $logFile" -ForegroundColor Yellow
}

Write-Host "`nPress any key to exit..."
pause

فایل کامل AutoDriver.ps1

اسکریپت نهایی:

powershell

# =============================
# AutoDriver.ps1 (Final Version)
# =============================

$ErrorActionPreference = "Stop"
trap {
    Write-Host ""
    Write-Host "❌ ERROR:" -ForegroundColor Red
    Write-Host $_.Exception.Message -ForegroundColor Red
    Write-Host ""
    Write-Host "Press any key to exit..."
    [void][System.Console]::ReadKey($true)
    exit 1
}

# مسیر لاگ‌ها
$serverLogDir = "\\192.168.2.1\reminst\Logs\DriverInstallLogs"
if (!(Test-Path $serverLogDir)) { 
    New-Item -Path $serverLogDir -ItemType Directory -Force | Out-Null 
}

# UUID یکتا
$uuid = (Get-WmiObject -Class Win32_ComputerSystemProduct).UUID
$logFile = Join-Path $serverLogDir ("DriverLog_" + $uuid + ".log")

# ثبت شروع
Add-Content -Path $logFile -Value "===== AUTO DRIVER INSTALL START ====="
Add-Content -Path $logFile -Value ("Date: " + (Get-Date).ToString("yyyy-MM-dd HH:mm:ss"))

# 1️⃣ اطلاعات سیستم
$manufacturer = (Get-WmiObject -Class Win32_ComputerSystem).Manufacturer
$model        = (Get-WmiObject -Class Win32_ComputerSystem).Model
$cpuName      = (Get-WmiObject -Class Win32_Processor).Name
$osCaption    = (Get-WmiObject -Class Win32_OperatingSystem).Caption

# نرمال‌سازی سازنده
switch -Regex ($manufacturer) {
    "ASUSTeK"     { $manufacturer = "ASUS" }
    "ASUS"        { $manufacturer = "ASUS" }
    "Dell"        { $manufacturer = "Dell" }
    "Hewlett|HP"  { $manufacturer = "HP" }
    "Lenovo"      { $manufacturer = "Lenovo" }
    "Acer"        { $manufacturer = "Acer" }
    "MSI"         { $manufacturer = "MSI" }
    "Gigabyte"    { $manufacturer = "Gigabyte" }
    "VMware"      { $manufacturer = "VMware" }
    "VirtualBox"  { $manufacturer = "VirtualBox" }
    default       { $manufacturer = $manufacturer.Trim() }
}

# CPU
if ($cpuName -match "Intel") { $cpuFolder = "Intel" }
elseif ($cpuName -match "AMD") { $cpuFolder = "AMD" }
else { $cpuFolder = "Unknown" }

# سیستم عامل
if ($osCaption -match "Windows 11") { $osFolder = "Windows11" }
elseif ($osCaption -match "Windows 10") { $osFolder = "Windows10" }
else { $osFolder = "Unknown" }

# مسیر درایورها
$driverServer = "\\192.168.2.1\reminst\Drivers"
$driverPath   = "$driverServer\$manufacturer\$model\$cpuFolder\$osFolder"

Write-Host "Manufacturer: $manufacturer"
Write-Host "Model: $model"
Write-Host "CPU Type: $cpuFolder"
Write-Host "Operating System: $osFolder"
Write-Host "UUID: $uuid"
Write-Host ""
Write-Host "Driver Path: $driverPath" -ForegroundColor Cyan

Add-Content -Path $logFile -Value "Manufacturer: $manufacturer"
Add-Content -Path $logFile -Value "Model: $model"
Add-Content -Path $logFile -Value "CPU: $cpuFolder"
Add-Content -Path $logFile -Value "OS: $osFolder"
Add-Content -Path $logFile -Value "UUID: $uuid"
Add-Content -Path $logFile -Value "Driver Path: $driverPath"

if (!(Test-Path $driverPath)) {
    Write-Host "❌ Driver path not found: $driverPath" -ForegroundColor Red
    Add-Content -Path $logFile -Value "Driver path not found: $driverPath"
    throw "Driver folder not accessible."
}

# 2️⃣ جستجو و شمارش درایورها
$infFiles = Get-ChildItem -Path $driverPath -Recurse -Include *.inf
$total = $infFiles.Count
if ($total -eq 0) {
    Write-Host "❌ No INF files found in: $driverPath" -ForegroundColor Red
    Add-Content -Path $logFile -Value "No INF files found."
    throw "No driver files found."
}

Write-Host ""
Write-Host "Found $total driver files (.inf)" -ForegroundColor Yellow
Add-Content -Path $logFile -Value "Found $total INF files."

# 3️⃣ نصب درایورها با لاگ
$success = 0
$failed = 0

foreach ($inf in $infFiles) {
    Write-Host "Installing: $($inf.FullName)" -ForegroundColor Cyan
    Add-Content -Path $logFile -Value "`nInstalling: $($inf.FullName)"
    try {
        $output = & pnputil /add-driver "$($inf.FullName)" /install
        Add-Content -Path $logFile -Value $output

        if ($output -match "Treiber wurde hinzugefügt" -or 
            $output -match "driver package added successfully") {
            $success++
        } else {
            $failed++
        }
    } catch {
        $failed++
        $errMsg = "Error installing: $($inf.FullName) - $($_.Exception.Message)"
        Write-Host $errMsg -ForegroundColor Red
        Add-Content -Path $logFile -Value $errMsg
    }
}

# 4️⃣ گزارش نهایی
Write-Host ""
Write-Host "===== SUMMARY =====" -ForegroundColor Green
Write-Host "Total Drivers Found: $total"
Write-Host "Successfully Installed: $success"
Write-Host "Failed: $failed"

Add-Content -Path $logFile -Value "`n===== SUMMARY ====="
Add-Content -Path $logFile -Value "Total: $total"
Add-Content -Path $logFile -Value "Success: $success"
Add-Content -Path $logFile -Value "Failed: $failed"

# بررسی دستگاه‌های بدون درایور
$missing = Get-WmiObject Win32_PnPEntity | 
           Where-Object { $_.ConfigManagerErrorCode -eq 28 }
if ($missing) {
    Write-Host ""
    Write-Host "⚠ Devices still missing drivers:" -ForegroundColor Red
    $missing | Select-Object -ExpandProperty Caption | 
               Tee-Object -FilePath $logFile -Append
} else {
    Write-Host ""
    Write-Host "✅ All devices are working properly!" -ForegroundColor Green
    Add-Content -Path $logFile -Value "`nAll devices OK."
}

# پایان
Write-Host ""
Write-Host "===== INSTALL FINISHED =====" -ForegroundColor Cyan
Write-Host ("Logs saved to: $logFile") -ForegroundColor DarkGray
Write-Host ""
Write-Host "Press any key to exit..."
[void][System.Console]::ReadKey($true)

نکات عملی و بهترین شیوه‌ها

سازماندهی درایورها:

  • دانلود از سایت رسمی سازندگان
  • نسخه‌بندی درایورها
  • تست قبل از استقرار
  • حذف درایورهای قدیمی

مدیریت لاگ‌ها:

  • بررسی منظم پوشه Logs
  • حذف لاگ‌های قدیمی
  • تحلیل خطاها

عیب‌یابی:

  • بررسی لاگ با UUID
  • چک کردن دسترسی شبکه
  • تست با مدل‌های مختلف

Comments

سوالات و نظرات خود درباره این آموزش را بنویسید. لطفاً سوالات فنی را دقیق مطرح کنید تا بتوانم پاسخ مناسبی ارائه دهم. اگر کد یا خطایی دارید، آن را با ذکر جزئیات بنویسید.

بایگانی وبلاگ

فرم تماس

Send