WindowsDefender/Invoke-DefenderScan.ps1
|
function Invoke-DefenderScan { <# .SYNOPSIS Initiates Windows Defender scans with various options .DESCRIPTION Starts Windows Defender antivirus scans including Quick Scan, Full Scan, Custom Scan, and Offline Scan with progress tracking and result reporting .PARAMETER ScanType Type of scan: Quick, Full, Custom, or Offline .PARAMETER ScanPath Path to scan (required for Custom scan) .PARAMETER UpdateSignatures Update virus definitions before scanning .PARAMETER Priority Scan priority: Low or Normal .EXAMPLE Invoke-DefenderScan -ScanType Quick Invoke-DefenderScan -ScanType Full -UpdateSignatures Invoke-DefenderScan -ScanType Custom -ScanPath "C:\Users" .OUTPUTS PSCustomObject with scan results #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [ValidateSet('Quick', 'Full', 'Custom', 'Offline')] [string]$ScanType, [Parameter()] [string]$ScanPath, [Parameter()] [switch]$UpdateSignatures, [Parameter()] [ValidateSet('Low', 'Normal')] [string]$Priority = 'Normal' ) begin { Write-Host "=== WINDOWS DEFENDER SCAN ===" -ForegroundColor Cyan Write-Host "Scan Type: $ScanType" -ForegroundColor Yellow Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow # Check for admin privileges $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { throw "This function requires Administrator privileges!" } # Validate custom scan path if ($ScanType -eq 'Custom' -and -not $ScanPath) { throw "ScanPath is required for Custom scan type!" } if ($ScanType -eq 'Custom' -and -not (Test-Path $ScanPath)) { throw "ScanPath does not exist: $ScanPath" } $results = [PSCustomObject]@{ ScanType = $ScanType StartTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss" EndTime = $null Duration = $null Status = 'Running' ThreatsDetected = 0 FilesScanned = 0 ThreatDetails = @() SignatureUpdate = @{ Updated = $false Version = $null Error = $null } Errors = @() } } process { try { # Update signatures if requested if ($UpdateSignatures) { Write-Host "`nUpdating virus definitions..." -ForegroundColor Cyan if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Update Defender signatures")) { try { Update-MpSignature -ErrorAction Stop $mpStatus = Get-MpComputerStatus $results.SignatureUpdate.Updated = $true $results.SignatureUpdate.Version = $mpStatus.AntivirusSignatureVersion Write-Host "Signatures updated to version: $($mpStatus.AntivirusSignatureVersion)" -ForegroundColor Green } catch { $results.SignatureUpdate.Error = $_.Exception.Message Write-Warning "Failed to update signatures: $_" } } } # Start the scan Write-Host "`nStarting $ScanType scan..." -ForegroundColor Cyan if (-not $PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Start $ScanType scan")) { $results.Status = 'WhatIf' } else { switch ($ScanType) { 'Quick' { Write-Host "This may take a few minutes..." -ForegroundColor Yellow try { if ($Priority -eq 'Low') { Start-MpScan -ScanType QuickScan -AsJob | Out-Null Write-Host "Quick scan started as background job" -ForegroundColor Green $results.Status = 'Started' } else { Start-MpScan -ScanType QuickScan Write-Host "Quick scan completed" -ForegroundColor Green $results.Status = 'Completed' } } catch { $results.Status = 'Failed' $results.Errors += $_.Exception.Message throw } } 'Full' { Write-Host "This may take 30+ minutes depending on system size..." -ForegroundColor Yellow Write-Host "Progress updates will appear in Windows Security app" -ForegroundColor Yellow try { if ($Priority -eq 'Low') { Start-MpScan -ScanType FullScan -AsJob | Out-Null Write-Host "Full scan started as background job" -ForegroundColor Green Write-Host "Use Get-MpThreat to check results later" -ForegroundColor Yellow $results.Status = 'Started' } else { $job = Start-MpScan -ScanType FullScan -AsJob Write-Host "Full scan in progress (background job)..." -ForegroundColor Yellow Write-Host "Monitoring scan progress..." -ForegroundColor Cyan # Monitor progress $startTime = Get-Date while ($job.State -eq 'Running') { $elapsed = ((Get-Date) - $startTime).ToString("hh\:mm\:ss") Write-Host "`rElapsed time: $elapsed" -NoNewline -ForegroundColor Yellow Start-Sleep -Seconds 10 } Write-Host "" if ($job.State -eq 'Completed') { Write-Host "Full scan completed" -ForegroundColor Green $results.Status = 'Completed' } else { Write-Warning "Scan job state: $($job.State)" $results.Status = $job.State } # Clean up the completed scan job so it does not linger in the session Remove-Job -Job $job -Force -ErrorAction SilentlyContinue } } catch { $results.Status = 'Failed' $results.Errors += $_.Exception.Message throw } } 'Custom' { Write-Host "Scanning path: $ScanPath" -ForegroundColor Yellow try { Start-MpScan -ScanType CustomScan -ScanPath $ScanPath Write-Host "Custom scan completed" -ForegroundColor Green $results.Status = 'Completed' } catch { $results.Status = 'Failed' $results.Errors += $_.Exception.Message throw } } 'Offline' { Write-Host "WARNING: Offline scan will restart your computer!" -ForegroundColor Red Write-Host "Your computer will boot into Windows Defender Offline environment" -ForegroundColor Yellow $confirmation = Read-Host "Do you want to continue? (yes/no)" if ($confirmation -eq 'yes') { try { Start-MpWDOScan Write-Host "Offline scan initiated - system will restart" -ForegroundColor Green $results.Status = 'Initiated' } catch { $results.Status = 'Failed' $results.Errors += $_.Exception.Message throw } } else { Write-Host "Offline scan cancelled by user" -ForegroundColor Yellow $results.Status = 'Cancelled' } } } } # Wait a moment for scan results to be processed if ($results.Status -eq 'Completed') { Start-Sleep -Seconds 2 } # Get scan results and threat information if ($results.Status -eq 'Completed') { Write-Host "`nCollecting scan results..." -ForegroundColor Cyan try { # Get updated Defender status $mpStatus = Get-MpComputerStatus # Get threat detections $threats = Get-MpThreat $results.ThreatsDetected = @($threats).Count foreach ($threat in $threats) { $results.ThreatDetails += [PSCustomObject]@{ ThreatName = $threat.ThreatName SeverityID = $threat.SeverityID Severity = switch ($threat.SeverityID) { 0 { 'Unknown' } 1 { 'Low' } 2 { 'Medium' } 4 { 'High' } 5 { 'Severe' } default { 'Unknown' } } CategoryID = $threat.CategoryID InitialDetectionTime = $threat.InitialDetectionTime IsActive = $threat.IsActive Resources = $threat.Resources } } # Try to get scan history switch ($ScanType) { 'Quick' { $results.EndTime = $mpStatus.QuickScanEndTime if ($mpStatus.QuickScanStartTime -and $mpStatus.QuickScanEndTime) { $results.Duration = ($mpStatus.QuickScanEndTime - $mpStatus.QuickScanStartTime).ToString() } } 'Full' { $results.EndTime = $mpStatus.FullScanEndTime if ($mpStatus.FullScanStartTime -and $mpStatus.FullScanEndTime) { $results.Duration = ($mpStatus.FullScanEndTime - $mpStatus.FullScanStartTime).ToString() } } } } catch { Write-Verbose "Could not retrieve detailed scan results: $_" } } } catch { Write-Error "Error during Defender scan: $_" $results.Status = 'Error' $results.Errors += $_.Exception.Message } } end { if ($results.Status -ne 'Initiated') { $results.EndTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss" } Write-Host "`n=== SCAN COMPLETE ===" -ForegroundColor Green Write-Host "`nScan Details:" -ForegroundColor Cyan Write-Host " Type: $($results.ScanType)" -ForegroundColor White Write-Host " Status: " -NoNewline Write-Host $results.Status -ForegroundColor $( switch ($results.Status) { 'Completed' { 'Green' } 'Running' { 'Yellow' } 'Started' { 'Yellow' } 'Failed' { 'Red' } 'Cancelled' { 'Yellow' } 'Initiated' { 'Green' } default { 'White' } } ) if ($results.Duration) { Write-Host " Duration: $($results.Duration)" -ForegroundColor White } if ($results.SignatureUpdate.Updated) { Write-Host "`nSignature Update:" -ForegroundColor Cyan Write-Host " Version: $($results.SignatureUpdate.Version)" -ForegroundColor White } Write-Host "`nThreats Detected: " -NoNewline if ($results.ThreatsDetected -gt 0) { Write-Host $results.ThreatsDetected -ForegroundColor Red Write-Host "`nThreat Details:" -ForegroundColor Red foreach ($threat in $results.ThreatDetails) { Write-Host " - $($threat.ThreatName)" -ForegroundColor Red Write-Host " Severity: $($threat.Severity)" -ForegroundColor Yellow Write-Host " Detected: $($threat.InitialDetectionTime)" -ForegroundColor White if ($threat.Resources) { Write-Host " Location: $($threat.Resources)" -ForegroundColor White } } } else { Write-Host "0" -ForegroundColor Green Write-Host " No threats detected!" -ForegroundColor Green } if ($results.Errors.Count -gt 0) { Write-Host "`nErrors:" -ForegroundColor Red $results.Errors | ForEach-Object { Write-Host " - $_" -ForegroundColor Red } } Write-Host "`nRecommendations:" -ForegroundColor Cyan Write-Host " 1. Run full scans weekly" -ForegroundColor White Write-Host " 2. Keep definitions updated automatically" -ForegroundColor White Write-Host " 3. Review detected threats in Windows Security" -ForegroundColor White Write-Host " 4. Schedule regular scans during off-peak hours" -ForegroundColor White if ($Priority -eq 'Low' -and $results.Status -eq 'Started') { Write-Host "`nNote: Background scan may still be running. Check Windows Security for status." -ForegroundColor Yellow } return $results } } |