mirror of
https://github.com/MAKS-IT-COM/uscheduler.git
synced 2026-02-14 06:37:18 +01:00
(feature): windows-update script example
This commit is contained in:
parent
728f657112
commit
986eea321e
@ -1,6 +1,6 @@
|
|||||||
@{
|
@{
|
||||||
RootModule = 'SchedulerTemplate.psm1'
|
RootModule = 'SchedulerTemplate.psm1'
|
||||||
ModuleVersion = '1.0.1'
|
ModuleVersion = '1.0.2'
|
||||||
GUID = 'a3b2c1d0-e4f5-6a7b-8c9d-0e1f2a3b4c5d'
|
GUID = 'a3b2c1d0-e4f5-6a7b-8c9d-0e1f2a3b4c5d'
|
||||||
Author = 'MaksIT'
|
Author = 'MaksIT'
|
||||||
CompanyName = 'MaksIT'
|
CompanyName = 'MaksIT'
|
||||||
@ -20,6 +20,7 @@
|
|||||||
'Test-ScheduledExecution',
|
'Test-ScheduledExecution',
|
||||||
'New-LockGuard',
|
'New-LockGuard',
|
||||||
'Remove-LockGuard',
|
'Remove-LockGuard',
|
||||||
|
'Send-EmailNotification',
|
||||||
'Invoke-ScheduledExecution'
|
'Invoke-ScheduledExecution'
|
||||||
)
|
)
|
||||||
CmdletsToExport = @()
|
CmdletsToExport = @()
|
||||||
@ -27,13 +28,19 @@
|
|||||||
AliasesToExport = @()
|
AliasesToExport = @()
|
||||||
PrivateData = @{
|
PrivateData = @{
|
||||||
PSData = @{
|
PSData = @{
|
||||||
Tags = @('Scheduler', 'Automation', 'Lock', 'Logging', 'Credentials')
|
Tags = @('Scheduler', 'Automation', 'Lock', 'Logging', 'Credentials', 'Email')
|
||||||
LicenseUri = ''
|
LicenseUri = ''
|
||||||
ProjectUri = 'https://github.com/MaksIT/uscheduler'
|
ProjectUri = 'https://github.com/MaksIT/uscheduler'
|
||||||
ReleaseNotes = @'
|
ReleaseNotes = @'
|
||||||
|
## 1.0.2 (2026-01-28)
|
||||||
|
- Added Send-EmailNotification function for SMTP email sending
|
||||||
|
- Supports SSL/TLS and credential-based authentication
|
||||||
|
|
||||||
## 1.0.1 (2026-01-26)
|
## 1.0.1 (2026-01-26)
|
||||||
- Improved UNC path validation (Test-UNCPath function)
|
- Improved UNC path validation (Test-UNCPath function)
|
||||||
- Enhanced credential management
|
- Enhanced credential management
|
||||||
|
|
||||||
|
## 1.0.0 (2026-01-24)
|
||||||
- Comprehensive logging with timestamp support
|
- Comprehensive logging with timestamp support
|
||||||
- Scheduled execution with lock files and interval control
|
- Scheduled execution with lock files and interval control
|
||||||
- Schedule validation (month, weekday, time)
|
- Schedule validation (month, weekday, time)
|
||||||
|
|||||||
@ -5,19 +5,20 @@
|
|||||||
Reusable PowerShell module for scheduled script execution with lock files,
|
Reusable PowerShell module for scheduled script execution with lock files,
|
||||||
interval control, and credential management.
|
interval control, and credential management.
|
||||||
.VERSION
|
.VERSION
|
||||||
1.0.1
|
1.0.2
|
||||||
.DATE
|
.DATE
|
||||||
2026-01-26
|
2026-01-28
|
||||||
.NOTES
|
.NOTES
|
||||||
- Provides Write-Log function with timestamp and level support
|
- Provides Write-Log function with timestamp and level support
|
||||||
- Provides Get-CredentialFromEnvVar for secure credential retrieval
|
- Provides Get-CredentialFromEnvVar for secure credential retrieval
|
||||||
- Provides Test-UNCPath for UNC path validation
|
- Provides Test-UNCPath for UNC path validation
|
||||||
|
- Provides Send-EmailNotification for SMTP email sending
|
||||||
- Provides Invoke-ScheduledExecution for scheduled task management
|
- Provides Invoke-ScheduledExecution for scheduled task management
|
||||||
#>
|
#>
|
||||||
|
|
||||||
# Module Version (exported for external scripts to check version)
|
# Module Version (exported for external scripts to check version)
|
||||||
$script:ModuleVersion = "1.0.1"
|
$script:ModuleVersion = "1.0.2"
|
||||||
$script:ModuleDate = "2026-01-26"
|
$script:ModuleDate = "2026-01-28"
|
||||||
|
|
||||||
# Module load confirmation
|
# Module load confirmation
|
||||||
Write-Verbose "SchedulerTemplate.psm1 v$ModuleVersion loaded ($ModuleDate)"
|
Write-Verbose "SchedulerTemplate.psm1 v$ModuleVersion loaded ($ModuleDate)"
|
||||||
@ -217,6 +218,69 @@ function Remove-LockGuard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Email Notification
|
||||||
|
# ======================================================================
|
||||||
|
function Send-EmailNotification {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[hashtable]$EmailSettings,
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Subject,
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Body,
|
||||||
|
[switch]$Automated
|
||||||
|
)
|
||||||
|
|
||||||
|
# Validate required settings
|
||||||
|
$required = @('smtpServer', 'smtpPort', 'from', 'to')
|
||||||
|
foreach ($key in $required) {
|
||||||
|
if (-not $EmailSettings.$key) {
|
||||||
|
Write-Log "Email setting '$key' is required but missing" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mailParams = @{
|
||||||
|
SmtpServer = $EmailSettings.smtpServer
|
||||||
|
Port = $EmailSettings.smtpPort
|
||||||
|
From = $EmailSettings.from
|
||||||
|
To = $EmailSettings.to
|
||||||
|
Subject = $Subject
|
||||||
|
Body = $Body
|
||||||
|
BodyAsHtml = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add SSL if configured
|
||||||
|
if ($EmailSettings.useSSL) {
|
||||||
|
$mailParams['UseSsl'] = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add credentials if configured
|
||||||
|
if ($EmailSettings.credentialEnvVar) {
|
||||||
|
$creds = Get-CredentialFromEnvVar -EnvVarName $EmailSettings.credentialEnvVar -Automated:$Automated
|
||||||
|
if ($creds) {
|
||||||
|
$securePassword = ConvertTo-SecureString $creds.Password -AsPlainText -Force
|
||||||
|
$credential = New-Object System.Management.Automation.PSCredential($creds.Username, $securePassword)
|
||||||
|
$mailParams['Credential'] = $credential
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "Failed to retrieve email credentials from '$($EmailSettings.credentialEnvVar)'" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Send-MailMessage @mailParams -ErrorAction Stop
|
||||||
|
Write-Log "Email sent successfully to $($EmailSettings.to -join ', ')" -Level Success -Automated:$Automated
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Failed to send email: $_" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
# Main unified executor (callback-based)
|
# Main unified executor (callback-based)
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
|
|||||||
513
examples/Windows-Update/README.md
Normal file
513
examples/Windows-Update/README.md
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
# Windows Update Script
|
||||||
|
|
||||||
|
**Version:** 1.0.0
|
||||||
|
**Last Updated:** 2026-01-28
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Production-ready Windows Update automation solution using PowerShell and PSWindowsUpdate module. Supports scheduled updates, category filtering, exclusions, pre/post checks, and auto-reboot with maintenance window control.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ **PSWindowsUpdate Integration** - Uses reliable PSWindowsUpdate module
|
||||||
|
- ✅ **Multiple Categories** - Critical, Security, Definition, and more
|
||||||
|
- ✅ **Smart Exclusions** - Exclude by KB number or title patterns
|
||||||
|
- ✅ **Pre-flight Checks** - Disk space, pending reboot, service status
|
||||||
|
- ✅ **Reboot Control** - Optional automatic reboot with delay
|
||||||
|
- ✅ **Update Reports** - Generate post-installation reports with optional email notifications
|
||||||
|
- ✅ **Dry Run Mode** - Test update detection without installation
|
||||||
|
- ✅ **Detailed Logging** - Comprehensive logging with timestamps and severity levels
|
||||||
|
- ✅ **Lock Files** - Prevents concurrent execution
|
||||||
|
- ✅ **Flexible Scheduling** - Schedule updates by month, weekday, and time
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### System Requirements
|
||||||
|
- Windows 10/11 or Windows Server 2016+
|
||||||
|
- PowerShell 5.1 or later
|
||||||
|
- Administrator privileges
|
||||||
|
- Internet access for Windows Update
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- `PSWindowsUpdate` module (auto-installed if missing)
|
||||||
|
- `SchedulerTemplate.psm1` module (located in parent directory)
|
||||||
|
- `scriptsettings.json` configuration file
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Windows-Update/
|
||||||
|
├── windows-update.bat # Batch launcher with admin check
|
||||||
|
├── windows-update.ps1 # Main PowerShell script
|
||||||
|
├── scriptsettings.json # Configuration file
|
||||||
|
├── README.md # This file
|
||||||
|
└── Utilities/
|
||||||
|
└── windows-update-policy.ps1 # Configure Windows Update behavior
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. **Copy Files**
|
||||||
|
```powershell
|
||||||
|
# Copy the entire Windows-Update folder to your desired location
|
||||||
|
# Ensure SchedulerTemplate.psm1 is in the parent directory
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure Settings**
|
||||||
|
|
||||||
|
Edit `scriptsettings.json` with your preferences:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schedule": {
|
||||||
|
"runWeekday": ["Wednesday"],
|
||||||
|
"runTime": ["02:00"]
|
||||||
|
},
|
||||||
|
"updateCategories": [
|
||||||
|
"Critical Updates",
|
||||||
|
"Security Updates"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Test Manual Execution**
|
||||||
|
```powershell
|
||||||
|
# Run as Administrator
|
||||||
|
.\windows-update.bat
|
||||||
|
# or
|
||||||
|
.\windows-update.ps1
|
||||||
|
|
||||||
|
# Test with dry run first (set dryRun: true in scriptsettings.json)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
### Schedule Settings
|
||||||
|
|
||||||
|
| Property | Type | Description | Example |
|
||||||
|
|----------|------|-------------|---------|
|
||||||
|
| `runMonth` | array | Month names to run. Empty = every month | `["January", "July"]` or `[]` |
|
||||||
|
| `runWeekday` | array | Weekday names to run. Empty = every day | `["Wednesday"]` |
|
||||||
|
| `runTime` | array | UTC times to run (HH:mm format) | `["02:00", "14:00"]` |
|
||||||
|
| `minIntervalMinutes` | number | Minimum minutes between runs | `60` |
|
||||||
|
|
||||||
|
### Update Categories
|
||||||
|
|
||||||
|
Available categories to install:
|
||||||
|
|
||||||
|
| Category | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `Critical Updates` | Critical security and stability updates |
|
||||||
|
| `Security Updates` | Security-focused updates |
|
||||||
|
| `Definition Updates` | Antivirus and malware definition updates |
|
||||||
|
| `Update Rollups` | Cumulative update packages |
|
||||||
|
| `Feature Packs` | New feature additions |
|
||||||
|
| `Service Packs` | Major cumulative updates |
|
||||||
|
| `Tools` | System tools and utilities |
|
||||||
|
| `Drivers` | Hardware driver updates |
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"updateCategories": [
|
||||||
|
"Critical Updates",
|
||||||
|
"Security Updates",
|
||||||
|
"Definition Updates"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exclusions
|
||||||
|
|
||||||
|
| Property | Type | Description | Example |
|
||||||
|
|----------|------|-------------|---------|
|
||||||
|
| `kbNumbers` | array | KB numbers to exclude | `["KB5034441", "KB5034123"]` |
|
||||||
|
| `titlePatterns` | array | Wildcard patterns to exclude | `["*Preview*", "*Beta*"]` |
|
||||||
|
|
||||||
|
**Pattern Syntax:**
|
||||||
|
- Use wildcards with `-like` operator (e.g., `*Preview*`, `*Optional*`)
|
||||||
|
- KB numbers should include the `KB` prefix
|
||||||
|
|
||||||
|
> **Note:** Filter matching uses PowerShell's `-like` operator. Test with `dryRun: true` first to verify expected behavior.
|
||||||
|
|
||||||
|
### Pre-Checks
|
||||||
|
|
||||||
|
| Property | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `minDiskSpaceGB` | number | `10` | Minimum free disk space in GB |
|
||||||
|
| `checkPendingReboot` | bool | `true` | Check for pending reboot before updates |
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Property | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `rebootBehavior` | string | `"manual"` | Reboot behavior: `"never"`, `"manual"`, or `"auto"` |
|
||||||
|
| `rebootDelayMinutes` | number | `5` | Minutes to wait before auto-reboot (when `rebootBehavior` is `"auto"`) |
|
||||||
|
| `dryRun` | bool | `false` | Simulate without installing updates |
|
||||||
|
|
||||||
|
**Reboot Behavior Values:**
|
||||||
|
| Value | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `"never"` | Abort if system has pending reboot or updates require reboot |
|
||||||
|
| `"manual"` | Continue with updates, log that reboot is needed, user reboots later |
|
||||||
|
| `"auto"` | Automatically reboot after configured delay when updates require it |
|
||||||
|
|
||||||
|
### Reporting
|
||||||
|
|
||||||
|
| Property | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `generateReport` | bool | `true` | Generate text report after updates |
|
||||||
|
| `emailNotification` | bool | `false` | Send email notification after updates |
|
||||||
|
| `emailSettings` | object | - | SMTP configuration for email notifications |
|
||||||
|
|
||||||
|
**Email Settings:**
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `smtpServer` | string | SMTP server hostname |
|
||||||
|
| `smtpPort` | number | SMTP port (587 for TLS, 465 for SSL, 25 for plain) |
|
||||||
|
| `from` | string | Sender email address |
|
||||||
|
| `to` | array | Recipient email addresses |
|
||||||
|
| `useSSL` | bool | Use SSL/TLS for connection |
|
||||||
|
| `credentialEnvVar` | string | Machine-level env var with Base64(`username:password`). Empty for no auth. |
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"reporting": {
|
||||||
|
"generateReport": true,
|
||||||
|
"emailNotification": true,
|
||||||
|
"emailSettings": {
|
||||||
|
"smtpServer": "smtp.office365.com",
|
||||||
|
"smtpPort": 587,
|
||||||
|
"from": "updates@example.com",
|
||||||
|
"to": ["admin@example.com"],
|
||||||
|
"useSSL": true,
|
||||||
|
"credentialEnvVar": "SMTP_CREDENTIALS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Manual Execution
|
||||||
|
|
||||||
|
**Using Batch File (Recommended):**
|
||||||
|
```batch
|
||||||
|
REM Right-click and select "Run as administrator"
|
||||||
|
windows-update.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using PowerShell:**
|
||||||
|
```powershell
|
||||||
|
# Run as Administrator
|
||||||
|
.\windows-update.ps1
|
||||||
|
|
||||||
|
# With verbose output
|
||||||
|
.\windows-update.ps1 -Verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automated Execution
|
||||||
|
|
||||||
|
The script supports automated execution through the UScheduler service:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Called by scheduler with -Automated flag
|
||||||
|
.\windows-update.ps1 -Automated -CurrentDateTimeUtc "2026-01-28 02:00:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
When `-Automated` is specified:
|
||||||
|
- Schedule is enforced (month, weekday, time)
|
||||||
|
- Lock files prevent concurrent execution
|
||||||
|
- Interval checking prevents duplicate runs
|
||||||
|
- Logs are formatted for service logger (no timestamps)
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Update Process Flow
|
||||||
|
|
||||||
|
1. **Initialization**
|
||||||
|
- Load SchedulerTemplate.psm1 module
|
||||||
|
- Load and validate scriptsettings.json
|
||||||
|
- Check/install PSWindowsUpdate module
|
||||||
|
|
||||||
|
2. **Pre-flight Checks**
|
||||||
|
- Verify disk space availability
|
||||||
|
- Check for pending reboot
|
||||||
|
- Verify Windows Update service is running
|
||||||
|
|
||||||
|
3. **Scan Phase**
|
||||||
|
- Query available updates from Windows Update
|
||||||
|
- Filter by configured categories
|
||||||
|
- Apply exclusions (KB numbers, title patterns)
|
||||||
|
|
||||||
|
4. **Installation Phase**
|
||||||
|
- Display list of updates to install
|
||||||
|
- Execute update installation
|
||||||
|
- Track installation results (success/failure)
|
||||||
|
- Monitor reboot requirements
|
||||||
|
|
||||||
|
5. **Post-Update Actions**
|
||||||
|
- Handle reboot if required and configured
|
||||||
|
- Generate update report
|
||||||
|
|
||||||
|
6. **Summary**
|
||||||
|
- Display installation statistics
|
||||||
|
- Report errors and warnings
|
||||||
|
|
||||||
|
### Reboot Behavior
|
||||||
|
|
||||||
|
| rebootBehavior | Behavior |
|
||||||
|
|----------------|----------|
|
||||||
|
| `"never"` | Abort if reboot pending/required |
|
||||||
|
| `"manual"` | Continue, log that reboot is needed |
|
||||||
|
| `"auto"` | Automatically reboot after delay |
|
||||||
|
|
||||||
|
### Progress Output
|
||||||
|
|
||||||
|
```
|
||||||
|
[INFO] ==========================================
|
||||||
|
[INFO] Windows Update Process Started
|
||||||
|
[INFO] Script Version: 1.0.0 (2026-01-28)
|
||||||
|
[INFO] ==========================================
|
||||||
|
[SUCCESS] PSWindowsUpdate module loaded
|
||||||
|
[INFO] Running pre-update checks...
|
||||||
|
[INFO] Free space on C: : 125.50 GB
|
||||||
|
[SUCCESS] Pre-update checks passed
|
||||||
|
[INFO] Scanning for available updates...
|
||||||
|
[INFO] Found 5 update(s) to install
|
||||||
|
[INFO] ========================================
|
||||||
|
[INFO] [KB5034441] 2024-01 Cumulative Update (15234.50 KB)
|
||||||
|
[INFO] [KB5034123] Security Intelligence Update (8765.25 KB)
|
||||||
|
[INFO] ========================================
|
||||||
|
[INFO] Installing updates...
|
||||||
|
[SUCCESS] Installed: 2024-01 Cumulative Update for Windows 11
|
||||||
|
[SUCCESS] Installed: Security Intelligence Update
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
========================================
|
||||||
|
UPDATE SUMMARY
|
||||||
|
========================================
|
||||||
|
Start Time : 2026-01-28 02:00:00
|
||||||
|
End Time : 2026-01-28 02:15:32
|
||||||
|
Duration : 0h 15m 32s
|
||||||
|
Status : SUCCESS
|
||||||
|
|
||||||
|
Installed : 5
|
||||||
|
Failed : 0
|
||||||
|
Skipped : 0
|
||||||
|
Reboot Needed : True
|
||||||
|
========================================
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
### Log Levels
|
||||||
|
|
||||||
|
| Level | Description | Color (Manual) |
|
||||||
|
|-------|-------------|----------------|
|
||||||
|
| `Info` | Informational messages | White |
|
||||||
|
| `Success` | Successful operations | Green |
|
||||||
|
| `Warning` | Non-critical issues | Yellow |
|
||||||
|
| `Error` | Critical errors | Red |
|
||||||
|
|
||||||
|
### Log Format
|
||||||
|
|
||||||
|
**Manual Execution:**
|
||||||
|
```
|
||||||
|
[2026-01-28 02:00:00] [Info] Windows Update Process Started
|
||||||
|
[2026-01-28 02:15:32] [Success] Updates completed successfully
|
||||||
|
```
|
||||||
|
|
||||||
|
**Automated Execution:**
|
||||||
|
```
|
||||||
|
[Info] Windows Update Process Started
|
||||||
|
[Success] Updates completed successfully
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exit Codes
|
||||||
|
|
||||||
|
| Code | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `0` | Success (no errors) |
|
||||||
|
| `1` | Error occurred (config, checks, installation errors) |
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**1. PSWindowsUpdate Module Not Found**
|
||||||
|
```
|
||||||
|
Error: Failed to import PSWindowsUpdate module
|
||||||
|
```
|
||||||
|
**Solution:**
|
||||||
|
```powershell
|
||||||
|
# Install manually
|
||||||
|
Install-Module -Name PSWindowsUpdate -Force -Scope AllUsers
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Insufficient Permissions**
|
||||||
|
```
|
||||||
|
Error: This script must be run as Administrator
|
||||||
|
```
|
||||||
|
**Solution:** Right-click the batch file and select "Run as administrator"
|
||||||
|
|
||||||
|
**3. Insufficient Disk Space**
|
||||||
|
```
|
||||||
|
Error: Insufficient disk space. Required: 10 GB, Available: 8.5 GB
|
||||||
|
```
|
||||||
|
**Solution:**
|
||||||
|
- Free up disk space
|
||||||
|
- Reduce `minDiskSpaceGB` in configuration (not recommended)
|
||||||
|
|
||||||
|
**4. Windows Update Service Not Running**
|
||||||
|
```
|
||||||
|
Error: Failed to start Windows Update service
|
||||||
|
```
|
||||||
|
**Solution:**
|
||||||
|
```powershell
|
||||||
|
# Start service manually
|
||||||
|
Start-Service -Name wuauserv
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
Get-Service -Name wuauserv
|
||||||
|
```
|
||||||
|
|
||||||
|
**5. Updates Fail to Install**
|
||||||
|
```
|
||||||
|
Error: Failed: 2024-01 Cumulative Update
|
||||||
|
```
|
||||||
|
**Solution:**
|
||||||
|
- Check Windows Update logs: `C:\Windows\Logs\WindowsUpdate`
|
||||||
|
- Run Windows Update Troubleshooter
|
||||||
|
- Check for conflicting software (antivirus, etc.)
|
||||||
|
- Review exclusions - update might be excluded by pattern
|
||||||
|
|
||||||
|
**6. Lock File Exists**
|
||||||
|
```
|
||||||
|
Guard: Lock file exists. Skipping.
|
||||||
|
```
|
||||||
|
**Solution:**
|
||||||
|
- Another instance is running, or previous run didn't complete
|
||||||
|
- Manually delete `.lock` file if stuck
|
||||||
|
- Check for hung PowerShell processes
|
||||||
|
|
||||||
|
**7. Pending Reboot Blocks Updates**
|
||||||
|
```
|
||||||
|
Error: Reboot required but rebootBehavior is 'never'
|
||||||
|
```
|
||||||
|
**Solution:**
|
||||||
|
- Set `rebootBehavior: "manual"` or `"auto"` in configuration
|
||||||
|
- Manually reboot system before running updates
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Run with verbose output:
|
||||||
|
```powershell
|
||||||
|
.\windows-update.ps1 -Verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
Test without installing updates (set in scriptsettings.json):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"dryRun": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Test First** - Always test with `dryRun: true` before actual execution
|
||||||
|
2. **Schedule Wisely** - Run during maintenance windows (nights, weekends)
|
||||||
|
3. **Start Conservative** - Begin with Critical/Security updates only
|
||||||
|
4. **Monitor Results** - Review update reports and logs regularly
|
||||||
|
5. **Backup First** - Ensure system backups before major updates
|
||||||
|
6. **Reboot Testing** - Test `rebootBehavior: "auto"` in non-production environment first
|
||||||
|
7. **Exclusion Management** - Keep exclusions list minimal and documented
|
||||||
|
8. **Review Failures** - Investigate and resolve failed updates promptly
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- Script requires **Administrator privileges** for update installation
|
||||||
|
- Updates are downloaded from **Microsoft Update** servers only
|
||||||
|
- Consider using **dedicated service account** for automated execution
|
||||||
|
- **Lock files** prevent concurrent execution and potential conflicts
|
||||||
|
- **Audit logs** maintain record of all update activities
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
- **Update scanning** typically takes 1-5 minutes
|
||||||
|
- **Download speed** depends on update size and internet connection
|
||||||
|
- **Installation time** varies by update type (minutes to hours)
|
||||||
|
- **Reboot time** adds 2-10 minutes to total duration
|
||||||
|
- **Definition updates** are typically fast (<1 minute)
|
||||||
|
- **Feature updates** can take 30+ minutes
|
||||||
|
|
||||||
|
### Typical Update Times
|
||||||
|
|
||||||
|
| Update Type | Download | Install | Reboot |
|
||||||
|
|-------------|----------|---------|--------|
|
||||||
|
| Definition Updates | <1 min | <1 min | No |
|
||||||
|
| Security Updates | 2-5 min | 5-10 min | Sometimes |
|
||||||
|
| Cumulative Updates | 5-15 min | 10-30 min | Yes |
|
||||||
|
| Feature Updates | 15-60 min | 30-120 min | Yes |
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
### 1.0.0 (2026-01-28)
|
||||||
|
- Initial release
|
||||||
|
- PSWindowsUpdate integration
|
||||||
|
- Category-based filtering
|
||||||
|
- KB number and title pattern exclusions
|
||||||
|
- Pre-flight checks (disk space, pending reboot, service)
|
||||||
|
- Auto-reboot with configurable delay
|
||||||
|
- Update report generation
|
||||||
|
- Dry run mode
|
||||||
|
- Integration with SchedulerTemplate.psm1
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues or questions:
|
||||||
|
1. Check the [Troubleshooting](#troubleshooting) section
|
||||||
|
2. Review script logs for error details
|
||||||
|
3. Verify all [Requirements](#requirements) are met
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See [LICENSE](../../LICENSE.md) in the root directory.
|
||||||
|
|
||||||
|
## Utilities
|
||||||
|
|
||||||
|
### windows-update-policy.ps1
|
||||||
|
|
||||||
|
Located in `Utilities/`, this script configures Windows Update to use server-style manual updates (notify-only mode with no automatic reboots).
|
||||||
|
|
||||||
|
**Apply server-style policy:**
|
||||||
|
```powershell
|
||||||
|
.\Utilities\windows-update-policy.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Revert to Windows defaults:**
|
||||||
|
```powershell
|
||||||
|
.\Utilities\windows-update-policy.ps1 -Revert
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Sets `AUOptions = 2` (notify for download and install)
|
||||||
|
- Disables automatic reboots with logged-on users
|
||||||
|
- Disables scheduled auto-reboot
|
||||||
|
- Disables automatic maintenance updates
|
||||||
|
- Uses Microsoft Update servers
|
||||||
|
|
||||||
|
This is useful when you want full control over when updates are downloaded, installed, and when the system reboots - particularly for workstations that should behave like servers.
|
||||||
|
|
||||||
|
## Related Files
|
||||||
|
|
||||||
|
- `../SchedulerTemplate.psm1` - Shared scheduling and logging module
|
||||||
|
- `scriptsettings.json` - Configuration file
|
||||||
|
- `windows-update.bat` - Batch launcher
|
||||||
|
- `windows-update.ps1` - Main script
|
||||||
|
- `Utilities/windows-update-policy.ps1` - Windows Update policy configuration
|
||||||
158
examples/Windows-Update/Utilities/windows-update-policy.ps1
Normal file
158
examples/Windows-Update/Utilities/windows-update-policy.ps1
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[switch]$Revert
|
||||||
|
)
|
||||||
|
|
||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Configure Windows Update to Server 2025-style manual updates.
|
||||||
|
.DESCRIPTION
|
||||||
|
Sets Windows Update behavior to notify-only mode with no automatic reboots.
|
||||||
|
This gives you full control over when updates are downloaded, installed, and rebooted.
|
||||||
|
.PARAMETER Revert
|
||||||
|
Remove the policy settings and restore Windows defaults.
|
||||||
|
.EXAMPLE
|
||||||
|
.\windows-update-policy.ps1
|
||||||
|
Apply server-style update policy.
|
||||||
|
.EXAMPLE
|
||||||
|
.\windows-update-policy.ps1 -Revert
|
||||||
|
Remove policy and restore defaults.
|
||||||
|
.VERSION
|
||||||
|
1.0.0
|
||||||
|
.DATE
|
||||||
|
2026-01-28
|
||||||
|
.NOTES
|
||||||
|
- Requires Administrator privileges
|
||||||
|
- Changes take effect after gpupdate or reboot
|
||||||
|
#>
|
||||||
|
|
||||||
|
# Registry paths
|
||||||
|
$WUBase = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
|
||||||
|
$WU_AU = Join-Path $WUBase 'AU'
|
||||||
|
|
||||||
|
# Helper Functions =========================================================
|
||||||
|
|
||||||
|
function Set-ServerStyleUpdates {
|
||||||
|
Write-Host "[INFO] Applying server-style Windows Update policy..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Create policy keys
|
||||||
|
New-Item -Path $WUBase -Force | Out-Null
|
||||||
|
New-Item -Path $WU_AU -Force | Out-Null
|
||||||
|
|
||||||
|
# AUOptions = 2: Notify for download and notify for install
|
||||||
|
New-ItemProperty -Path $WU_AU -Name 'AUOptions' -PropertyType DWord -Value 2 -Force | Out-Null
|
||||||
|
Write-Host " - AUOptions = 2 (Notify for download and install)" -ForegroundColor Gray
|
||||||
|
|
||||||
|
# Do not auto reboot with logged-on users
|
||||||
|
New-ItemProperty -Path $WU_AU -Name 'NoAutoRebootWithLoggedOnUsers' -PropertyType DWord -Value 1 -Force | Out-Null
|
||||||
|
Write-Host " - NoAutoRebootWithLoggedOnUsers = 1" -ForegroundColor Gray
|
||||||
|
|
||||||
|
# Prevent any scheduled auto reboot
|
||||||
|
New-ItemProperty -Path $WU_AU -Name 'AlwaysAutoRebootAtScheduledTime' -PropertyType DWord -Value 0 -Force | Out-Null
|
||||||
|
Write-Host " - AlwaysAutoRebootAtScheduledTime = 0" -ForegroundColor Gray
|
||||||
|
|
||||||
|
# Disable automatic maintenance updates
|
||||||
|
New-ItemProperty -Path $WU_AU -Name 'AutomaticMaintenanceEnabled' -PropertyType DWord -Value 0 -Force | Out-Null
|
||||||
|
Write-Host " - AutomaticMaintenanceEnabled = 0" -ForegroundColor Gray
|
||||||
|
|
||||||
|
# Disable auto restart reminders
|
||||||
|
New-ItemProperty -Path $WU_AU -Name 'RebootWarningTimeoutEnabled' -PropertyType DWord -Value 0 -Force | Out-Null
|
||||||
|
New-ItemProperty -Path $WU_AU -Name 'RebootRelaunchTimeoutEnabled' -PropertyType DWord -Value 0 -Force | Out-Null
|
||||||
|
Write-Host " - RebootWarningTimeoutEnabled = 0" -ForegroundColor Gray
|
||||||
|
Write-Host " - RebootRelaunchTimeoutEnabled = 0" -ForegroundColor Gray
|
||||||
|
|
||||||
|
# Set empty WUServer/WUStatusServer (use Microsoft Update)
|
||||||
|
New-ItemProperty -Path $WUBase -Name 'WUServer' -PropertyType String -Value '' -Force | Out-Null
|
||||||
|
New-ItemProperty -Path $WUBase -Name 'WUStatusServer' -PropertyType String -Value '' -Force | Out-Null
|
||||||
|
Write-Host " - WUServer/WUStatusServer = '' (Microsoft Update)" -ForegroundColor Gray
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] Server-style Windows Update policy applied." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
function Remove-ServerStyleUpdates {
|
||||||
|
Write-Host "[INFO] Removing server-style Windows Update policy..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Remove WUBase properties
|
||||||
|
foreach ($name in @('WUServer', 'WUStatusServer')) {
|
||||||
|
Remove-ItemProperty -Path $WUBase -Name $name -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove AU properties
|
||||||
|
foreach ($name in @(
|
||||||
|
'AUOptions',
|
||||||
|
'NoAutoRebootWithLoggedOnUsers',
|
||||||
|
'AlwaysAutoRebootAtScheduledTime',
|
||||||
|
'AutomaticMaintenanceEnabled',
|
||||||
|
'RebootWarningTimeoutEnabled',
|
||||||
|
'RebootRelaunchTimeoutEnabled'
|
||||||
|
)) {
|
||||||
|
Remove-ItemProperty -Path $WU_AU -Name $name -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up empty keys
|
||||||
|
try {
|
||||||
|
$auProps = Get-ItemProperty -Path $WU_AU -ErrorAction SilentlyContinue
|
||||||
|
if ($auProps) {
|
||||||
|
$members = $auProps | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -notlike 'PS*' }
|
||||||
|
if (-not $members) {
|
||||||
|
Remove-Item $WU_AU -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] Server-style Windows Update policy removed." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-PolicyRefresh {
|
||||||
|
Write-Host "[INFO] Refreshing group policy..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
try {
|
||||||
|
gpupdate /force 2>&1 | Out-Null
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "[SUCCESS] Group policy refreshed." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw "gpupdate returned exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Host "[WARNING] gpupdate failed. Restarting Windows Update service..." -ForegroundColor Yellow
|
||||||
|
Stop-Service wuauserv -Force -ErrorAction SilentlyContinue
|
||||||
|
Start-Service wuauserv -ErrorAction SilentlyContinue
|
||||||
|
Write-Host "[INFO] Windows Update service restarted." -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main =====================================================================
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "Windows Update Policy Configuration" -ForegroundColor Cyan
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
if ($Revert) {
|
||||||
|
Remove-ServerStyleUpdates
|
||||||
|
Write-Host ""
|
||||||
|
Invoke-PolicyRefresh
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[INFO] Windows Update will now use default behavior." -ForegroundColor Cyan
|
||||||
|
Write-Host "[INFO] You may need to reboot for all changes to take effect." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Set-ServerStyleUpdates
|
||||||
|
Write-Host ""
|
||||||
|
Invoke-PolicyRefresh
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[INFO] Windows Update is now configured for manual control:" -ForegroundColor Cyan
|
||||||
|
Write-Host " - Updates will notify but not auto-download" -ForegroundColor Gray
|
||||||
|
Write-Host " - No automatic reboots will occur" -ForegroundColor Gray
|
||||||
|
Write-Host " - Use windows-update.ps1 to manually install updates" -ForegroundColor Gray
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[INFO] You may need to reboot for all changes to take effect." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
83
examples/Windows-Update/scriptsettings.json
Normal file
83
examples/Windows-Update/scriptsettings.json
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft-07/schema",
|
||||||
|
"title": "Windows Update Script Settings",
|
||||||
|
"description": "Configuration file for windows-update.ps1 script (automated Windows Update management)",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lastModified": "2026-01-28",
|
||||||
|
"schedule": {
|
||||||
|
"runMonth": [],
|
||||||
|
"runWeekday": ["Wednesday"],
|
||||||
|
"runTime": ["02:00"],
|
||||||
|
"minIntervalMinutes": 60
|
||||||
|
},
|
||||||
|
"updateCategories": [
|
||||||
|
"Critical Updates",
|
||||||
|
"Security Updates",
|
||||||
|
"Definition Updates",
|
||||||
|
"Update Rollups"
|
||||||
|
],
|
||||||
|
"exclusions": {
|
||||||
|
"kbNumbers": [],
|
||||||
|
"titlePatterns": [
|
||||||
|
"*Preview*",
|
||||||
|
"*Beta*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"preChecks": {
|
||||||
|
"minDiskSpaceGB": 10,
|
||||||
|
"checkPendingReboot": true
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"rebootBehavior": "manual",
|
||||||
|
"rebootDelayMinutes": 5,
|
||||||
|
"dryRun": false
|
||||||
|
},
|
||||||
|
"reporting": {
|
||||||
|
"generateReport": true,
|
||||||
|
"emailNotification": false,
|
||||||
|
"emailSettings": {
|
||||||
|
"smtpServer": "smtp.example.com",
|
||||||
|
"smtpPort": 587,
|
||||||
|
"from": "windows-update@example.com",
|
||||||
|
"to": ["admin@example.com"],
|
||||||
|
"useSSL": true,
|
||||||
|
"credentialEnvVar": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"_comments": {
|
||||||
|
"version": "Configuration schema version",
|
||||||
|
"lastModified": "Last modification date (YYYY-MM-DD)",
|
||||||
|
"schedule": {
|
||||||
|
"runMonth": "Array of month names (e.g. 'January', 'June') to run updates. Empty array = every month.",
|
||||||
|
"runWeekday": "Array of weekday names (e.g. 'Wednesday', 'Sunday') to run updates. Empty array = every day.",
|
||||||
|
"runTime": "Array of UTC times in HH:mm format when updates should run.",
|
||||||
|
"minIntervalMinutes": "Minimum minutes between update runs to prevent duplicate executions."
|
||||||
|
},
|
||||||
|
"updateCategories": "Array of update categories to install. Available: 'Critical Updates', 'Security Updates', 'Definition Updates', 'Update Rollups', 'Feature Packs', 'Service Packs', 'Tools', 'Drivers'",
|
||||||
|
"exclusions": {
|
||||||
|
"kbNumbers": "Array of KB numbers to exclude (e.g. 'KB5034441')",
|
||||||
|
"titlePatterns": "Array of wildcard patterns to exclude by title (e.g. '*Preview*', '*Optional*')"
|
||||||
|
},
|
||||||
|
"preChecks": {
|
||||||
|
"minDiskSpaceGB": "Minimum free disk space in GB required before installing updates",
|
||||||
|
"checkPendingReboot": "Check if system has pending reboot from previous updates"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"rebootBehavior": "Reboot behavior: 'never' (abort if reboot needed), 'manual' (continue, user reboots later), 'auto' (reboot automatically after delay)",
|
||||||
|
"rebootDelayMinutes": "Minutes to wait before auto-reboot when rebootBehavior is 'auto' (0 = immediate)",
|
||||||
|
"dryRun": "Simulate update installation without making changes"
|
||||||
|
},
|
||||||
|
"reporting": {
|
||||||
|
"generateReport": "Generate text report after update installation",
|
||||||
|
"emailNotification": "Send email notification after updates (requires emailSettings)",
|
||||||
|
"emailSettings": {
|
||||||
|
"smtpServer": "SMTP server hostname",
|
||||||
|
"smtpPort": "SMTP port (typically 587 for TLS, 465 for SSL, 25 for plain)",
|
||||||
|
"from": "Sender email address",
|
||||||
|
"to": "Array of recipient email addresses",
|
||||||
|
"useSSL": "Use SSL/TLS for connection",
|
||||||
|
"credentialEnvVar": "Machine-level environment variable containing Base64('username:password'). Empty for no auth."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
examples/Windows-Update/windows-update.bat
Normal file
74
examples/Windows-Update/windows-update.bat
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
|
REM ============================================================================
|
||||||
|
REM Windows Update Launcher
|
||||||
|
REM VERSION: 1.0.0
|
||||||
|
REM DATE: 2026-01-28
|
||||||
|
REM DESCRIPTION: Batch file launcher for windows-update.ps1 with admin check
|
||||||
|
REM ============================================================================
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Windows Update Automation Launcher
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Check for Administrator privileges
|
||||||
|
net session >nul 2>&1
|
||||||
|
if %errorLevel% NEQ 0 (
|
||||||
|
echo [ERROR] This script must be run as Administrator!
|
||||||
|
echo.
|
||||||
|
echo Please right-click and select "Run as administrator"
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [OK] Running with Administrator privileges
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Get script directory
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
set "PS_SCRIPT=%SCRIPT_DIR%windows-update.ps1"
|
||||||
|
|
||||||
|
REM Check if PowerShell script exists
|
||||||
|
if not exist "%PS_SCRIPT%" (
|
||||||
|
echo [ERROR] PowerShell script not found: %PS_SCRIPT%
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [OK] Found PowerShell script: %PS_SCRIPT%
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Starting Windows Update process...
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Execute PowerShell script
|
||||||
|
REM Note: Logging is handled by UScheduler service
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%"
|
||||||
|
|
||||||
|
REM Capture exit code
|
||||||
|
set "EXIT_CODE=%ERRORLEVEL%"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ============================================
|
||||||
|
echo Windows Update process completed
|
||||||
|
echo Exit Code: %EXIT_CODE%
|
||||||
|
echo ============================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
if %EXIT_CODE% EQU 0 (
|
||||||
|
echo [SUCCESS] Updates completed successfully
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Updates completed with errors
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
|
||||||
|
endlocal
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
480
examples/Windows-Update/windows-update.ps1
Normal file
480
examples/Windows-Update/windows-update.ps1
Normal file
@ -0,0 +1,480 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[switch]$Automated,
|
||||||
|
[string]$CurrentDateTimeUtc
|
||||||
|
)
|
||||||
|
|
||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Automated Windows Update management with scheduling and reporting.
|
||||||
|
.DESCRIPTION
|
||||||
|
Production-ready Windows Update automation using PSWindowsUpdate module.
|
||||||
|
Supports scheduled updates, category filtering, exclusions, pre/post checks,
|
||||||
|
and auto-reboot with maintenance window control.
|
||||||
|
.VERSION
|
||||||
|
1.0.0
|
||||||
|
.DATE
|
||||||
|
2026-01-28
|
||||||
|
.NOTES
|
||||||
|
- Requires PSWindowsUpdate module (auto-installed if missing)
|
||||||
|
- Requires SchedulerTemplate.psm1 module
|
||||||
|
#>
|
||||||
|
|
||||||
|
# Script Version
|
||||||
|
$ScriptVersion = "1.0.0"
|
||||||
|
$ScriptDate = "2026-01-28"
|
||||||
|
|
||||||
|
try {
|
||||||
|
Import-Module "$PSScriptRoot\..\SchedulerTemplate.psm1" -Force -ErrorAction Stop
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Failed to load SchedulerTemplate.psm1: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load Settings ============================================================
|
||||||
|
|
||||||
|
$settingsFile = Join-Path $PSScriptRoot "scriptsettings.json"
|
||||||
|
|
||||||
|
if (-not (Test-Path $settingsFile)) {
|
||||||
|
Write-Error "Settings file not found: $settingsFile"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settings = Get-Content $settingsFile -Raw | ConvertFrom-Json
|
||||||
|
Write-Verbose "Loaded settings from $settingsFile"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Failed to load settings from $settingsFile : $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process Settings =========================================================
|
||||||
|
|
||||||
|
# Validate required settings
|
||||||
|
$requiredSettings = @('updateCategories', 'preChecks', 'options')
|
||||||
|
foreach ($setting in $requiredSettings) {
|
||||||
|
if (-not $settings.$setting) {
|
||||||
|
Write-Error "Required setting '$setting' is missing or empty in $settingsFile"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract settings
|
||||||
|
$UpdateCategories = $settings.updateCategories
|
||||||
|
$Exclusions = $settings.exclusions
|
||||||
|
$PreChecks = $settings.preChecks
|
||||||
|
$Options = $settings.options
|
||||||
|
$Reporting = $settings.reporting
|
||||||
|
|
||||||
|
# Get DryRun from settings
|
||||||
|
$DryRun = $Options.dryRun
|
||||||
|
|
||||||
|
# Schedule Configuration
|
||||||
|
$Config = @{
|
||||||
|
RunMonth = $settings.schedule.runMonth
|
||||||
|
RunWeekday = $settings.schedule.runWeekday
|
||||||
|
RunTime = $settings.schedule.runTime
|
||||||
|
MinIntervalMinutes = $settings.schedule.minIntervalMinutes
|
||||||
|
}
|
||||||
|
|
||||||
|
# End Settings =============================================================
|
||||||
|
|
||||||
|
# Global variables
|
||||||
|
$script:UpdateStats = @{
|
||||||
|
StartTime = Get-Date
|
||||||
|
EndTime = $null
|
||||||
|
Success = $false
|
||||||
|
Installed = 0
|
||||||
|
Failed = 0
|
||||||
|
Skipped = 0
|
||||||
|
RebootRequired = $false
|
||||||
|
ErrorMessage = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper Functions =========================================================
|
||||||
|
|
||||||
|
function Test-PSWindowsUpdate {
|
||||||
|
param([switch]$Automated)
|
||||||
|
|
||||||
|
Write-Log "Checking PSWindowsUpdate module..." -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
if (-not (Get-Module -ListAvailable -Name PSWindowsUpdate)) {
|
||||||
|
Write-Log "PSWindowsUpdate module not found. Installing..." -Level Warning -Automated:$Automated
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Try to install from PSGallery
|
||||||
|
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
|
||||||
|
Install-Module -Name PSWindowsUpdate -Force -Scope AllUsers -ErrorAction Stop
|
||||||
|
Write-Log "PSWindowsUpdate module installed successfully" -Level Success -Automated:$Automated
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Failed to install PSWindowsUpdate module: $_" -Level Error -Automated:$Automated
|
||||||
|
Write-Log "Please install manually: Install-Module PSWindowsUpdate -Force" -Level Info -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Import-Module PSWindowsUpdate -ErrorAction Stop
|
||||||
|
Write-Log "PSWindowsUpdate module loaded" -Level Success -Automated:$Automated
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Failed to import PSWindowsUpdate module: $_" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-PreUpdateChecks {
|
||||||
|
param([switch]$Automated)
|
||||||
|
|
||||||
|
Write-Log "Running pre-update checks..." -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
# Check disk space
|
||||||
|
$systemDrive = $env:SystemDrive
|
||||||
|
$drive = Get-PSDrive -Name $systemDrive.TrimEnd(':')
|
||||||
|
$freeSpaceGB = [math]::Round($drive.Free / 1GB, 2)
|
||||||
|
$minSpaceGB = $PreChecks.minDiskSpaceGB
|
||||||
|
|
||||||
|
Write-Log "Free space on $systemDrive : $freeSpaceGB GB" -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
if ($freeSpaceGB -lt $minSpaceGB) {
|
||||||
|
Write-Log "Insufficient disk space. Required: $minSpaceGB GB, Available: $freeSpaceGB GB" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for pending reboot
|
||||||
|
if ($PreChecks.checkPendingReboot) {
|
||||||
|
$rebootRequired = Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired"
|
||||||
|
if ($rebootRequired) {
|
||||||
|
Write-Log "System has pending reboot from previous updates" -Level Warning -Automated:$Automated
|
||||||
|
if ($Options.rebootBehavior -eq 'never') {
|
||||||
|
Write-Log "Reboot required but rebootBehavior is 'never'" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check Windows Update service
|
||||||
|
$wuService = Get-Service -Name wuauserv
|
||||||
|
if ($wuService.Status -ne 'Running') {
|
||||||
|
Write-Log "Starting Windows Update service..." -Level Info -Automated:$Automated
|
||||||
|
try {
|
||||||
|
Start-Service -Name wuauserv -ErrorAction Stop
|
||||||
|
Write-Log "Windows Update service started" -Level Success -Automated:$Automated
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Failed to start Windows Update service: $_" -Level Error -Automated:$Automated
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Pre-update checks passed" -Level Success -Automated:$Automated
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AvailableUpdates {
|
||||||
|
param([switch]$Automated)
|
||||||
|
|
||||||
|
Write-Log "Scanning for available updates..." -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Get updates
|
||||||
|
$updates = Get-WindowsUpdate -MicrosoftUpdate -Verbose:$false | Where-Object {
|
||||||
|
$update = $_
|
||||||
|
$included = $false
|
||||||
|
|
||||||
|
# Check categories
|
||||||
|
foreach ($cat in $UpdateCategories) {
|
||||||
|
if ($update.Categories -match $cat) {
|
||||||
|
$included = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply KB exclusions
|
||||||
|
if ($included -and $Exclusions.kbNumbers.Count -gt 0) {
|
||||||
|
foreach ($kb in $Exclusions.kbNumbers) {
|
||||||
|
if ($update.KBArticleIDs -contains $kb) {
|
||||||
|
Write-Log "Excluded by KB: $($update.Title) [$kb]" -Level Info -Automated:$Automated
|
||||||
|
$included = $false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply title exclusions
|
||||||
|
if ($included -and $Exclusions.titlePatterns.Count -gt 0) {
|
||||||
|
foreach ($pattern in $Exclusions.titlePatterns) {
|
||||||
|
if ($update.Title -like $pattern) {
|
||||||
|
Write-Log "Excluded by pattern: $($update.Title) [$pattern]" -Level Info -Automated:$Automated
|
||||||
|
$included = $false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $included
|
||||||
|
}
|
||||||
|
|
||||||
|
return $updates
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Failed to scan for updates: $_" -Level Error -Automated:$Automated
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-AvailableUpdates {
|
||||||
|
param(
|
||||||
|
$Updates,
|
||||||
|
[switch]$Automated
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($Updates.Count -eq 0) {
|
||||||
|
Write-Log "No updates to install" -Level Info -Automated:$Automated
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Found $($Updates.Count) update(s) to install" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
foreach ($update in $Updates) {
|
||||||
|
$sizeKB = [math]::Round($update.Size / 1KB, 2)
|
||||||
|
Write-Log " [$($update.KBArticleIDs -join ',')] $($update.Title) ($sizeKB KB)" -Level Info -Automated:$Automated
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Log "DRY RUN MODE - No updates will be installed" -Level Warning -Automated:$Automated
|
||||||
|
$script:UpdateStats.Skipped = $Updates.Count
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install updates
|
||||||
|
Write-Log "Installing updates..." -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
try {
|
||||||
|
$installParams = @{
|
||||||
|
MicrosoftUpdate = $true
|
||||||
|
AcceptAll = $true
|
||||||
|
IgnoreReboot = ($Options.rebootBehavior -ne 'auto')
|
||||||
|
Verbose = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use KBArticleID filter if available
|
||||||
|
$kbList = $Updates | ForEach-Object { $_.KBArticleIDs } | Where-Object { $_ }
|
||||||
|
if ($kbList.Count -gt 0) {
|
||||||
|
$installParams['KBArticleID'] = $kbList
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Install-WindowsUpdate @installParams
|
||||||
|
|
||||||
|
# Process results
|
||||||
|
foreach ($item in $result) {
|
||||||
|
if ($item.Result -eq "Installed" -or $item.Result -eq "Downloaded") {
|
||||||
|
$script:UpdateStats.Installed++
|
||||||
|
Write-Log "Installed: $($item.Title)" -Level Success -Automated:$Automated
|
||||||
|
}
|
||||||
|
elseif ($item.Result -eq "Failed") {
|
||||||
|
$script:UpdateStats.Failed++
|
||||||
|
Write-Log "Failed: $($item.Title)" -Level Error -Automated:$Automated
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$script:UpdateStats.Skipped++
|
||||||
|
Write-Log "Skipped: $($item.Title) [Result: $($item.Result)]" -Level Warning -Automated:$Automated
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item.RebootRequired) {
|
||||||
|
$script:UpdateStats.RebootRequired = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Update installation failed: $_" -Level Error -Automated:$Automated
|
||||||
|
$script:UpdateStats.Failed = $Updates.Count
|
||||||
|
$script:UpdateStats.ErrorMessage = "Installation failed: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-PostUpdateActions {
|
||||||
|
param([switch]$Automated)
|
||||||
|
|
||||||
|
Write-Log "Running post-update actions..." -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
# Check reboot requirement
|
||||||
|
if ($script:UpdateStats.RebootRequired) {
|
||||||
|
Write-Log "System reboot required" -Level Warning -Automated:$Automated
|
||||||
|
|
||||||
|
if ($Options.rebootBehavior -eq 'auto') {
|
||||||
|
$delayMinutes = $Options.rebootDelayMinutes
|
||||||
|
Write-Log "System will reboot in $delayMinutes minutes..." -Level Warning -Automated:$Automated
|
||||||
|
|
||||||
|
if ($delayMinutes -gt 0) {
|
||||||
|
Start-Sleep -Seconds ($delayMinutes * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove lock file before reboot to prevent future runs from being blocked
|
||||||
|
$lockFile = [IO.Path]::ChangeExtension($PSCommandPath, ".lock")
|
||||||
|
if (Test-Path $lockFile) {
|
||||||
|
Remove-Item $lockFile -Force
|
||||||
|
Write-Log "Lock file removed before reboot" -Level Info -Automated:$Automated
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Initiating system reboot..." -Level Warning -Automated:$Automated
|
||||||
|
Restart-Computer -Force
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "Manual reboot required" -Level Warning -Automated:$Automated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate update report
|
||||||
|
if ($Reporting.generateReport) {
|
||||||
|
$reportPath = Join-Path $PSScriptRoot "update-report-$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"
|
||||||
|
|
||||||
|
$reportContent = @"
|
||||||
|
Windows Update Report
|
||||||
|
=====================
|
||||||
|
Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
||||||
|
|
||||||
|
Statistics:
|
||||||
|
-----------
|
||||||
|
Updates Installed: $($script:UpdateStats.Installed)
|
||||||
|
Updates Failed: $($script:UpdateStats.Failed)
|
||||||
|
Updates Skipped: $($script:UpdateStats.Skipped)
|
||||||
|
Reboot Required: $($script:UpdateStats.RebootRequired)
|
||||||
|
|
||||||
|
Recent Update History:
|
||||||
|
----------------------
|
||||||
|
"@
|
||||||
|
|
||||||
|
try {
|
||||||
|
$history = Get-WindowsUpdate -Last 10 -Verbose:$false
|
||||||
|
foreach ($item in $history) {
|
||||||
|
$reportContent += "`n[$($item.Date)] $($item.Title) - $($item.Result)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$reportContent += "`nFailed to retrieve update history"
|
||||||
|
}
|
||||||
|
|
||||||
|
$reportContent | Out-File -FilePath $reportPath -Encoding UTF8
|
||||||
|
Write-Log "Report saved: $reportPath" -Level Success -Automated:$Automated
|
||||||
|
|
||||||
|
# Send email notification if enabled
|
||||||
|
if ($Reporting.emailNotification -and $Reporting.emailSettings) {
|
||||||
|
$hostname = $env:COMPUTERNAME
|
||||||
|
$status = if ($script:UpdateStats.Failed -eq 0) { "SUCCESS" } else { "COMPLETED WITH ERRORS" }
|
||||||
|
$subject = "[$hostname] Windows Update Report - $status"
|
||||||
|
|
||||||
|
Send-EmailNotification -EmailSettings $Reporting.emailSettings -Subject $subject -Body $reportContent -Automated:$Automated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-UpdateSummary {
|
||||||
|
param([switch]$Automated)
|
||||||
|
|
||||||
|
$script:UpdateStats.EndTime = Get-Date
|
||||||
|
$duration = $script:UpdateStats.EndTime - $script:UpdateStats.StartTime
|
||||||
|
|
||||||
|
Write-Log "" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "UPDATE SUMMARY" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Start Time : $($script:UpdateStats.StartTime.ToString('yyyy-MM-dd HH:mm:ss'))" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "End Time : $($script:UpdateStats.EndTime.ToString('yyyy-MM-dd HH:mm:ss'))" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Duration : $($duration.Hours)h $($duration.Minutes)m $($duration.Seconds)s" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Status : $(if ($script:UpdateStats.Failed -eq 0) { 'SUCCESS' } else { 'COMPLETED WITH ERRORS' })" -Level $(if ($script:UpdateStats.Failed -eq 0) { 'Success' } else { 'Warning' }) -Automated:$Automated
|
||||||
|
Write-Log "" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Installed : $($script:UpdateStats.Installed)" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Failed : $($script:UpdateStats.Failed)" -Level $(if ($script:UpdateStats.Failed -eq 0) { 'Info' } else { 'Error' }) -Automated:$Automated
|
||||||
|
Write-Log "Skipped : $($script:UpdateStats.Skipped)" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Reboot Needed : $($script:UpdateStats.RebootRequired)" -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
if ($script:UpdateStats.ErrorMessage) {
|
||||||
|
Write-Log "" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Error: $($script:UpdateStats.ErrorMessage)" -Level Error -Automated:$Automated
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
$script:UpdateStats.Success = ($script:UpdateStats.Failed -eq 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main Business Logic ======================================================
|
||||||
|
|
||||||
|
function Start-BusinessLogic {
|
||||||
|
param([switch]$Automated)
|
||||||
|
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Windows Update Process Started" -Level Info -Automated:$Automated
|
||||||
|
Write-Log "Script Version: $ScriptVersion ($ScriptDate)" -Level Info -Automated:$Automated
|
||||||
|
if ($DryRun) {
|
||||||
|
Write-Log "DRY RUN MODE - No changes will be made" -Level Warning -Automated:$Automated
|
||||||
|
}
|
||||||
|
Write-Log "========================================" -Level Info -Automated:$Automated
|
||||||
|
|
||||||
|
# Check PSWindowsUpdate module
|
||||||
|
if (-not (Test-PSWindowsUpdate -Automated:$Automated)) {
|
||||||
|
Write-Log "PSWindowsUpdate module check failed. Aborting." -Level Error -Automated:$Automated
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run pre-update checks
|
||||||
|
if (-not (Test-PreUpdateChecks -Automated:$Automated)) {
|
||||||
|
Write-Log "Pre-update checks failed. Aborting." -Level Error -Automated:$Automated
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Scan for updates
|
||||||
|
$updates = Get-AvailableUpdates -Automated:$Automated
|
||||||
|
|
||||||
|
if ($updates.Count -eq 0) {
|
||||||
|
Write-Log "System is up to date. No updates available." -Level Success -Automated:$Automated
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Install updates
|
||||||
|
Install-AvailableUpdates -Updates $updates -Automated:$Automated
|
||||||
|
|
||||||
|
# Post-update actions
|
||||||
|
Invoke-PostUpdateActions -Automated:$Automated
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print summary
|
||||||
|
Write-UpdateSummary -Automated:$Automated
|
||||||
|
|
||||||
|
# Exit with appropriate code
|
||||||
|
if ($script:UpdateStats.Failed -gt 0) {
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Entry Point ==============================================================
|
||||||
|
|
||||||
|
if ($Automated) {
|
||||||
|
if (Get-Command Invoke-ScheduledExecution -ErrorAction SilentlyContinue) {
|
||||||
|
$params = @{
|
||||||
|
Config = $Config
|
||||||
|
Automated = $Automated
|
||||||
|
CurrentDateTimeUtc = $CurrentDateTimeUtc
|
||||||
|
ScriptBlock = { Start-BusinessLogic -Automated:$Automated }
|
||||||
|
}
|
||||||
|
Invoke-ScheduledExecution @params
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "Invoke-ScheduledExecution not available. Execution aborted." -Level Error -Automated:$Automated
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "Manual execution started" -Level Info -Automated:$Automated
|
||||||
|
Start-BusinessLogic -Automated:$Automated
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user