Windows Server 2022 PowerShell Automation: Enterprise Management

Tyler Maginnis | February 05, 2024

Windows ServerPowerShellautomationscriptingenterprise

Need Professional Windows Server 2022?

Get expert assistance with your windows server 2022 implementation and management. Tyler on Tech Louisville provides priority support for Louisville businesses.

Same-day service available for Louisville area

Windows Server 2022 PowerShell Automation: Enterprise Management

PowerShell transforms Windows Server administration from manual tasks to automated, scalable operations. This comprehensive guide covers PowerShell automation for Windows Server 2022, from basic server management to enterprise-wide deployments.

PowerShell Fundamentals for Server Management

Initial Server Configuration

# Server Initial Configuration Script
# Run as Administrator on fresh Windows Server 2022 installation

#Requires -RunAsAdministrator
#Requires -Version 5.1

param(
    [Parameter(Mandatory=$true)]
    [string]$ComputerName,

    [Parameter(Mandatory=$true)]
    [string]$IPAddress,

    [Parameter(Mandatory=$true)]
    [string]$DefaultGateway,

    [Parameter(Mandatory=$true)]
    [string[]]$DNSServers,

    [Parameter(Mandatory=$true)]
    [string]$DomainName,

    [Parameter(Mandatory=$true)]
    [SecureString]$DomainAdminPassword
)

# Set execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force

# Configure Windows Error Reporting
Write-Host "Configuring Windows Error Reporting..." -ForegroundColor Yellow
Disable-WindowsErrorReporting

# Set computer name
Write-Host "Setting computer name to $ComputerName..." -ForegroundColor Yellow
Rename-Computer -NewName $ComputerName -Force

# Configure network settings
Write-Host "Configuring network settings..." -ForegroundColor Yellow
$adapter = Get-NetAdapter | Where-Object {$_.Status -eq "Up"} | Select-Object -First 1

# Remove existing IP configuration
Remove-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue
Remove-NetRoute -InterfaceIndex $adapter.InterfaceIndex -Confirm:$false -ErrorAction SilentlyContinue

# Set static IP
New-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex `
    -IPAddress $IPAddress `
    -PrefixLength 24 `
    -DefaultGateway $DefaultGateway

# Set DNS servers
Set-DnsClientServerAddress -InterfaceIndex $adapter.InterfaceIndex `
    -ServerAddresses $DNSServers

# Configure time zone
Write-Host "Setting time zone..." -ForegroundColor Yellow
Set-TimeZone -Name "Eastern Standard Time"

# Enable Remote Desktop
Write-Host "Enabling Remote Desktop..." -ForegroundColor Yellow
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' `
    -Name "fDenyTSConnections" -Value 0
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

# Configure Windows Firewall
Write-Host "Configuring Windows Firewall..." -ForegroundColor Yellow
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True

# Enable PowerShell Remoting
Write-Host "Enabling PowerShell Remoting..." -ForegroundColor Yellow
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force

# Configure Windows Update
Write-Host "Configuring Windows Update..." -ForegroundColor Yellow
Install-Module PSWindowsUpdate -Force -SkipPublisherCheck
Import-Module PSWindowsUpdate
Add-WUServiceManager -ServiceID "7971f918-a847-4430-9279-4a52d1efe18d" -AddServiceFlag 7

# Install common features
Write-Host "Installing common Windows features..." -ForegroundColor Yellow
$features = @(
    "NET-Framework-Core",
    "NET-Framework-45-Core",
    "PowerShell-ISE",
    "Telnet-Client",
    "Windows-Server-Backup"
)

foreach ($feature in $features) {
    Install-WindowsFeature -Name $feature -IncludeManagementTools
}

