<#-- ===================================== Veeam Security & Compliance Analyzer Enforcement Script ===================================== This script provides reporting on the current status and enforces recommended security settings on a backup server. It is designed to be executed locally on the backup server. # PowerShell version 7.0+ is required. # If script execution is blocked, run this command first in PowerShell: # Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force Version: 1.93 (Updated on 25/02/2026) --#> #Requires -RunAsAdministrator #Requires -Version 7.0 # Validate environment before proceeding Write-Host "Validating environment..." -ForegroundColor Cyan # Check for Veeam Backup Service presence $backupService = Get-Service VeeamBackupSvc -ErrorAction SilentlyContinue if (-not $backupService) { Write-Host "ERROR: Veeam Backup Service not detected on this machine." -ForegroundColor Red Write-Host "Ensure you are running this script on the Veeam Backup Server itself." -ForegroundColor Red exit 1 } # Check if service is running if ($backupService.Status -ne "Running") { Write-Host "ERROR: Veeam Backup Service is not running (Status: $($backupService.Status))." -ForegroundColor Red Write-Host "Start the VeeamBackupSvc service and try again." -ForegroundColor Red exit 1 } # Import Veeam PowerShell module Write-Host "Importing Veeam PowerShell module..." -ForegroundColor Cyan $path = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') $env:PSModulePath += "$([System.IO.Path]::PathSeparator)$path" $veeamPSModule = Get-Module -ListAvailable | Where-Object { $_.Name -match "Veeam.Backup.PowerShell" } if (-not $veeamPSModule) { Write-Host "ERROR: Veeam PowerShell module not found. Is Veeam B&R installed correctly?" -ForegroundColor Red exit 1 } try { Import-Module $veeamPSModule.Path -DisableNameChecking -ErrorAction Stop Write-Host "Veeam PowerShell module imported successfully" -ForegroundColor Green } catch { Write-Host "ERROR: Failed to import Veeam PowerShell module" -ForegroundColor Red Write-Host "Error details: $($_.Exception.Message)" -ForegroundColor Red exit 1 } # This function collects current status and prints out a compliance report function Get-VBRComplianceReport { Write-host "Initiating Analyzer and collecting compliance status..." -ForegroundColor Cyan # Trigger S&CA session $analyzerJob = Start-VBRSecurityComplianceAnalyzer # Note: Results retrieved via retry loop instead # Wait for analyzer job to complete (max 60 seconds) - normally takes about ~10 seconds $timeout = 60 $elapsed = 0 Write-Host "Waiting for analyzer to complete..." -NoNewline while ($analyzerJob.IsCompleted -eq $false -and $elapsed -lt $timeout) { Start-Sleep -Seconds 2 $elapsed += 2 Write-Host "." -NoNewline # Re-query the job status $analyzerJob = Get-VBRSession | Where-Object { $_.Id -eq $analyzerJob.Id } | Select-Object -First 1 } if ($analyzerJob.IsCompleted) { Write-Host " Done" -ForegroundColor Green } else { Write-Host "" } # Retrieve analyzer results with retry logic (up to 5 attempts with 15-second delays) Write-Host "Retrieving compliance results..." -ForegroundColor Cyan $AnalyzerResult = Get-VBRSecurityComplianceAnalyzerResults $resultCount = ($AnalyzerResult | Measure-Object).Count if ($resultCount -eq 0) { for ($retryCount = 1; $retryCount -le 5; $retryCount++) { Write-Host " Waiting for results (attempt $retryCount/5)..." -ForegroundColor Cyan Start-Sleep -Seconds 15 $AnalyzerResult = Get-VBRSecurityComplianceAnalyzerResults $resultCount = ($AnalyzerResult | Measure-Object).Count if ($resultCount -gt 0) { Write-Host " Results retrieved successfully ($resultCount items)" -ForegroundColor Green break } } if ($resultCount -eq 0) { Write-Host "ERROR: Unable to retrieve analyzer results after multiple attempts." -ForegroundColor Red Write-Host "Try running the script again or check Veeam Backup service logs." -ForegroundColor Red } } else { Write-Host "Results retrieved successfully ($resultCount items)" -ForegroundColor Green } $Recommendations = @( [Ordered]@{ Id = 1 Name = "Remote Desktop Services (TermService) should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "RemoteDesktopServiceDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 2 Name = "Remote Registry service (RemoteRegistry) should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "RemoteRegistryDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 3 Name = "Windows Remote Management (WinRM) service should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "WinRmServiceDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 4 Name = "Windows Firewall should be enabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "FirewallEnabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 5 Name = "WDigest credentials caching should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "WDigestNotStorePasswordsInMemory"}).Status Remediation = "Script" } [Ordered]@{ Id = 6 Name = "Web Proxy Auto-Discovery service (WinHttpAutoProxySvc) should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "WebProxyAutoDiscoveryDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 7 Name = "Deprecated versions of SSL and TLS should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "OutdatedSslAndTlsDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 8 Name = "Windows Script Host should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "WindowsScriptHostDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 9 Name = "SMBv1 protocol should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "SMB1ProtocolDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 10 Name = "Link-Local Multicast Name Resolution (LLMNR) should be disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "LLMNRDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 11 Name = "SMBv3 signing and encryption should be enabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "CSmbSigningAndEncryptionEnabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 12 Name = "MFA for the backup console should be enabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "MfaEnabledInBackupConsole"}).Status Remediation = "Manual" } [Ordered]@{ Id = 13 Name = "Immutable or offline (air gapped) media should be used" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ImmutableOrOfflineMediaPresence"}).Status Remediation = "Manual" } [Ordered]@{ Id = 14 Name = "Password loss protection should be enabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "LossProtectionEnabled"}).Status Remediation = "Manual" } [Ordered]@{ Id = 15 Name = "Backup server should not be a part of the production domain" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "BackupServerInProductionDomain"}).Status Remediation = "Manual" } [Ordered]@{ Id = 16 Name = "Email notifications should be enabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "EmailNotificationsEnabled"}).Status Remediation = "Manual" } [Ordered]@{ Id = 17 Name = "All backups should have at least one copy (the 3-2-1 backup rule)" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ContainBackupCopies"}).Status Remediation = "Manual" } [Ordered]@{ Id = 18 Name = "Reverse incremental backup mode is deprecated and should be avoided" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ReverseIncrementalInUse"}).Status Remediation = "Manual" } [Ordered]@{ Id = 19 Name = "Unknown Linux servers should not be trusted automatically" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ManualLinuxHostAuthentication"}).Status Remediation = "Script" } [Ordered]@{ Id = 20 Name = "The configuration backup must not be stored on the backup server" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ConfigurationBackupRepositoryNotLocal"}).Status Remediation = "Manual" } [Ordered]@{ Id = 21 Name = "Host to proxy traffic encryption should be enabled for the Network transport mode" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ViProxyTrafficEncrypted"}).Status Remediation = "Script" } [Ordered]@{ Id = 22 Name = "Hardened repositories should not be hosted in virtual machines" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "HardenedRepositoryNotVirtual"}).Status Remediation = "Manual" } [Ordered]@{ Id = 23 Name = "Network traffic encryption should be enabled in the backup network" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "TrafficEncryptionEnabled"}).Status Remediation = "Manual" } [Ordered]@{ Id = 24 Name = "Linux servers should have password-based authentication disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "LinuxServersUsingSSHKeys"}).Status Remediation = "Manual" } [Ordered]@{ Id = 25 Name = "Backup services should be running under the LocalSystem account" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "BackupServicesUnderLocalSystem"}).Status Remediation = "Manual" } [Ordered]@{ Id = 26 Name = "Configuration backup should be enabled and use encryption" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "ConfigurationBackupEnabledAndEncrypted"}).Status Remediation = "Manual" } [Ordered]@{ Id = 27 Name = "Credentials and encryption passwords should be rotated at least annually" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "PasswordsRotation"}).Status Remediation = "Manual" } [Ordered]@{ Id = 28 Name = "Hardened repositories should have the SSH Server disabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "HardenedRepositorySshDisabled"}).Status Remediation = "Manual" } [Ordered]@{ Id = 29 Name = "S3 Object Lock in the Governance mode does not provide true immutability" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "OsBucketsInComplianceMode"}).Status Remediation = "Manual" } [Ordered]@{ Id = 30 Name = "Backup jobs to cloud repositories should use encryption" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "JobsTargetingCloudRepositoriesEncrypted"}).Status Remediation = "Manual" } [Ordered]@{ Id = 31 Name = "Latest product updates should be installed" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "BackupServerUpToDate"}).Status Remediation = "Manual" } [Ordered]@{ Id = 32 Name = "PostgreSQL server should be configured with recommended settings" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "PostgreSqlUseRecommendedSettings"}).Status Remediation = "Script" } [Ordered]@{ Id = 33 Name = "Hardened repositories should not be used as backup proxy servers" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "HardenedRepositoryNotContainsNBDProxies"}).Status Remediation = "Manual" } [Ordered]@{ Id = 34 Name = "Local Security Authority Server Service (LSASS) should be set to run as a protected process" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "LsassProtectedProcess"}).Status Remediation = "Script" } [Ordered]@{ Id = 35 Name = "NetBIOS protocol should be disabled on all network interfaces" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "NetBiosDisabled"}).Status Remediation = "Script" } [Ordered]@{ Id = 36 Name = "Credential Guard should be enabled" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "CredentialsGuardConfigured"}).Status Remediation = "Manual" } [Ordered]@{ Id = 37 Name = "Backup encryption passwords should follow length and complexity recommendations" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "EncryptionPasswordsComplexityRules"}).Status Remediation = "Manual" } [Ordered]@{ Id = 38 Name = "Saved credentials should follow password length and complexity recommendations" Status = ($AnalyzerResult | Where-Object {$_.Type -eq "CredentialsPasswordsComplexityRules"}).Status Remediation = "Manual" } ) # Formatting and better structure for future updates $Summary = $Recommendations | ForEach-Object { if ($_.Id -lt 10) { $CleanID = "0"+$_.Id } else { $CleanID = $_.Id } [PSCustomObject]@{ Id = $CleanID Name = $_.Name Status = $_.Status Remediation = $_.Remediation } } # Print out current status report Clear-Host Write-Host "Report:" -ForegroundColor Green Write-Host "" foreach ($Recommendation in $Summary) { switch ($Recommendation.Status) { "UnableToCheck" { Write-Host $Recommendation.Id "-" $Recommendation.Name ": " -NoNewline; Write-Host "Unable to detect" -ForegroundColor Yellow } {$Recommendation.Status -eq "Ok"} { Write-Host $Recommendation.Id "-" $Recommendation.Name ": " -NoNewline; Write-Host "Passed" -ForegroundColor Green } "Suppressed" { Write-Host $Recommendation.Id "-" $Recommendation.Name ": " -NoNewline; Write-Host "Suppressed" -ForegroundColor DarkGray } default { Write-Host $Recommendation.Id "-" $Recommendation.Name ": " -NoNewline; if($Recommendation.Remediation -eq "Script") {Write-Host "Not implemented" -ForegroundColor Red -NoNewline; Write-Host " (Use 'Apply configurations' option to fix)" -ForegroundColor Yellow} else {Write-Host "Not implemented" -ForegroundColor Red;} } } } return $Summary } # This function sets defined practice ID configuration on a server into a recommended state function Set-VBRComplianceRecommendations($id) { switch ([int]$id){ 1 { Write-host "Disabling Remote Desktop Services (TermService)..." -NoNewline Try { Set-Service "TermService" -StartupType "Disabled" -ErrorAction SilentlyContinue Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 2 { Write-host "Disabling Remote Registry service (RemoteRegistry)..." -NoNewline Try { Stop-Service "RemoteRegistry" -Force -ErrorAction SilentlyContinue Set-Service "RemoteRegistry" -StartupType "Disabled" -ErrorAction SilentlyContinue Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 3 { Write-host "Disabling Windows Remote Management (WinRM) service..." -NoNewline Try { Set-Service "WinRM" -StartupType "Disabled" -ErrorAction SilentlyContinue Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 4 { Write-host "Enabling Windows Firewall..." -NoNewline Try { Set-NetFirewallProfile -All -Enabled "True" -ErrorAction SilentlyContinue Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 5 { Write-host "Disabling WDigest credentials caching..." -NoNewline Try { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" -Name "UseLogonCredential" -ErrorAction Ignore Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 6 { Write-host "Disabling Web Proxy Auto-Discovery service (WinHttpAutoProxySvc)..." -NoNewline Try { Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\WinHttpAutoProxySvc" -Name "Start" -Value 4 $winhttpPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp" if (-not (Test-Path $winhttpPath)) { New-Item -Path $winhttpPath -Force | Out-Null } Set-ItemProperty -Path $winhttpPath -Name "DisableWpad" -Value 1 -Type DWord -Force Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 7 { Write-host "Disabling deprecated versions of SSL and TLS..." -NoNewline Try { New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -Force | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue| Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Force -ErrorAction SilentlyContinue | Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'Enabled' -value '0' -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWORD' -Force -ErrorAction SilentlyContinue | Out-Null Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 8 { Write-host "Disabling Windows Script Host..." -NoNewline Try { New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Script Host\Settings" -Name "Enabled" -PropertyType "DWORD" -Value "0" -Force -ErrorAction SilentlyContinue | Out-Null Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 9 { Write-host "Disabling SMBv1 protocol..." -NoNewline Try { Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force -ErrorAction SilentlyContinue | Out-Null Disable-WindowsOptionalFeature -Online -FeatureName "SMB1Protocol" -NoRestart -ErrorAction SilentlyContinue | Out-Null Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 10 { Write-host "Disabling Link-Local Multicast Name Resolution (LLMNR)..." -NoNewline Try { New-Item "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT" -Name "DNSClient" -Force -ErrorAction SilentlyContinue | Out-Null New-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Name "EnableMultiCast" -Value "0" -PropertyType "DWORD" -Force -ErrorAction SilentlyContinue | Out-Null Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 11 { Write-host "Enabling SMBv3 signing and encryption..." -NoNewline Try { Set-SmbServerConfiguration -EncryptData $true -Force -ErrorAction SilentlyContinue Set-SmbServerConfiguration -EnableSecuritySignature $true -Force -ErrorAction SilentlyContinue Set-SmbServerConfiguration -RequireSecuritySignature $true -Force -ErrorAction SilentlyContinue Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 19 { Write-host "Setting unknown Linux servers trust settings..." -NoNewline Try { Set-VBRLinuxTrustedHostPolicy -Type "KnownHosts" Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 21 { Write-host "Setting host to proxy traffic encryption in Network transport mode..." -NoNewline Try { Get-VBRViProxy | Where-Object {$_.UseSSL -ne $True} | Set-VBRViProxy -EnableHostToProxyEncryption -ErrorAction SilentlyContinue Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 32 { Write-host "Setting recommended PostgreSQL settings (applies to local instances only)..." -NoNewline Try { Set-VBRPSQLDatabaseServerLimits -WA 0 Write-host "OK (Reboot required)" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 34 { Write-host "Making LSASS to run as a protected process..." -NoNewline Try { if ($env:firmware_type -eq "UEFI") { Set-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -Value 1 } else { Set-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -Value 2 } Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 35 { Write-host "Disabling NETBIOS on all network interfaces..." -NoNewline Try { $interfaces = Get-ChildItem "HKLM:SYSTEM\CurrentControlSet\Services\NetBT\Parameters\Interfaces" | Select -ExpandProperty PSChildName Foreach($interface in $interfaces) { Set-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Services\NetBT\Parameters\Interfaces\$interface" -Name "NetbiosOptions" -Value 2 } Write-host "OK" -ForegroundColor Green } Catch {Write-host "Failed" -ForegroundColor Red} } 36 { Write-host "For Credential Guard, please perform manual changes as per Security & Compliance Analyzer User's Guide." -ForegroundColor Cyan Write-host "Reference: https://helpcenter.veeam.com/docs/vbr/userguide/best_practices_analyzer.html?ver=13" -ForegroundColor Cyan } default {Write-host "Unknown recommendation ID"} } } # This function draws main menu function Get-VeeamMenu { Write-host "" Write-host "Available actions:" -ForegroundColor Green Write-host "" Write-host "1 : Refresh compliance report" if ($RemediationCount -gt 0) {Write-host "2 : Apply ALL recommended configurations " -NoNewline; Write-host "(total:$RemediationCount)" -ForegroundColor Yellow } else {Write-host "2 : Apply all recommended security & compliance configurations"} Write-host "3 : Apply selected configuration only..." Write-host "0 : Exit" Write-host "" } # Trigger execution Write-Host "Validating user permissions..." try { Start-VBRSecurityComplianceAnalyzer } catch { if ($Error[0].Exception.Message -match "MFA-enabled") { Write-Host "Unable to connect to the server with MFA-enabled user account. Consider KB4535 (https://www.veeam.com/kb4535)." -ForegroundColor Red Exit } else { Write-Host "Failed to start Security & Compliance Analyzer" -ForegroundColor Red Exit } } $Report = Get-VBRComplianceReport do { $RemediationActions = $Report | Where-Object {$_.Remediation -eq "Script" -and $_.Status -eq "Violation"} $RemediationCount = ($RemediationActions | Measure-Object).count Get-VeeamMenu $choice = Read-host "Select action to perform" Write-host "" switch ($choice) { 0 { break } 1 { $Report = Get-VBRComplianceReport } 2 { foreach ($Action in $RemediationActions) {Set-VBRComplianceRecommendations $Action.ID} } 3 { $ActionID = Read-Host "Enter recommendation ID" if ($Remediationactions.id -contains $ActionID) { Set-VBRComplianceRecommendations $ActionID } else { $matchedReport = $Report | Where-Object { $_.Id -eq $ActionID -or $_.Id -eq ([int]$ActionID).ToString("00") } if ($matchedReport -and $matchedReport.Status -eq "UnableToCheck") { Write-host "Selected configuration ID does not need to be applied." -ForegroundColor Yellow Write-host " This setting may already be applied at a system level by default." -ForegroundColor Cyan } elseif ($matchedReport -and $matchedReport.Status -eq "Ok") { Write-host "Selected configuration ID is already compliant (Passed)." -ForegroundColor Green } else { Write-host "Selected configuration ID does not need to be applied or manual only" -ForegroundColor Red Write-host "Refer to https://helpcenter.veeam.com/docs/vbr/userguide/best_practices_analyzer.html?ver=13" -ForegroundColor Red } } } default { Write-host "Error: select correct action." -ForegroundColor Red} } } until ($choice -eq 0)