diff --git a/Veeam Hardening/Veeam Security & Compliance Analyzer 1.93.ps1 b/Veeam Hardening/Veeam Security & Compliance Analyzer 1.93.ps1 new file mode 100644 index 0000000..8530368 --- /dev/null +++ b/Veeam Hardening/Veeam Security & Compliance Analyzer 1.93.ps1 @@ -0,0 +1,628 @@ +<#-- +===================================== +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)