# Join domain
Write-Host "Joining domain $DomainName..." -ForegroundColor Yellow
$credential = New-Object System.Management.Automation.PSCredential `
    ("$DomainName\Administrator", $DomainAdminPassword)

Add-Computer -DomainName $DomainName -Credential $credential -Force

Write-Host "Initial configuration complete. Server will restart in 10 seconds..." -ForegroundColor Green
Start-Sleep -Seconds 10
Restart-Computer -Force

Advanced PowerShell Profile Configuration

# Microsoft.PowerShell_profile.ps1
# Place in $HOME\Documents\WindowsPowerShell\

# Set console appearance
$host.UI.RawUI.WindowTitle = "PowerShell - $env:COMPUTERNAME"
$host.UI.RawUI.BackgroundColor = "Black"
$host.UI.RawUI.ForegroundColor = "Green"

# Custom prompt
function prompt {
    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = [Security.Principal.WindowsPrincipal] $identity
    $adminRole = [Security.Principal.WindowsBuiltInRole]::Administrator

    $prefix = ""
    if ($principal.IsInRole($adminRole)) {
        $prefix = "[ADMIN] "
    }

    Write-Host "$prefix" -NoNewline -ForegroundColor Red
    Write-Host "$env:USERNAME@$env:COMPUTERNAME" -NoNewline -ForegroundColor Yellow
    Write-Host " $(Get-Location)" -NoNewline -ForegroundColor Cyan
    Write-Host " >" -NoNewline -ForegroundColor White
    return " "
}

# Aliases and functions
Set-Alias -Name npp -Value "C:\Program Files\Notepad++\notepad++.exe"
Set-Alias -Name grep -Value Select-String

function Get-SystemInfo {
    $os = Get-CimInstance Win32_OperatingSystem
    $cs = Get-CimInstance Win32_ComputerSystem
    $cpu = Get-CimInstance Win32_Processor
    $mem = Get-CimInstance Win32_PhysicalMemory

    [PSCustomObject]@{
        ComputerName = $cs.Name
        Domain = $cs.Domain
        OS = $os.Caption
        Version = $os.Version
        Architecture = $os.OSArchitecture
        CPUName = $cpu.Name
        CPUCores = $cpu.NumberOfCores
        TotalMemoryGB = [math]::Round(($mem | Measure-Object -Property Capacity -Sum).Sum / 1GB, 2)
        LastBoot = $os.LastBootUpTime
    }
}

function Get-DiskSpace {
    Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | 
    Select-Object DeviceID, 
        @{n='Size(GB)';e={[math]::Round($_.Size/1GB,2)}},
        @{n='Free(GB)';e={[math]::Round($_.FreeSpace/1GB,2)}},
        @{n='Used(GB)';e={[math]::Round(($_.Size-$_.FreeSpace)/1GB,2)}},
        @{n='%Used';e={[math]::Round((($_.Size-$_.FreeSpace)/$_.Size)*100,2)}}
}

# Load modules
Import-Module ActiveDirectory -ErrorAction SilentlyContinue
Import-Module ServerManager -ErrorAction SilentlyContinue

# Set location to scripts directory
Set-Location C:\Scripts

Active Directory Automation

User and Group Management

# Active Directory User Management Module
# Save as ADUserManagement.psm1

function New-BulkADUsers {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$CSVPath,

        [Parameter(Mandatory=$true)]
        [string]$DefaultPassword,

        [Parameter(Mandatory=$false)]
        [string]$LogPath = "C:\Logs\ADUserCreation.log"
    )

    # Initialize logging
    Start-Transcript -Path $LogPath -Append

    # Import CSV
    $users = Import-Csv -Path $CSVPath

    foreach ($user in $users) {
        try {
            # Generate username
            $username = ($user.FirstName.Substring(0,1) + $user.LastName).ToLower()
            $upn = "$username@$((Get-ADDomain).DNSRoot)"

            # Check if user exists
            if (Get-ADUser -Filter "SamAccountName -eq '$username'" -ErrorAction SilentlyContinue) {
                Write-Warning "User $username already exists. Skipping..."
                continue
            }

            # Create user parameters
            $userParams = @{
                Name = "$($user.FirstName) $($user.LastName)"
                GivenName = $user.FirstName
                Surname = $user.LastName
                SamAccountName = $username
                UserPrincipalName = $upn
                DisplayName = "$($user.FirstName) $($user.LastName)"
                Description = $user.JobTitle
                Department = $user.Department
                Company = $user.Company
                Office = $user.Office
                EmailAddress = "$username@company.com"
                AccountPassword = (ConvertTo-SecureString $DefaultPassword -AsPlainText -Force)
                Enabled = $true
                ChangePasswordAtLogon = $true
                Path = "OU=$($user.Department),OU=Users,DC=company,DC=local"
            }

            # Create the user
            New-ADUser @userParams
            Write-Host "Created user: $username" -ForegroundColor Green

            # Add to groups
            if ($user.Groups) {
                $groups = $user.Groups -split ';'
                foreach ($group in $groups) {
                    Add-ADGroupMember -Identity $group.Trim() -Members $username
                    Write-Host "  Added to group: $group" -ForegroundColor Cyan
                }
            }

            # Set additional attributes
            Set-ADUser -Identity $username -Replace @{
                'msExchHideFromAddressLists' = $false
                'extensionAttribute1' = $user.EmployeeID
                'extensionAttribute2' = $user.CostCenter
            }

        } catch {
            Write-Error "Failed to create user $($user.FirstName) $($user.LastName): $_"
        }
    }

    Stop-Transcript
}

function Get-ADUserReport {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [int]$InactiveDays = 90,

        [Parameter(Mandatory=$false)]
        [string]$ExportPath = "C:\Reports\ADUserReport_$(Get-Date -Format 'yyyyMMdd').csv"
    )

    $inactiveDate = (Get-Date).AddDays(-$InactiveDays)

    $users = Get-ADUser -Filter * -Properties * | Select-Object `
        Name,
        SamAccountName,
        EmailAddress,
        Department,
        Title,
        Manager,
        Enabled,
        LastLogonDate,
        PasswordLastSet,
        PasswordNeverExpires,
        AccountExpirationDate,
        @{n='Groups';e={(Get-ADPrincipalGroupMembership $_.SamAccountName | 
            Where-Object {$_.Name -ne 'Domain Users'}).Name -join ';'}},
        @{n='IsInactive';e={$_.LastLogonDate -lt $inactiveDate}},
        @{n='DaysSinceLastLogon';e={
            if ($_.LastLogonDate) {
                (New-TimeSpan -Start $_.LastLogonDate -End (Get-Date)).Days
            } else { "Never" }
        }}

    $users | Export-Csv -Path $ExportPath -NoTypeInformation

    # Generate summary
    $summary = @{
        TotalUsers = $users.Count
        EnabledUsers = ($users | Where-Object {$_.Enabled}).Count
        DisabledUsers = ($users | Where-Object {-not $_.Enabled}).Count
        InactiveUsers = ($users | Where-Object {$_.IsInactive}).Count
        PasswordNeverExpires = ($users | Where-Object {$_.PasswordNeverExpires}).Count
    }

    Write-Host "`nActive Directory User Summary:" -ForegroundColor Yellow
    $summary.GetEnumerator() | ForEach-Object {
        Write-Host "$($_.Key): $($_.Value)" -ForegroundColor Cyan
    }

    return $users
}

function Set-BulkADUserAttributes {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$SearchBase,

        [Parameter(Mandatory=$true)]
        [hashtable]$Attributes
    )

    $users = Get-ADUser -SearchBase $SearchBase -Filter *

    foreach ($user in $users) {
        try {
            Set-ADUser -Identity $user -Replace $Attributes
            Write-Host "Updated user: $($user.Name)" -ForegroundColor Green
        } catch {
            Write-Error "Failed to update user $($user.Name): $_"
        }
    }
}

Export-ModuleMember -Function *

Group Policy Automation

# Group Policy Management Automation

