نصب خودکار درایورها با PowerShell در WDS - بخش نهم سری آموزش سرویس WDS
در نهمین قسمت از سری آموزشهای سرویس توزیع ویندوز، به یکی از پیچیدهترین چالشهای نصب انبوه میپردازیم: نصب خودکار درایورهای مناسب هر دستگاه. این مشکل به ویژه در شرکتهای مونتاژ که با مدلهای مختلف لپتاپ کار میکنند، بسیار جدی است.
چالش اصلی: تنوع سختافزاری
مشکل: هر برند و مدل لپتاپ درایورهای خاص خود را دارد:
- ASUS مدل X با درایورهای مخصوص
- HP مدل Y با درایورهای متفاوت
- Dell مدل Z با درایورهای خاص خود
روش سنتی (غیرعملی):
- نصب ویندوز
- شناسایی دستی مدل
- پیدا کردن درایورها
- نصب یکبهیک
مشکلات روش سنتی:
- بسیار زمانبر
- نیاز به تخصص
- احتمال اشتباه بالا
- غیرقابل مقیاسپذیری
راهحل: نصب خودکار با 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: مسیر اسکریپت روی سرور
قدم چهارم: ساختار پوشههای سرور
مسیرهای استاندارد:
├── cmd\
│ └── AutoDriver.ps1 ← اسکریپت اصلی
├── Drivers\
│ ├── ASUS\
│ │ └── F15\
│ │ └── Intel\
│ │ ├── Windows10\
│ │ │ └── *.inf
│ │ └── Windows11\
│ │ └── *.inf
│ ├── Dell\
│ ├── HP\
│ └── Lenovo\
└── Logs\
└── DriverInstallLogs\ ← لاگهای نصبمسیرها:
- اسکریپت:
- درایورها:
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