function New-StandardGPO {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$GPOName,

        [Parameter(Mandatory=$true)]
        [ValidateSet('Computer','User','Both')]
        [string]$PolicyType,

        [Parameter(Mandatory=$true)]
        [string]$LinkedOU
    )

    try {
        # Create new GPO
        $gpo = New-GPO -Name $GPOName -Comment "Created by PowerShell automation on $(Get-Date)"

        # Configure computer policies
        if ($PolicyType -in 'Computer','Both') {
            # Security settings
            Set-GPRegistryValue -Name $GPOName -Key "HKLM\Software\Policies\Microsoft\Windows\System" `
                -ValueName "EnableSmartScreen" -Type DWord -Value 2

            Set-GPRegistryValue -Name $GPOName -Key "HKLM\Software\Policies\Microsoft\Windows Defender" `
                -ValueName "DisableAntiSpyware" -Type DWord -Value 0

            # Windows Update settings
            Set-GPRegistryValue -Name $GPOName -Key "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU" `
                -ValueName "NoAutoUpdate" -Type DWord -Value 0

            Set-GPRegistryValue -Name $GPOName -Key "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate\AU" `
                -ValueName "AUOptions" -Type DWord -Value 4

            # Power settings
            Set-GPRegistryValue -Name $GPOName -Key "HKLM\Software\Policies\Microsoft\Power\PowerSettings\0e796bdb-100d-47d6-a2d5-f7d2daa51f51" `
                -ValueName "DCSettingIndex" -Type DWord -Value 0
        }

        # Configure user policies
        if ($PolicyType -in 'User','Both') {
            # Desktop settings
            Set-GPRegistryValue -Name $GPOName -Key "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System" `
                -ValueName "Wallpaper" -Type String -Value "C:\Windows\Web\Wallpaper\Corporate.jpg"

            # Internet Explorer settings
            Set-GPRegistryValue -Name $GPOName -Key "HKCU\Software\Policies\Microsoft\Internet Explorer\Main" `
                -ValueName "Start Page" -Type String -Value "https://intranet.company.com"

            # Control Panel restrictions
            Set-GPRegistryValue -Name $GPOName -Key "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer" `
                -ValueName "NoControlPanel" -Type DWord -Value 0
        }

        # Link GPO to OU
        New-GPLink -Name $GPOName -Target $LinkedOU -LinkEnabled Yes

        Write-Host "GPO '$GPOName' created and linked to $LinkedOU" -ForegroundColor Green

    } catch {
        Write-Error "Failed to create GPO: $_"
    }
}

function Backup-AllGPOs {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$BackupPath
    )

    $backupFolder = Join-Path $BackupPath "GPO_Backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
    New-Item -ItemType Directory -Path $backupFolder -Force | Out-Null

    $gpos = Get-GPO -All

    foreach ($gpo in $gpos) {
        try {
            $backupInfo = Backup-GPO -Name $gpo.DisplayName -Path $backupFolder
            Write-Host "Backed up GPO: $($gpo.DisplayName)" -ForegroundColor Green

            # Export additional information
            $gpoInfo = @{
                Name = $gpo.DisplayName
                Id = $gpo.Id
                CreationTime = $gpo.CreationTime
                ModificationTime = $gpo.ModificationTime
                WmiFilter = $gpo.WmiFilter
                Description = $gpo.Description
                BackupId = $backupInfo.Id
            }

            $gpoInfo | Export-Clixml -Path (Join-Path $backupFolder "$($gpo.Id).xml")

        } catch {
            Write-Error "Failed to backup GPO $($gpo.DisplayName): $_"
        }
    }

    # Create HTML report
    $htmlReport = @"
<!DOCTYPE html>
<html>
<head>
    <title>GPO Backup Report</title>
    <style>
        body { font-family: Arial, sans-serif; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #4CAF50; color: white; }
        tr:nth-child(even) { background-color: #f2f2f2; }
    </style>
</head>
<body>
    <h1>Group Policy Backup Report</h1>
    <p>Backup Date: $(Get-Date)</p>
    <p>Total GPOs: $($gpos.Count)</p>
    <table>
        <tr>
            <th>GPO Name</th>
            <th>ID</th>
            <th>Created</th>
            <th>Modified</th>
            <th>Links</th>
        </tr>
"@

    foreach ($gpo in $gpos) {
        $links = Get-GPOReport -Name $gpo.DisplayName -ReportType Xml | 
            Select-Xml -XPath "//LinksTo/SOMPath" | 
            ForEach-Object { $_.Node.InnerText }

        $htmlReport += @"
        <tr>
            <td>$($gpo.DisplayName)</td>
            <td>$($gpo.Id)</td>
            <td>$($gpo.CreationTime)</td>
            <td>$($gpo.ModificationTime)</td>
            <td>$($links -join '<br>')</td>
        </tr>
"@
    }

    $htmlReport += @"
    </table>
</body>
</html>
"@

    $htmlReport | Out-File -FilePath (Join-Path $backupFolder "BackupReport.html")

    Write-Host "`nGPO backup completed. Report saved to: $backupFolder\BackupReport.html" -ForegroundColor Yellow
}

Server Role Automation

IIS Web Server Management

# IIS Automation Module

function Install-IISWithFeatures {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [string[]]$AdditionalFeatures = @()
    )

    $features = @(
        "IIS-WebServerRole",
        "IIS-WebServer",
        "IIS-CommonHttpFeatures",
        "IIS-HttpErrors",
        "IIS-HttpRedirect",
        "IIS-ApplicationDevelopment",
        "IIS-NetFxExtensibility45",
        "IIS-HealthAndDiagnostics",
        "IIS-HttpLogging",
        "IIS-Security",
        "IIS-RequestFiltering",
        "IIS-Performance",
        "IIS-WebServerManagementTools",
        "IIS-IIS6ManagementCompatibility",
        "IIS-Metabase",
        "IIS-ASPNET45",
        "IIS-NetFx4Extended-ASPNET45",
        "IIS-ManagementConsole"
    ) + $AdditionalFeatures

    foreach ($feature in $features) {
        Enable-WindowsOptionalFeature -Online -FeatureName $feature -All -NoRestart
        Write-Host "Installed feature: $feature" -ForegroundColor Green
    }

    # Install IIS Management Tools
    Import-Module WebAdministration

    Write-Host "IIS installation completed" -ForegroundColor Yellow
}

function New-IISWebsite {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,

        [Parameter(Mandatory=$true)]
        [string]$PhysicalPath,

        [Parameter(Mandatory=$true)]
        [int]$Port,

        [Parameter(Mandatory=$false)]
        [string]$HostHeader = "",

        [Parameter(Mandatory=$false)]
        [string]$AppPoolName = $SiteName,

        [Parameter(Mandatory=$false)]
        [ValidateSet('v2.0','v4.0')]
        [string]$RuntimeVersion = 'v4.0',

        [Parameter(Mandatory=$false)]
        [ValidateSet('Integrated','Classic')]
        [string]$PipelineMode = 'Integrated'
    )

    Import-Module WebAdministration

    try {
        # Create physical directory
        if (-not (Test-Path $PhysicalPath)) {
            New-Item -ItemType Directory -Path $PhysicalPath -Force
            Write-Host "Created directory: $PhysicalPath" -ForegroundColor Green
        }

        # Create application pool
        if (-not (Test-Path "IIS:\AppPools\$AppPoolName")) {
            New-WebAppPool -Name $AppPoolName
            Set-ItemProperty -Path "IIS:\AppPools\$AppPoolName" -Name managedRuntimeVersion -Value $RuntimeVersion
            Set-ItemProperty -Path "IIS:\AppPools\$AppPoolName" -Name managedPipelineMode -Value $PipelineMode
            Set-ItemProperty -Path "IIS:\AppPools\$AppPoolName" -Name enable32BitAppOnWin64 -Value $false

            # Configure recycling
            Set-ItemProperty -Path "IIS:\AppPools\$AppPoolName" -Name recycling.periodicRestart.time -Value "00:00:00"
            Set-ItemProperty -Path "IIS:\AppPools\$AppPoolName" -Name recycling.periodicRestart.schedule -Value @{value="03:00:00"}

            Write-Host "Created application pool: $AppPoolName" -ForegroundColor Green
        }

        # Create website
        $binding = @{protocol="http";bindingInformation="*:${Port}:${HostHeader}"}
        New-Website -Name $SiteName -PhysicalPath $PhysicalPath -Port $Port -ApplicationPool $AppPoolName

        # Set permissions
        $acl = Get-Acl $PhysicalPath
        $permission = "IIS_IUSRS","ReadAndExecute","ContainerInherit,ObjectInherit","None","Allow"
        $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
        $acl.SetAccessRule($accessRule)
        Set-Acl $PhysicalPath $acl

        # Configure website settings
        Set-WebConfigurationProperty -Filter "/system.webServer/directoryBrowse" `
            -PSPath "IIS:\Sites\$SiteName" -Name "enabled" -Value $false

        Set-WebConfigurationProperty -Filter "/system.webServer/security/requestFiltering" `
            -PSPath "IIS:\Sites\$SiteName" -Name "removeServerHeader" -Value $true

        # Create default document
        $defaultDoc = @"
<!DOCTYPE html>
<html>
<head>
    <title>$SiteName</title>
</head>
<body>
    <h1>Welcome to $SiteName</h1>
    <p>Site is running on IIS</p>
</body>
</html>
"@
        $defaultDoc | Out-File -FilePath "$PhysicalPath\index.html" -Encoding UTF8

        Write-Host "Website '$SiteName' created successfully" -ForegroundColor Yellow

    } catch {
        Write-Error "Failed to create website: $_"
    }
}

function Set-IISSecurityHeaders {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName
    )

    $headers = @{
        "X-Content-Type-Options" = "nosniff"
        "X-Frame-Options" = "SAMEORIGIN"
        "X-XSS-Protection" = "1; mode=block"
        "Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
        "Content-Security-Policy" = "default-src 'self'"
        "Referrer-Policy" = "strict-origin-when-cross-origin"
    }

    foreach ($header in $headers.GetEnumerator()) {
        Add-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" `
            -Filter "system.webServer/httpProtocol/customHeaders" `
            -Name "." -Value @{name=$header.Key;value=$header.Value}

        Write-Host "Added security header: $($header.Key)" -ForegroundColor Green
    }
}

File Server Management

# File Server Resource Manager Automation

function Configure-FileServer {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$SharePath,

        [Parameter(Mandatory=$true)]
        [string]$ShareName,

        [Parameter(Mandatory=$false)]
        [string]$Description = "Managed by PowerShell"
    )

    # Install File Server features
    $features = @(
        "FS-FileServer",
        "FS-Resource-Manager",
        "FS-DFS-Namespace",
        "FS-DFS-Replication",
        "FS-VSS-Agent"
    )

    foreach ($feature in $features) {
        Install-WindowsFeature -Name $feature -IncludeManagementTools
    }

    # Create share directory
    if (-not (Test-Path $SharePath)) {
        New-Item -ItemType Directory -Path $SharePath -Force
    }

    # Create SMB share
    New-SmbShare -Name $ShareName `
        -Path $SharePath `
        -Description $Description `
        -FullAccess "Domain Admins" `
        -ChangeAccess "Domain Users" `
        -FolderEnumerationMode AccessBased `
        -CachingMode Documents `
        -EncryptData $true

    # Configure NTFS permissions
    $acl = Get-Acl $SharePath

    # Remove inherited permissions
    $acl.SetAccessRuleProtection($true, $false)

    # Add specific permissions
    $permissions = @(
        @{Identity="Domain Admins"; Rights="FullControl"; Type="Allow"},
        @{Identity="Domain Users"; Rights="ReadAndExecute,Write"; Type="Allow"},
        @{Identity="CREATOR OWNER"; Rights="FullControl"; Type="Allow"}
    )

    foreach ($perm in $permissions) {
        $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
            $perm.Identity,
            $perm.Rights,
            "ContainerInherit,ObjectInherit",
            "None",
            $perm.Type
        )
        $acl.AddAccessRule($accessRule)
    }

    Set-Acl -Path $SharePath -AclObject $acl

    # Configure File Screen
    New-FsrmFileScreen -Path $SharePath `
        -Template "Block Executable Files" `
        -Description "Blocks potentially dangerous file types"

    # Set up quota
    New-FsrmQuota -Path $SharePath `
        -Template "100 MB Limit" `
        -Description "User folder quota"

    Write-Host "File share '$ShareName' configured successfully" -ForegroundColor Green
}

function New-UserHomeDirectories {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$BasePath,

        [Parameter(Mandatory=$false)]
        [string]$SearchBase = (Get-ADDomain).DistinguishedName
    )

    # Get all AD users
    $users = Get-ADUser -SearchBase $SearchBase -Filter * -Properties HomeDirectory, HomeDrive

    foreach ($user in $users) {
        $homePath = Join-Path $BasePath $user.SamAccountName

        # Create home directory
        if (-not (Test-Path $homePath)) {
            New-Item -ItemType Directory -Path $homePath -Force

            # Set permissions
            $acl = Get-Acl $homePath
            $acl.SetAccessRuleProtection($true, $false)

            # Domain Admins - Full Control
            $adminRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                "Domain Admins",
                "FullControl",
                "ContainerInherit,ObjectInherit",
                "None",
                "Allow"
            )
            $acl.AddAccessRule($adminRule)

            # User - Full Control
            $userRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                $user.SamAccountName,
                "FullControl",
                "ContainerInherit,ObjectInherit",
                "None",
                "Allow"
            )
            $acl.AddAccessRule($userRule)

            Set-Acl -Path $homePath -AclObject $acl

            # Update AD user properties
            Set-ADUser -Identity $user -HomeDirectory "\\$env:COMPUTERNAME\Users$\$($user.SamAccountName)" -HomeDrive "H:"

            Write-Host "Created home directory for: $($user.SamAccountName)" -ForegroundColor Green
        }
    }
}

System Monitoring and Maintenance

Performance Monitoring

# System Performance Monitoring Module

function Start-PerformanceMonitoring {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [int]$DurationMinutes = 60,

        [Parameter(Mandatory=$false)]
        [int]$SampleInterval = 5,

        [Parameter(Mandatory=$false)]
        [string]$OutputPath = "C:\PerfLogs\PerfMon_$(Get-Date -Format 'yyyyMMdd_HHmmss').blg"
    )

    # Create output directory
    $outputDir = Split-Path $OutputPath -Parent
    if (-not (Test-Path $outputDir)) {
        New-Item -ItemType Directory -Path $outputDir -Force
    }

    # Define counters
    $counters = @(
        "\Processor(_Total)\% Processor Time",
        "\Memory\Available MBytes",
        "\Memory\Pages/sec",
        "\PhysicalDisk(_Total)\Disk Reads/sec",
        "\PhysicalDisk(_Total)\Disk Writes/sec",
        "\PhysicalDisk(_Total)\Avg. Disk Queue Length",
        "\Network Interface(*)\Bytes Total/sec",
        "\System\Processor Queue Length",
        "\Process(_Total)\Handle Count",
        "\Process(_Total)\Thread Count"
    )

    # Create data collector set
    $datacollectorset = New-Object -COM Pla.DataCollectorSet
    $datacollectorset.DisplayName = "PowerShell Performance Monitor"
    $datacollectorset.Duration = $DurationMinutes * 60
    $datacollectorset.SubdirectoryFormat = 1
    $datacollectorset.SubdirectoryFormatPattern = "yyyy-MM-dd"
    $datacollectorset.RootPath = Split-Path $OutputPath -Parent

    $datacollector = $datacollectorset.DataCollectors.CreateDataCollector(0)
    $datacollector.FileName = [System.IO.Path]::GetFileNameWithoutExtension($OutputPath)
    $datacollector.FileNameFormat = 0
    $datacollector.FileNameFormatPattern = ""
    $datacollector.SampleInterval = $SampleInterval
    $datacollector.LogFileFormat = 3 # Binary

    $counters | ForEach-Object { $datacollector.PerformanceCounters.Add($_) | Out-Null }

    $datacollectorset.DataCollectors.Add($datacollector)

    try {
        $datacollectorset.Commit("TempPerfMon", $null, 0x0003) | Out-Null
        $datacollectorset.Start($false)

        Write-Host "Performance monitoring started. Duration: $DurationMinutes minutes" -ForegroundColor Green
        Write-Host "Output file: $OutputPath" -ForegroundColor Yellow

    } catch {
        Write-Error "Failed to start performance monitoring: $_"
    }
}

function Get-SystemHealthReport {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [string]$OutputPath = "C:\Reports\SystemHealth_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
    )

    # Gather system information
    $os = Get-CimInstance Win32_OperatingSystem
    $cs = Get-CimInstance Win32_ComputerSystem
    $cpu = Get-CimInstance Win32_Processor
    $mem = Get-CimInstance Win32_PhysicalMemory
    $disk = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3"

    # Get event log errors
    $criticalEvents = Get-EventLog -LogName System -EntryType Error -Newest 50
    $appErrors = Get-EventLog -LogName Application -EntryType Error -Newest 50

    # Get service status
    $stoppedServices = Get-Service | Where-Object {
        $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running'
    }

    # Generate HTML report
    $html = @"
<!DOCTYPE html>
<html>
<head>
    <title>System Health Report - $($cs.Name)</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h1, h2 { color: #333; }
        table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #4CAF50; color: white; }
        tr:nth-child(even) { background-color: #f2f2f2; }
        .warning { background-color: #ff9800; color: white; }
        .error { background-color: #f44336; color: white; }
        .success { background-color: #4CAF50; color: white; }
    </style>
</head>
<body>
    <h1>System Health Report</h1>
    <p>Generated: $(Get-Date)</p>

    <h2>System Information</h2>
    <table>
        <tr><th>Property</th><th>Value</th></tr>
        <tr><td>Computer Name</td><td>$($cs.Name)</td></tr>
        <tr><td>Domain</td><td>$($cs.Domain)</td></tr>
        <tr><td>Operating System</td><td>$($os.Caption)</td></tr>
        <tr><td>Version</td><td>$($os.Version)</td></tr>
        <tr><td>Last Boot Time</td><td>$($os.LastBootUpTime)</td></tr>
        <tr><td>Uptime</td><td>$((Get-Date) - $os.LastBootUpTime)</td></tr>
    </table>

    <h2>Hardware Information</h2>
    <table>
        <tr><th>Component</th><th>Details</th></tr>
        <tr><td>CPU</td><td>$($cpu.Name) ($($cpu.NumberOfCores) cores)</td></tr>
        <tr><td>Total Memory</td><td>$([math]::Round(($mem | Measure-Object -Property Capacity -Sum).Sum / 1GB, 2)) GB</td></tr>
        <tr><td>Available Memory</td><td>$([math]::Round($os.FreePhysicalMemory / 1MB, 2)) GB</td></tr>
    </table>

    <h2>Disk Space</h2>
    <table>
        <tr><th>Drive</th><th>Total (GB)</th><th>Free (GB)</th><th>Used %</th><th>Status</th></tr>
"@

    foreach ($d in $disk) {
        $totalGB = [math]::Round($d.Size / 1GB, 2)
        $freeGB = [math]::Round($d.FreeSpace / 1GB, 2)
        $usedPercent = [math]::Round((($d.Size - $d.FreeSpace) / $d.Size) * 100, 2)

        $statusClass = if ($usedPercent -gt 90) { "error" } 
                       elseif ($usedPercent -gt 80) { "warning" } 
                       else { "success" }

        $html += @"
        <tr>
            <td>$($d.DeviceID)</td>
            <td>$totalGB</td>
            <td>$freeGB</td>
            <td class="$statusClass">$usedPercent%</td>
            <td class="$statusClass">$(if ($usedPercent -gt 90) { "Critical" } elseif ($usedPercent -gt 80) { "Warning" } else { "OK" })</td>
        </tr>
"@
    }

    $html += @"
    </table>

    <h2>Stopped Services</h2>
"@

    if ($stoppedServices) {
        $html += @"
    <table>
        <tr><th>Service Name</th><th>Display Name</th><th>Start Type</th></tr>
"@
        foreach ($service in $stoppedServices) {
            $html += @"
        <tr>
            <td>$($service.Name)</td>
            <td>$($service.DisplayName)</td>
            <td>$($service.StartType)</td>
        </tr>
"@
        }
        $html += "</table>"
    } else {
        $html += "<p>All automatic services are running.</p>"
    }

    $html += @"

    <h2>Recent System Errors (Last 10)</h2>
    <table>
        <tr><th>Time</th><th>Source</th><th>Message</th></tr>
"@

    foreach ($event in ($criticalEvents | Select-Object -First 10)) {
        $html += @"
        <tr>
            <td>$($event.TimeGenerated)</td>
            <td>$($event.Source)</td>
            <td>$($event.Message.Substring(0, [Math]::Min(200, $event.Message.Length)))...</td>
        </tr>
"@
    }

    $html += @"
    </table>
</body>
</html>
"@

    # Save report
    $outputDir = Split-Path $OutputPath -Parent
    if (-not (Test-Path $outputDir)) {
        New-Item -ItemType Directory -Path $outputDir -Force
    }

    $html | Out-File -FilePath $OutputPath -Encoding UTF8

    Write-Host "System health report generated: $OutputPath" -ForegroundColor Green

    # Open report in browser
    Start-Process $OutputPath
}

Security Automation

Security Baseline Configuration

# Windows Server Security Hardening Module

function Apply-SecurityBaseline {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [ValidateSet('Basic','Moderate','High')]
        [string]$SecurityLevel = 'Moderate'
    )

    Write-Host "Applying $SecurityLevel security baseline..." -ForegroundColor Yellow

    # Account Policies
    Write-Host "Configuring account policies..." -ForegroundColor Cyan

    # Password Policy
    $passwordPolicy = @{
        'MinimumPasswordLength' = if ($SecurityLevel -eq 'High') { 15 } elseif ($SecurityLevel -eq 'Moderate') { 12 } else { 8 }
        'PasswordComplexity' = $true
        'MaximumPasswordAge' = if ($SecurityLevel -eq 'High') { 60 } elseif ($SecurityLevel -eq 'Moderate') { 90 } else { 180 }
        'MinimumPasswordAge' = 1
        'PasswordHistorySize' = if ($SecurityLevel -eq 'High') { 24 } elseif ($SecurityLevel -eq 'Moderate') { 12 } else { 6 }
    }

    # Apply using secedit
    $secTemplate = @"
[Unicode]
Unicode=yes
[System Access]
MinimumPasswordAge = $($passwordPolicy.MinimumPasswordAge)
MaximumPasswordAge = $($passwordPolicy.MaximumPasswordAge)
MinimumPasswordLength = $($passwordPolicy.MinimumPasswordLength)
PasswordComplexity = $(if ($passwordPolicy.PasswordComplexity) { 1 } else { 0 })
PasswordHistorySize = $($passwordPolicy.PasswordHistorySize)
LockoutBadCount = $(if ($SecurityLevel -eq 'High') { 3 } elseif ($SecurityLevel -eq 'Moderate') { 5 } else { 10 })
LockoutDuration = 30
ResetLockoutCount = 30
"@

    $tempFile = [System.IO.Path]::GetTempFileName()
    $secTemplate | Out-File -FilePath $tempFile -Encoding Unicode

    secedit /configure /db secedit.sdb /cfg $tempFile /quiet
    Remove-Item $tempFile -Force

    # Audit Policy
    Write-Host "Configuring audit policies..." -ForegroundColor Cyan

    $auditPolicies = @(
        "Account Logon:Success,Failure",
        "Account Management:Success,Failure",
        "Logon/Logoff:Success,Failure",
        "Object Access:Success,Failure",
        "Policy Change:Success,Failure",
        "Privilege Use:Success,Failure",
        "System:Success,Failure"
    )

    foreach ($policy in $auditPolicies) {
        $parts = $policy -split ':'
        auditpol /set /subcategory:$($parts[0]) /success:enable /failure:enable
    }

    # Windows Firewall
    Write-Host "Configuring Windows Firewall..." -ForegroundColor Cyan

    Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True
    Set-NetFirewallProfile -Profile Public -DefaultInboundAction Block
    Set-NetFirewallProfile -Profile Private -DefaultInboundAction Block

    # Security Options
    Write-Host "Configuring security options..." -ForegroundColor Cyan

    # Disable Guest account
    Disable-LocalUser -Name "Guest" -ErrorAction SilentlyContinue

    # Rename Administrator account
    if ($SecurityLevel -in 'Moderate','High') {
        Rename-LocalUser -Name "Administrator" -NewName "LocalAdmin" -ErrorAction SilentlyContinue
    }

    # Registry security settings
    $regSettings = @{
        # Disable autorun
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer' = @{
            'NoDriveTypeAutoRun' = 255
        }
        # Enable UAC
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' = @{
            'EnableLUA' = 1
            'ConsentPromptBehaviorAdmin' = if ($SecurityLevel -eq 'High') { 2 } else { 5 }
            'PromptOnSecureDesktop' = 1
        }
        # Network security
        'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' = @{
            'RequireSecuritySignature' = 1
            'EnableSecuritySignature' = 1
            'RestrictNullSessAccess' = 1
        }
    }

    foreach ($path in $regSettings.Keys) {
        if (-not (Test-Path $path)) {
            New-Item -Path $path -Force | Out-Null
        }

        foreach ($name in $regSettings[$path].Keys) {
            Set-ItemProperty -Path $path -Name $name -Value $regSettings[$path][$name]
        }
    }

    Write-Host "Security baseline applied successfully!" -ForegroundColor Green
}

function Get-SecurityAssessment {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [string]$OutputPath = "C:\Reports\SecurityAssessment_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
    )

    $assessment = @()

    # Check Windows Updates
    $updates = Get-HotFix | Sort-Object -Property InstalledOn -Descending | Select-Object -First 1
    $daysSinceUpdate = (New-TimeSpan -Start $updates.InstalledOn -End (Get-Date)).Days

    $assessment += [PSCustomObject]@{
        Category = "Windows Updates"
        Check = "Last Update"
        Status = if ($daysSinceUpdate -lt 30) { "Pass" } else { "Fail" }
        Details = "Last update: $($updates.InstalledOn) ($daysSinceUpdate days ago)"
    }

    # Check password policy
    $passPolicy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
    if ($passPolicy) {
        $assessment += [PSCustomObject]@{
            Category = "Password Policy"
            Check = "Minimum Length"
            Status = if ($passPolicy.MinPasswordLength -ge 12) { "Pass" } else { "Fail" }
            Details = "Minimum length: $($passPolicy.MinPasswordLength)"
        }
    }

    # Check firewall status
    $fwProfiles = Get-NetFirewallProfile
    foreach ($profile in $fwProfiles) {
        $assessment += [PSCustomObject]@{
            Category = "Firewall"
            Check = "$($profile.Name) Profile"
            Status = if ($profile.Enabled) { "Pass" } else { "Fail" }
            Details = "Enabled: $($profile.Enabled)"
        }
    }

    # Check antivirus status
    $av = Get-MpComputerStatus -ErrorAction SilentlyContinue
    if ($av) {
        $assessment += [PSCustomObject]@{
            Category = "Antivirus"
            Check = "Real-time Protection"
            Status = if ($av.RealTimeProtectionEnabled) { "Pass" } else { "Fail" }
            Details = "Enabled: $($av.RealTimeProtectionEnabled)"
        }
    }

    # Generate HTML report
    $html = @"
<!DOCTYPE html>
<html>
<head>
    <title>Security Assessment Report</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #4CAF50; color: white; }
        .pass { background-color: #4CAF50; color: white; }
        .fail { background-color: #f44336; color: white; }
    </style>
</head>
<body>
    <h1>Security Assessment Report</h1>
    <p>Generated: $(Get-Date)</p>
    <p>Server: $env:COMPUTERNAME</p>

    <table>
        <tr>
            <th>Category</th>
            <th>Check</th>
            <th>Status</th>
            <th>Details</th>
        </tr>
"@

    foreach ($item in $assessment) {
        $statusClass = if ($item.Status -eq 'Pass') { 'pass' } else { 'fail' }
        $html += @"
        <tr>
            <td>$($item.Category)</td>
            <td>$($item.Check)</td>
            <td class="$statusClass">$($item.Status)</td>
            <td>$($item.Details)</td>
        </tr>
"@
    }

    $html += @"
    </table>

    <h2>Summary</h2>
    <p>Total Checks: $($assessment.Count)</p>
    <p>Passed: $(($assessment | Where-Object {$_.Status -eq 'Pass'}).Count)</p>
    <p>Failed: $(($assessment | Where-Object {$_.Status -eq 'Fail'}).Count)</p>
</body>
</html>
"@

    $html | Out-File -FilePath $OutputPath -Encoding UTF8
    Write-Host "Security assessment report generated: $OutputPath" -ForegroundColor Green

    # Display summary
    $passed = ($assessment | Where-Object {$_.Status -eq 'Pass'}).Count
    $failed = ($assessment | Where-Object {$_.Status -eq 'Fail'}).Count
    $score = [math]::Round(($passed / $assessment.Count) * 100, 2)

    Write-Host "`nSecurity Score: $score%" -ForegroundColor $(if ($score -ge 80) { 'Green' } elseif ($score -ge 60) { 'Yellow' } else { 'Red' })
}

Backup and Recovery Automation

Automated Backup System

# Backup and Recovery Module

function Start-ServerBackup {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string[]]$BackupTargets,

        [Parameter(Mandatory=$true)]
        [string]$BackupDestination,

        [Parameter(Mandatory=$false)]
        [ValidateSet('Full','Incremental','Differential')]
        [string]$BackupType = 'Full',

        [Parameter(Mandatory=$false)]
        [switch]$IncludeSystemState,

        [Parameter(Mandatory=$false)]
        [switch]$IncludeBareMetalRecovery
    )

    # Install Windows Server Backup if needed
    $feature = Get-WindowsFeature -Name Windows-Server-Backup
    if ($feature.InstallState -ne 'Installed') {
        Install-WindowsFeature -Name Windows-Server-Backup -IncludeManagementTools
    }

    # Create backup policy
    $policy = New-WBPolicy

    # Add backup targets
    foreach ($target in $BackupTargets) {
        if (Test-Path $target) {
            $volume = Get-WBVolume -VolumePath $target
            Add-WBVolume -Policy $policy -Volume $volume
        } else {
            Write-Warning "Backup target not found: $target"
        }
    }

    # Add system state if requested
    if ($IncludeSystemState) {
        Add-WBSystemState -Policy $policy
    }

    # Add bare metal recovery if requested
    if ($IncludeBareMetalRecovery) {
        Add-WBBareMetalRecovery -Policy $policy
    }

    # Set backup destination
    if ($BackupDestination -match '^\\\\') {
        # Network backup
        $backupLocation = New-WBBackupTarget -NetworkPath $BackupDestination
    } else {
        # Local backup
        $backupLocation = New-WBBackupTarget -VolumePath $BackupDestination
    }

    Add-WBBackupTarget -Policy $policy -Target $backupLocation

    # Start backup
    Start-WBBackup -Policy $policy

    # Log backup details
    $logEntry = @{
        Timestamp = Get-Date
        BackupType = $BackupType
        Targets = $BackupTargets -join ';'
        Destination = $BackupDestination
        SystemState = $IncludeSystemState
        BareMetalRecovery = $IncludeBareMetalRecovery
    }

    $logEntry | Export-Csv -Path "C:\Logs\BackupHistory.csv" -Append -NoTypeInformation

    Write-Host "Backup completed successfully" -ForegroundColor Green
}

function New-BackupSchedule {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$TaskName,

        [Parameter(Mandatory=$true)]
        [string[]]$BackupTargets,

        [Parameter(Mandatory=$true)]
        [string]$BackupDestination,

        [Parameter(Mandatory=$false)]
        [string]$Schedule = "Daily",

        [Parameter(Mandatory=$false)]
        [DateTime]$StartTime = (Get-Date "02:00 AM")
    )

    # Create scheduled task action
    $scriptBlock = @"
Import-Module ServerBackupAutomation
Start-ServerBackup -BackupTargets @('$($BackupTargets -join "','")') -BackupDestination '$BackupDestination' -IncludeSystemState
"@

    $action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
        -Argument "-NoProfile -WindowStyle Hidden -Command `"$scriptBlock`""

    # Create trigger based on schedule
    switch ($Schedule) {
        "Daily" {
            $trigger = New-ScheduledTaskTrigger -Daily -At $StartTime
        }
        "Weekly" {
            $trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday,Wednesday,Friday -At $StartTime
        }
        "Monthly" {
            $trigger = New-ScheduledTaskTrigger -Monthly -DaysOfMonth 1,15 -At $StartTime
        }
    }

    # Set principal
    $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" `
        -LogonType ServiceAccount -RunLevel Highest

    # Register task
    Register-ScheduledTask -TaskName $TaskName `
        -Action $action `
        -Trigger $trigger `
        -Principal $principal `
        -Description "Automated backup task created by PowerShell"

    Write-Host "Backup schedule created: $TaskName" -ForegroundColor Green
}

function Test-BackupRecovery {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$BackupLocation,

        [Parameter(Mandatory=$true)]
        [string]$TestRestorePath
    )

    Write-Host "Starting backup recovery test..." -ForegroundColor Yellow

    # Get latest backup
    $backups = Get-WBBackupSet -BackupTarget (Get-WBBackupTarget -VolumePath $BackupLocation)
    $latestBackup = $backups | Sort-Object -Property BackupTime -Descending | Select-Object -First 1

    if (-not $latestBackup) {
        Write-Error "No backups found at $BackupLocation"
        return
    }

    Write-Host "Found backup from: $($latestBackup.BackupTime)" -ForegroundColor Cyan

    # Create test restore directory
    if (-not (Test-Path $TestRestorePath)) {
        New-Item -ItemType Directory -Path $TestRestorePath -Force
    }

    # Perform test restore of a small portion
    try {
        $testFiles = Get-WBBackupFile -BackupSet $latestBackup | Select-Object -First 10

        foreach ($file in $testFiles) {
            Start-WBFileRecovery -BackupSet $latestBackup `
                -SourcePath $file.Path `
                -TargetPath $TestRestorePath `
                -Option CreateSubdirectory `
                -Force
        }

        Write-Host "Recovery test completed successfully" -ForegroundColor Green

        # Verify restored files
        $restoredFiles = Get-ChildItem -Path $TestRestorePath -Recurse
        Write-Host "Restored $($restoredFiles.Count) files for verification" -ForegroundColor Cyan

        # Clean up test files
        Remove-Item -Path $TestRestorePath -Recurse -Force

    } catch {
        Write-Error "Recovery test failed: $_"
    }
}

Conclusion

PowerShell automation transforms Windows Server 2022 administration from reactive management to proactive orchestration. This comprehensive guide provides the foundation for automating every aspect of server management.

Next Steps

  • Implement PowerShell Desired State Configuration (DSC)
  • Explore Azure Automation for hybrid environments
  • Build custom PowerShell modules for your organization
  • Integrate with configuration management tools
  • Develop PowerShell-based monitoring solutions

Remember: Automation is about consistency, reliability, and scalability. Start small, test thoroughly, and gradually expand your automation footprint.