﻿[CmdletBinding()]
Param
(
	[Parameter(Mandatory = $false)]
	[bool]
	$RunUnattendedAndAcceptDisclaimer
)

$unattendedString = "!!! Running in unattended Mode !!!"

# List of failed Actions
[System.Collections.ArrayList]$ArrayList = $global:failedActions = @()

$global:failedActions += "The following settings failed:"

# ============================================================================
# ============================== FUNCTIONS ===================================
# ============================================================================

Function StartExe
{
	<#    
		.Synopsis
			Starts the given Exe with the given Parameters
​​
		.Description
			Starts the given Exe with the given Parameters, if the ReturnCode is not equal 0 it also writes the StandardOutput

		.Parameter ExeName
			 Executable to be started

		.Parameter ExeParameters
			 Parameters to hand over

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$ExeName,

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

	Process
	{
		if (Test-Path $ExeName)
		{
			$ps = new-object System.Diagnostics.Process

			$ps.StartInfo.Filename = "$ExeName"
			$ps.StartInfo.Arguments = "$ExeParameters"
			$ps.StartInfo.RedirectStandardOutput = $True
			$ps.StartInfo.UseShellExecute = $false
			$ps.Start()
			$ps.WaitForExit()

			$ExitCode = $ps.ExitCode
    
			if ($ExitCode -ne 0)
			{
				[string] $Out = $ps.StandardOutput.ReadToEnd()
				Write-Log -Message "!!! Not Successfull call of '$ExeName' '$ExeParameters' !!!" -Type Error
				Write-Log -Message "!!! StandardOutput = '$Out' !!!" -Type Error
				throw "Execution failed with ExitCode = '$ExitCode'"
			}
		}
		else
		{
			throw "!!! ExeName = '$ExeName' was not present, quit execution of the script !!!"
		}
	}
}

Function GetRegistryString
{
	<#    
		.Synopsis
			Gets the Registry String
​​
		.Description
			Gets the Registry String

		.Parameter $RegPath
			Path of the Registry

		.Parameter $RegKey
			Key of the Registry

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$RegPath,

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

	Process
	{					
		$valueString = (Get-ItemProperty -Path "$RegPath" -Name "$RegKey" -ErrorAction SilentlyContinue).$RegKey
		if (($valueString -ne $null) -and ($valueString.Length -ne 0)) 
		{
			$valueString = $valueString
		}
		else
		{
			$valueString = "null"
		}
		return $valueString
	}
}

Function ApplyRegistryPolicy
{
	<#    
		.Synopsis
			Applies Registry Policy Settings
​​
		.Description
			Applies Registry Policy Settings by writing the corresponding Registry value

		.Parameter $RegPath
			Path of the Registry

		.Parameter $RegKey
			Key of the Registry

		.Parameter $valueToSet
			Value to set the Key of the Registry

		.Parameter $PropertyType
			Type of the Registry key, e.g. DWord or String

		.Parameter $PolicyDescription
			 Description of the Policy to be changed

		.Parameter $PolicyValueDescription
			 Value Description of the Policy to be changed

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$RegPath,

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

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

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

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

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

	Process
	{			
		$failureHeader = "Local Group Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> Security Options"
		try
		{
			# all errors shall be terminating
			$ErrorActionPreference = "Stop";
			# Get Registry value before change
			$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose

			Write-Log -Message "Set '$PolicyDescription' to -> '$PolicyValueDescription'" -Type Verbose
			# Set the new Registry value
			New-ItemProperty -Path "$RegPath" -Name "$RegKey" -Value "$valueToSet" -PropertyType $PropertyType -Force | Out-Null

			Write-Log -Message "RegistryPolicy is now '$RegPath!$RegKey = $valueToSet' (before change: '$valueStringBeforeChange')" -Type Verbose
			Write-Log -Message " "
		}
		catch
		{
			$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
			Write-Log -Message "$ErrorDescription" -Type Verbose
			$global:failedActions += "$ErrorDescription"
		}		
	}
}

Function ApplyRegistryPolicies
{
	<#    
		.Synopsis
			Applies Registry Policy Settings
​​
		.Description
			Applies Registry Policy Settings by writing the corresponding Registry value

		.Parameter $LGPOContext
			Either "Client" or "Server" needs to be passed there to set the correct context.

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[ValidateSet("Client", "Server")]
		$LGPOContext
	)

	Process
	{
		if ($LGPOContext -eq "Server")
		{
			# no changes so far between Server and Client
		}
		else
		{
			# no changes so far between Server and Client
		}		
	
		# Activate User Account Control
		Write-Log -Message " "
		Write-Log -Message "========================="
		Write-Log -Message "'Apply Registry Policies'"
		Write-Log -Message "========================="
		Write-Log -Message "Local Group Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> Security Options" -Type Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "FilterAdministratorToken" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "User Account Control: Admin Approval Mode for the built-in Administrator account" -PolicyValueDescription "Enabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "EnableUIADesktopToggle" -valueToSet "0" -PropertyType "DWord" -PolicyDescription "User Account Control: Allow UIAccess applications to prompt for elevation without using the secure desktop" -PolicyValueDescription "Disabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "ConsentPromptBehaviorAdmin" -valueToSet "4" -PropertyType "DWord" -PolicyDescription "User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode" -PolicyValueDescription "Prompt for consent" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "ConsentPromptBehaviorUser" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "User Account Control: Behavior of the elevation prompt for standard users" -PolicyValueDescription "Prompt for credentials on the secure desktop" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "EnableInstallerDetection" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "User Account Control: Detect application installations and prompt for elevation" -PolicyValueDescription "Enabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "ValidateAdminCodeSignatures" -valueToSet "0" -PropertyType "DWord" -PolicyDescription "User Account Control: Only elevate executables that are signed and validated" -PolicyValueDescription "Disabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "EnableLUA" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "User Account Control: Run all administrators in Admin Approval Mode" -PolicyValueDescription "Enabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "PromptOnSecureDesktop" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "User Account Control: Switch to the secure desktop when prompting for elevation" -PolicyValueDescription "Enabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "EnableVirtualization" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "User Account Control: Virtualize file and registry write failures to peruser locations" -PolicyValueDescription "Enabled" -Verbose	

		Write-Log -Message " "
		Write-Log -Message "Local Group Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> Security Options" -Type Verbose
		
		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -RegKey "undockwithoutlogon" -valueToSet "0" -PropertyType "DWord" -PolicyDescription "Devices: Allow undock without having to log on" -PolicyValueDescription "Disabled" -Verbose	

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -RegKey "AllocateDASD" -valueToSet "0" -PropertyType "String" -PolicyDescription "Devices: Allowed to format and eject removable media" -PolicyValueDescription "Administrators" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\System\CurrentControlSet\Control\Print\Providers\LanMan Print Services\Servers" -RegKey "AddPrinterDrivers" -valueToSet "1" -PropertyType "DWord" -PolicyDescription "Devices: Prevent users from installing printer drivers" -PolicyValueDescription "Enabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -RegKey "AllocateCDRoms" -valueToSet "1" -PropertyType "String" -PolicyDescription "Devices: Restrict CD-ROM access to locally logged-on user only" -PolicyValueDescription "Enabled" -Verbose

		ApplyRegistryPolicy -RegPath "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon" -RegKey "AllocateFloppies" -valueToSet "1" -PropertyType "String" -PolicyDescription "Devices: Restrict floppy access to locally logged-on user only" -PolicyValueDescription "Enabled" -Verbose		
	}
}

Function ApplyAdministrativeTemplates
{
	<#    
		.Synopsis
			Applies Administrative Templates
​​
		.Description
			Applies Administrative Templates for Machine or User Context

		.Parameter LGPOTextFile
			 TextFile which will contain the changes for the Administrative Templates

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$LGPOTextFile
	)

	Process
	{						
		$failureHeader = "Local Group Policy -> Computer Configuration -> Administrative Templates"
		try
		{
			# all errors shall be terminating
			$ErrorActionPreference = "Stop";

			if (Test-Path "$Global:TempFolder\$LGPOTextFile")
			{
				Remove-Item "$Global:TempFolder\$LGPOTextFile" -confirm:$false -Force
			}
			New-Item "$Global:TempFolder\$LGPOTextFile" -type file | Out-Null

			if ($LGPOTextFile -eq "WinMachineReg.txt")
			{			
				# Write Textfile to be handed over to secedit with the specific policies				
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; ----------------------------------------------------------------------"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; PARSING Computer POLICY"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; Source file:  $env:SystemRoot\System32\GroupPolicy\Machine\registry.pol"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Computer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Microsoft\Windows\CurrentVersion\Policies\CredUI"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "EnumerateAdministrators"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "DWORD:0"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Computer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "NoDriveTypeAutoRun"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "DWORD:255"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Computer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "NoAutorun"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "DWORD:1"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Computer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Policies\Microsoft\Windows\Explorer"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "NoAutoplayfornonVolume"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "DWORD:1"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; PARSING COMPLETED."
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; ----------------------------------------------------------------------"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
			
				Write-Log -Message "Local Group Policy -> Computer Configuration -> Administrative Templates -> Windows Components -> Credential User Interface" -Type Verbose			
				# Get Registry value before change
				$RegPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\CredUI"
				$RegKey = "EnumerateAdministrators"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose			
				Write-Log -Message "Administrative Templates Policies (Machine): Status of 'Enumerate administrator accounts on elevation' is now -> 'Disabled'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of '$RegPath!$RegKey' is now -> '0' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "

				Write-Log -Message "Local Group Policy -> Computer Configuration -> Administrative Templates -> Windows Components -> AutoPlay Policies" -Type Verbose
				# Get Registry value before change
				$RegPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
				$RegKey = "NoDriveTypeAutoRun"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of 'Turn off AutoPlay' is now -> 'Enabled all drives'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of '$RegPath!$RegKey' is now -> '255' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "

				# Get Registry value before change
				$RegPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"
				$RegKey = "NoAutorun"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of 'Default behavior for AutoRun' is now -> 'Enabled, do not execute any AutoRun commands'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of '$RegPath!$RegKey' is now -> '1' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "

				# Get Registry value before change
				$RegPath = "HKLM:\Software\Policies\Microsoft\Windows\Explorer"
				$RegKey = "NoAutoplayfornonVolume"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of 'Turn off AutoPlay for non-volume devices' is now -> 'Enabled'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (Machine): Status of '$RegPath!$RegKey' is now -> '1' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "
			}
			else
			{
				# Write Textfile to be handed over to secedit with the specific policies
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; ----------------------------------------------------------------------"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; PARSING User POLICY"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; Source file:  $env:SystemRoot\System32\GroupPolicy\User\registry.pol"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "User"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Policies\Microsoft\Windows\Control Panel\Desktop"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "ScreenSaveActive"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "SZ:1"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "User"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Policies\Microsoft\Windows\Control Panel\Desktop"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "ScreenSaverIsSecure"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "SZ:1"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "User"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "Software\Policies\Microsoft\Windows\Control Panel\Desktop"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "ScreenSaveTimeOut"
				Add-Content "$Global:TempFolder\$LGPOTextFile" "SZ:1800"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; PARSING COMPLETED."
				Add-Content "$Global:TempFolder\$LGPOTextFile" "; ----------------------------------------------------------------------"
				Add-Content "$Global:TempFolder\$LGPOTextFile" ""

				Write-Log -Message "Local Group Policy -> User Configuration -> Administrative Templates -> Control Panel -> Personalization" -Type Verbose
				# Get Registry value before change
				$RegPath = "HKCU:\Software\Policies\Microsoft\Windows\Control Panel\Desktop"
				$RegKey = "ScreenSaveActive"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose
				Write-Log -Message "Administrative Templates Policies (User): Status of 'Enable Screen saver' is now -> 'Enabled'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (User): Status of '$RegPath!$RegKey' is now -> '1' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "

				# Get Registry value before change
				$RegPath = "HKCU:\Software\Policies\Microsoft\Windows\Control Panel\Desktop"
				$RegKey = "ScreenSaverIsSecure"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose
				Write-Log -Message "Administrative Templates Policies (User): Status of 'Password protect the screen saver' is now -> 'Enabled'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (User): Status of '$RegPath!$RegKey' is now -> '1' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "

				# Get Registry value before change
				$RegPath = "HKCU:\Software\Policies\Microsoft\Windows\Control Panel\Desktop"
				$RegKey = "ScreenSaverIsSecure"
				$valueStringBeforeChange = ""
				$valueStringBeforeChange = GetRegistryString -RegPath $RegPath -RegKey $RegKey -Verbose
				Write-Log -Message "Administrative Templates Policies (User): Status of 'Screen saver timeout' is now -> '1800 second'" -Type Verbose
				Write-Log -Message "Administrative Templates Policies (User): Status of '$RegPath!$RegKey' is now -> '1800' (before change: '$valueStringBeforeChange')" -Type Verbose
				Write-Log -Message " "
			}

			StartExe -ExeName "$Global:ScriptPath\LGPO.exe" -ExeParameters "/t `"$Global:TempFolder\$LGPOTextFile`" /q" | Out-Null

			Remove-Item "$Global:TempFolder\$LGPOTextFile" -confirm:$false -Force
		}
		catch
		{
			$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
			Write-Log -Message "$ErrorDescription" -Type Verbose
			$global:failedActions += "$ErrorDescription"
		}		
	}
}

Function DisableService
{
	<#    
		.Synopsis
			Disables the given Windows Services
​​
		.Description
			Disables the given Windows Services

		.Parameter $ServiceName
			Name of the Service to be used.

		.Parameter $DisplayName
			Display Name of the Service to be used.

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$ServiceName,

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

	Process
	{		
		# Check if the Service exists at all
		if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue)
		{
			# Get Status of the Service before change
			$ServiceStatusBeforeChange = (Get-Service -Name $ServiceName).Status

			$failureHeader = "Stop non-essential Windows Services"
			try
            {
				# all errors shall be terminating
				$ErrorActionPreference = "Stop";

				# Stop the Service first
				if ($Global:PowerShellVersion.Major -ge 5)
				{					
					# PowerShell Major Version >= 5
					Stop-Service $ServiceName -Force -NoWait
				}
				else
				{					
					# PowerShell Major Version <= 4 -> Stop-Service Paramter -NoWait not available
					Stop-Service $ServiceName -Force
				}
				Write-Log -Message "Status of Service '$DisplayName' ($ServiceName) is now 'Stopped' (before change: '$ServiceStatusBeforeChange')" -Type Verbose
			}
			catch
			{				
				$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
				Write-Log -Message "$ErrorDescription" -Type Verbose
				$global:failedActions += "$ErrorDescription"			
			}
			
			# Get Startup Type of the Service before change
			$StartupTypeBeforeChange = (Get-WmiObject Win32_Service -filter "Name='$ServiceName'").StartMode

			$failureHeader = "Disable non-essential Windows Services"
			try
            {
				# all errors shall be terminating
				$ErrorActionPreference = "Stop";

				# Set the Startup Type to Disabled		
				Set-Service $ServiceName -Startuptype Disabled
				Write-Log -Message "Startup Type for Service of '$DisplayName' ($ServiceName) is now 'Disabled' (before change: '$StartupTypeBeforeChange')" -Type Verbose
				Write-Log -Message " "
			}
			catch
			{
				$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
				Write-Log -Message "$ErrorDescription" -Type Verbose
				$global:failedActions += "$ErrorDescription"
			}			
		}
		else
		{
			Write-Log -Message "Service '$DisplayName' ($ServiceName) is not available and cannot be disabled" -Type Verbose
			Write-Log -Message " "
		}
	}
}


Function DisableWindowsServices
{
	<#    
		.Synopsis
			Disables the non-essential Windows Services
​​
		.Description
			Disables the non-essential Windows Services

		.Parameter $LGPOContext
			Either "Client" or "Server" needs to be passed there to set the correct context.

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[ValidateSet("Client", "Server")]
		$LGPOContext
	)

	Process
	{		
		Write-Log -Message " "
		Write-Log -Message "========================================================="
		Write-Log -Message "'Disable Startup Type for non-essential Windows Services'"
		Write-Log -Message "========================================================="

		Write-Log -Message "DisableWindowsServices Running in context '$LGPOContext'"
		
		DisableService -ServiceName "ALG" -DisplayName "Application Layer Gateway Service" -Verbose

		DisableService -ServiceName "AppMgmt" -DisplayName "Application Management" -Verbose

		DisableService -ServiceName "Browser" -DisplayName "Computer Browser" -Verbose

		DisableService -ServiceName "TrkWks" -DisplayName "Distributed Link Tracking Client" -Verbose

		DisableService -ServiceName "fdPHost" -DisplayName "Function Discovery Provider Host" -Verbose

		DisableService -ServiceName "FDResPub" -DisplayName "Function Discovery Resource Publication" -Verbose

		DisableService -ServiceName "hidserv" -DisplayName "Human Interface Device Service" -Verbose

		DisableService -ServiceName "SharedAccess" -DisplayName "Internet Connection Sharing (ICS)" -Verbose
						
		DisableService -ServiceName "lltdsvc" -DisplayName "Link-Layer Topology Discovery Mapper" -Verbose

		DisableService -ServiceName "MMCSS" -DisplayName "Multimedia Class Scheduler" -Verbose

		if ($LGPOContext -eq "Client")
		{
			DisableService -ServiceName "CscService" -DisplayName "Offline Files" -Verbose
		}				

		DisableService -ServiceName "RasAuto" -DisplayName "Remote Access Auto Connection Manager" -Verbose

		DisableService -ServiceName "RasMan" -DisplayName "Remote Access Connection Manager" -Verbose

		DisableService -ServiceName "RemoteAccess" -DisplayName "Routing and Remote Access" -Verbose

		DisableService -ServiceName "ShellHWDetection" -DisplayName "Shell Hardware Detection" -Verbose
		
		if ($LGPOContext -eq "Server")
		{
			DisableService -ServiceName "sacsvr" -DisplayName "Special Administration Console Helper" -Verbose
		}

		DisableService -ServiceName "SSDPSRV" -DisplayName "SSDP Discovery" -Verbose
	}
}

Function ApplySecurityPoliciesWithSecedit
{
	<#    
		.Synopsis
			Changes the SECURITYPOLICY via Secedit
​​
		.Description
			Changes the SECURITYPOLICY to the given value via Secedit

		.Parameter $PolicyName
			 Name of the Policy to be changed

		.Parameter $PolicyValue
			 Value of the Policy to be changed

		.Parameter $PolicyDescription
			 Description of the Policy to be changed

		.Parameter $PolicyValueDescription
			 Value Description of the Policy to be changed

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$PolicyName,

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

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

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

	Process
	{		
		$failureHeader = "Local Group Policy -> Windows Settings -> Security Settings -> Account Policies -> Password Policy"
		try
		{
			# all errors shall be terminating
			$ErrorActionPreference = "Stop";

			Write-Log -Message "Secedit.exe: Apply Security Policy '$PolicyDescription' to -> '$PolicyValueDescription'" -Type Verbose

			if (Test-Path "$Global:TempFolder\secpol.cfg")
			{
				Remove-Item "$Global:TempFolder\secpol.cfg" -confirm:$false -Force
			}

			# Export DB in .cfg file
			StartExe -ExeName "$Global:Sys32Folder\secedit.exe" -ExeParameters "/export /cfg $Global:TempFolder\secpol.cfg /areas SECURITYPOLICY /log $Global:TempFolder\secpol.export.log /quiet" | Out-Null

			# Changed value in .cfg file
			((Get-Content "$Global:TempFolder\secpol.cfg") |
				Foreach-Object {												
					if ($_ -like "*$PolicyName = *")
					{										
						$valueBeforeChange = $_
						Write-Log -Message "Change Policy is now '$PolicyName = $PolicyValue' (before change: '$valueBeforeChange')" -Type Verbose
						Write-Log -Message " "

						if ($_ -like "*LockoutBadCount = *")
						{
							$_ -replace $_, "$PolicyName = $PolicyValue`r`nResetLockoutCount = 15`r`nLockoutDuration = 15"
						}
						elseif ($_ -like "*ResetLockoutCount = *")
						{
							$_ -replace $_, ""
						}
						elseif ($_ -like "*LockoutDuration = *")
						{
							$_ -replace $_, ""
						}
						else
						{				
							$_ -replace $_, "$PolicyName = $PolicyValue"
						}
					}
					else 
					{
						# Do not change the line
						$_
					}
			}) | 
			
			Set-Content "$Global:TempFolder\secpol.cfg" -Force

			# Import changed value into DB
			StartExe -ExeName "$Global:Sys32Folder\secedit.exe" -ExeParameters "/configure /db $env:SystemRoot\security\local.sdb /cfg $Global:TempFolder\secpol.cfg /areas SECURITYPOLICY /log $Global:TempFolder\secpol.configure.log /quiet" | Out-Null
				
			Remove-Item "$Global:TempFolder\secpol.cfg" -Confirm:$false -Force
		}
		catch
		{
			$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
			Write-Log -Message "$ErrorDescription" -Type Verbose
			$global:failedActions += "$ErrorDescription"
		}		
	}
}

Function ApplyUserRightsWithSecedit
{
	<#    
		.Synopsis
			Changes the USER_RIGHTS via Secedit
​​
		.Description
			Changes the USER_RIGHTS to the given value via Secedit

		.Parameter $PolicyName
			 Name of the Policy to be changed

		.Parameter $PolicyValue
			 Value of the Policy to be changed

		.Parameter $PolicyDescription
			 Description of the Policy to be changed

		.Parameter $PolicyValueDescription
			 Value Description of the Policy to be changed

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$PolicyName,

		[Parameter(Mandatory = $true)]
		[AllowEmptyString()]
		[string]
		$PolicyValue,

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

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

	Process
	{		
		$failureHeader = "Local Group Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment"
		try
		{
			# all errors shall be terminating
			$ErrorActionPreference = "Stop";

			Write-Log -Message "Secedit.exe: Apply User Rights '$PolicyDescription' to -> '$PolicyValueDescription'" -Type Verbose

			if (Test-Path "$Global:TempFolder\secpol.cfg")
			{
				Remove-Item "$Global:TempFolder\secpol.cfg" -confirm:$false -Force
			}

			# Export DB in .cfg file
			StartExe -ExeName "$Global:Sys32Folder\secedit.exe" -ExeParameters "/export /cfg $Global:TempFolder\secpol.cfg /areas USER_RIGHTS /log $Global:TempFolder\secpol.export.log /quiet" | Out-Null

			$counter = 0;


			(Get-Content "$Global:TempFolder\secpol.cfg") |
				Foreach-Object {												
					if ($_ -like "*$PolicyName = *")
					{										
						$counter = $counter + 1
						$valueBeforeChange = $_
						Write-Log -Message "Change Policy is now '$PolicyName = $PolicyValue' (before change: '$valueBeforeChange')" -Type Verbose
						return					
					}				
			}

			if ($counter -eq 0)
			{
				$valueBeforeChange = "$PolicyName = "
				Write-Log -Message "Change Policy is now '$PolicyName = $PolicyValue' (before change: '$valueBeforeChange')" -Type Verbose
			}
			Write-Log -Message " "

			if (Test-Path "$Global:TempFolder\secpol.changed.cfg")
			{
				Remove-Item "$Global:TempFolder\secpol.changed.cfg" -Confirm:$false -Force
			}

			$writer = [System.IO.StreamWriter] "$Global:TempFolder\secpol.changed.cfg"

			try 
			{
				# all errors shall be terminating
				$ErrorActionPreference = "Stop";

				$LineToWrite = "[Unicode]`r`nUnicode=yes`r`n[Privilege Rights]`r`n$PolicyName = $PolicyValue`r`n[Version]`r`nsignature=""`$CHICAGO$""`r`nRevision=1"
				$writer.WriteLine($LineToWrite)			
			}
			catch
			{
				$MessageText = "Secedit.exe: Applying User Rights (write Policies '$LineToWrite' into '$Global:TempFolder\secpol.changed.cfg') failed with Error: '$($_.Exception.Message)'"
				Write-Log -Message "$MessageText" -Type Error
			}
			finally 
			{
				$writer.Close()
			}	

			# Import changed value into DB
			StartExe -ExeName "$Global:Sys32Folder\secedit.exe" -ExeParameters "/overwrite /configure /db $env:SystemRoot\security\local.sdb /cfg $Global:TempFolder\secpol.changed.cfg /areas USER_RIGHTS /log $Global:TempFolder\secpol.configure.log /quiet" | Out-Null
				
			Remove-Item "$Global:TempFolder\secpol.changed.cfg" -Confirm:$false -Force
		}
		catch
		{
			$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
			Write-Log -Message "$ErrorDescription" -Type Verbose
			$global:failedActions += "$ErrorDescription"
		}		
	}
}

Function CheckOSVersion
{
	<#    
		.Synopsis
			Checks the Operating System Version
​​
		.Description
			Checks the Operating System Version and returns to continue or not

		.Returns
			True if we are on Windows 8.1 or higher and we are not running on a Domain Controller

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		
	)

	Process
	{		
		Write-Log -Message "Checking the OS Version ..." -Type Verbose

		$MessageText = "Your Operating System does not meet the Requirements. Supported Versions of Windows are:`r`n`r`nWindows 8 and higher`r`nWindows Server 2008 and higher"

		$OSVersionMajor = [System.Environment]::OSVersion.Version.Major
		$OSVersionMinor = [System.Environment]::OSVersion.Version.Minor
		$OSVersionBuild = [System.Environment]::OSVersion.Version.Build

		Write-Log -Message "-> OS Version = '$OSVersionMajor.$OSVersionMinor.$OSVersionBuild'" -Type Verbose

		if ($OSVersionMajor -ge 6)
		{
			if ($OSVersionMajor -eq 6)
			{
				if ($OSVersionMinor -eq 1)
				{
					Write-Log -Message "-> OS Version = '6.1'" -Type Verbose

					if ($Global:OSProductType -eq 1)
					{
						# we are running on a Windows 7 Workstation -> OSProductType -eq 1
						Write-Log -Message "-> OS Version = '6.1' Workstation -> OS (Windows 7) not supported!" -Type Error
						ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Operating System" -MessageBoxIcon Exclamation
						throw "CheckOSVersion failed!"
					}									
				}
				elseif ($OSVersionMinor -ge 2)
				{
					Write-Log -Message "-> OS Version >= '6.2'" -Type Verbose

					if ($OSVersionMinor -eq 2)
					{
						Write-Log -Message "-> OS Version = '6.2'" -Type Verbose

						if ($OSVersionBuild -ge 9200)
						{
							Write-Log -Message "-> OS Version >= '6.2.9200'" -Type Verbose
						}
						else
						{
							if ($Global:OSProductType -eq 1)
							{
								# we are running on a Windows 8.0 Workstation -> OSProductType -eq 1
								Write-Log -Message "-> OS Version = '6.2' Workstation -> OS (Windows 8.0) not supported!" -Type Error
								ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Operating System" -MessageBoxIcon Exclamation
								throw "CheckOSVersion failed!"
							}									
						}						
					}													
				}
				else
				{					
					Write-Log -Message "-> OS Version < '6.1' -> OS not supported!" -Type Error
					ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Operating System" -MessageBoxIcon Exclamation
					throw "CheckOSVersion failed!"
				}
			}
			
			if ($OSVersionMajor -gt 6)
			{
				Write-Log -Message "-> OS Version > '6'" -Type Verbose
			}						
		}
		else
		{
			Write-Log -Message "-> OS Version < '6' -> OS not supported!" -Type Error
			ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Operating System" -MessageBoxIcon Exclamation
			throw "CheckOSVersion failed!"
			
		}
	}
}

Function CheckOSProductType
{
	<#    
		.Synopsis
			Evaluates the Product Type of the Operating System
​​
		.Description
			Evaluates the Product Type of the Operating System: Workstation, Domain Controller, Server

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		
	)

	Process
	{		
		Write-Log -Message "Checking the OS ProductType ..." -Type Verbose

		$Global:OSProductType = (Get-WmiObject Win32_OperatingSystem).ProductType
		switch ($Global:OSProductType)
		{
			"1" { $Type = "Workstation" }	
			"2" { $Type = "Domain Controller" }
			"3" { $Type = "Server" }		
		}

		Write-Log -Message "-> OS ProductType = '$Type' ($Global:OSProductType)" -Type Verbose

		if ($OSProductType -eq 2)
		{
			$MessageText = "Your OS with Type = 'Domain Controller ($OSProductType)' is not supported -> Quit this script"
			Write-Log -Message "$MessageText" -Type Error
			ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Domain Controller" -MessageBoxIcon Exclamation
			throw "CheckOSProductType failed!"
		}
	}
}

Function CheckDomainJoined
{
	<#    
		.Synopsis
			Evaluates if the PC is currently Domain joined
​​
		.Description
			Evaluates if the PC is currently Domain joined, returns true if it is Domain joined

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		
	)

	Process
	{		
		Write-Log -Message "Checking if joined to a Domain ..." -Type Verbose

		$MessageText = "Your PC is joined to a Domain -> Quit this script"

		if ((gwmi win32_computersystem).partofdomain -eq $true) 
		{
			Write-Log -Message "-> joined to a Domain" -Type Verbose
			Write-Log -Message "$MessageText" -Type Error
			ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Domain joined" -MessageBoxIcon Exclamation
			throw "CheckDomainJoined failed!"
		}
		else 
		{
			Write-Log -Message "-> not joined to a Domain, part of a Workgroup" -Type Verbose		
		}
	}
}

Function CheckFirewallStatus
{
	<#    
		.Synopsis
			Checks the Status of the Windows Firewall
​​
		.Description
			Checks the Status of the Windows Firewall and displays it to the User if it is not enabled

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		
	)

	Process
	{		
		Write-Log -Message "Checking the Status of the Windows Firewall ..." -Type Verbose

		$MessageText = "The Windows Firewall is not enabled in one or more Profiles, we recommend to enable the Windows Firewall"

		$FirewallObj = New-object –comObject HNetCfg.FwPolicy2

		# NET_FW_PROFILE2_DOMAIN = 1
		# NET_FW_PROFILE2_PRIVATE = 2
		# NET_FW_PROFILE2_PUBLIC = 4
		$FirewallEnabledDomain = $FirewallObj.FirewallEnabled(1)  # Domain
		$FirewallEnabledPrivate = $FirewallObj.FirewallEnabled(2) # Private
		$FirewallEnabledPublic = $FirewallObj.FirewallEnabled(4)  # Public
		
		if ( ($FirewallEnabledDomain -eq $false) -or ($FirewallEnabledPrivate -eq $false) -or ($FirewallEnabledPublic -eq $false) ) 
		{
			Write-Log -Message $MessageText -Type Warning
			$MessageText = "$($MessageText):`r`n"

			if ($FirewallEnabledDomain -eq $false)
			{				
				$MessageTextDomain = "Firewall Profile 'Domain' is disabled"
				Write-Log -Message $MessageText -Type Warning
				$MessageText = "$($MessageText)`r`n-> $MessageTextDomain"
			}

			if ($FirewallEnabledPrivate -eq $false)
			{
				$MessageTextPrivate = "Firewall Profile 'Private' is disabled"
				Write-Log -Message "-> $MessageTextPrivate" -Type Warning
				$MessageText = "$MessageText`r`n-> $MessageTextPrivate"
			}
			
			if ($FirewallEnabledPublic -eq $false)
			{
				$MessageTextPublic = "Firewall Profile 'Public' is disabled"
				Write-Log -Message "-> $MessageTextPublic" -Type Warning
				$MessageText = "$MessageText`r`n-> $MessageTextPublic"
			}
			
			ShowMessageBoxOK -Message $MessageText -Header "$Global:BVMSHardeningTool, $Global:BVMSHardeningToolVersion : Windows Firewall" -MessageBoxIcon Exclamation
		}
		else
		{
			Write-Log -Message "-> The Windows Firewall is enabled" -Type Verbose
		}
	}
}

Function CheckBVMSorViewerInstalled
{
	<#    
		.Synopsis
			Evaluates BVMS or the BVMS Viewer is already present on the PC
​​
		.Description
			Evaluates BVMS or the BVMS Viewer is already present on the PC using the UpgradeCode of both Windows Installer Installations in the Windows Registry

			BVMS: -> UpgradeCode {BC9E56E7-7A79-4724-B2C7-B18E 66AD5C62}
			[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\7E65E9CB97A742742B7C1BE866DAC526]

			BVMS Viewer: -> UpgradeCode {47C7A180-7C56-414C-BC5D-9FCE E07F444A}
			[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\081A7C7456C7C414CBD5F9EC0EF744A4]

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		
	)

	Process
	{		
		Write-Log -Message "Checking if BVMS or the BVMS Viewer is already installed ..." -Type Verbose	
		
		$MessageTextBVMS = ""
		$MessageTextBVMSViewer = ""
		$MessageFailure = "Either BVMS or the BVMS Viewer or both are NOT installed.`r`nWe recommend to install one of these Software Packages first before continuing with this script as the SQL Server Installation may fail because of the restrictions made by this script!`r`n`r`nDo you really want to continue?"

		# check if BVMS is installed
		if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\7E65E9CB97A742742B7C1BE866DAC526") 
		{
			Write-Log -Message "-> BVMS is installed" -Type Verbose	
		}
		else
		{
			$MessageTextBVMS = "-> BVMS is NOT installed"
			Write-Log -Message $MessageTextBVMS -Type Verbose
		}

		# check if BVMS Viewer is installed
		if (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\081A7C7456C7C414CBD5F9EC0EF744A4") 
		{
			Write-Log -Message "-> BVMS is installed" -Type Verbose
		}		
		else 
		{
			$MessageTextBVMSViewer = "-> BVMS Viewer is NOT installed"
			Write-Log -Message $MessageTextBVMSViewer -Type Verbose
		}

		if( ( -not $MessageTextBVMS) -and ( -not $MessageTextBVMSViewer) )
		{
			if ($Global:UnattendedMode -eq $false)
			{
				$Result = ShowMessageBoxYesNo -Message $MessageFailure -Header "$Global:BVMSHardeningTool, $Global:BVMSHardeningToolVersion : Check BVMS or BVMS Viewer installed" -MessageBoxIcon Exclamation
			}
			else
			{
				$Result = "Yes"
				Write-Log -Message "$($unattendedString): $MessageFailure" -Type Verbose
			}
			
			if ($Result -ne "Yes")
			{
				Write-Log -Message "Execution was canceled by the user because BVMS or BVMS Viewer is NOT installed." -Type Verbose
				throw "CheckBVMSorViewerInstalled failed!"
			}			
		}
	}
}

Function ShowMessageBoxOK
{
	<#    
		.Synopsis
			Shows a Message Box with OK Button
​​
		.Description
			Shows a Message Box with OK Button

		.Parameter $Message
			 The Message to be displayed

		.Parameter $Header
			 The Header to be displayed

		.Parameter $MessageBoxIcon
			 The Icon to be displayed

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$Message,

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

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

	Process
	{			
		if ($Global:UnattendedMode -eq $false)
		{
			[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
			[System.Windows.Forms.MessageBox]::Show($Message,$Header,[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::$MessageBoxIcon)
		}		
	}
}

Function ShowMessageBoxYesNo
{
	<#    
		.Synopsis
			Shows a Message Box with Yes and No Button
​​
		.Description
			Shows a Message Box with Yes and No Button, return the corresponding value

		.Parameter $Message
			 The Message to be displayed

		.Parameter $Header
			 The Header to be displayed

		.Parameter $MessageBoxIcon
			 The Icon to be displayed

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		[Parameter(Mandatory = $true)]
		[string]
		$Message,

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

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

	Process
	{
		[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
		$Result = [System.Windows.Forms.MessageBox]::Show($Message,$Header,[System.Windows.Forms.MessageBoxButtons]::YesNo,[System.Windows.Forms.MessageBoxIcon]::$MessageBoxIcon)
		
		return $Result
	}
}

Function ShowDisclaimerAskToContinue
{
	<#    
		.Synopsis
			Shows a Text Box with the Disclaimer and a Continue Button
​​
		.Description
			Shows a Text Box with the Disclaimer and a Continue Button, returns the corresponding value	
			Shows different layout if the Icon and the PNGs are available	

		.Example
	#>

	[CmdletBinding()]
	Param
	(
	
	)

	Process
	{
		# Create the Windows Form
		[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
		$form = New-Object Windows.Forms.Form

		# Include the BVMS icon if it exists in the same directory
		if ( (Test-Path "$Global:bvmsIcon") -and (Test-Path "$Global:panelBitmap") -and (Test-Path "$Global:panelBoschBitmap") )
		{            
            if ($Global:PowerShellVersion.Major -ge 4)
            {
                # PowerShell Major Version <= 3 -> System.Drawing.Icon not available
				$formIcon = New-Object System.Drawing.Icon ("$Global:bvmsIcon")
    			$form.Icon = $formIcon
            }

			# Settings for the Windows Form with Icons and PNGs
			$form.Width = "815"
			$form.Height = "512"

			# Creation and Settings for the Windows Forms Panel with Icons and PNGs
			$panel = New-Object System.Windows.Forms.Panel
            if ($Global:PowerShellVersion.Major -ge 4)
            {
    			# PowerShell Major Version <= 3 -> System.Drawing.Bitmap not available
				$panelPng = New-Object System.Drawing.Bitmap ("$Global:panelBitmap")            
    			$panel.BackgroundImage = $panelPng
            }
			$panel.Size = New-Object Drawing.Size @(802,10)
			$panel.Dock = "Top"
			$form.Controls.Add($panel)
			
		
			# Creation and Settings for the Windows Forms Panel with Icons and PNGs
			$panelBosch = New-Object System.Windows.Forms.Panel
            if ($Global:PowerShellVersion.Major -ge 4)
            {
    			# PowerShell Major Version <= 3 -> System.Drawing.Bitmap not available
				$panelBoschPng = New-Object System.Drawing.Bitmap ("$Global:panelBoschBitmap")
    			$panelBosch.BackgroundImage = $panelBoschPng
            }
			$panelBosch.Size = New-Object Drawing.Size @(159, 49)
			$panelBosch.Location = New-Object System.Drawing.Point(5, 10)
			$panelBosch.AutoSizeMode = "GrowOnly"
			$form.Controls.Add($panelBosch)			

			# Creation and Settings for the Windows Forms Label with Icons and PNGs
			$labelHardeningTool = New-Object System.Windows.Forms.Label
			$labelHardeningTool.Size = New-Object Drawing.Size @(385, 24)
			$labelHardeningTool.Location = New-Object System.Drawing.Point(257, 23)
			$labelHardeningTool.AutoSize = $true
			$labelHardeningTool.Text = "$Global:BVMSHardeningTool"
			$labelHardeningTool.Font = "Arial,15.75"
			$form.Controls.Add($labelHardeningTool)

			# Creation and Settings for the Windows Forms TextBox with Icons and PNGs
			$textBoxDisclaimer = New-Object System.Windows.Forms.TextBox
			$textBoxDisclaimer.Location = New-Object System.Drawing.Point(10, 60)
			$textBoxDisclaimer.Width = "778"
			$textBoxDisclaimer.Height = "140"

			# Creation and Settings for the Windows Forms TextBox with Icons and PNGs
			$richtextBoxDisclaimer = New-Object System.Windows.Forms.RichTextBox
			$richtextBoxDisclaimer.Location = New-Object System.Drawing.Point(10, 205)
			$richtextBoxDisclaimer.Width = "778"
			$richtextBoxDisclaimer.Height = "205"

			# Creation and Settings for the Windows Forms Checkbox with Icons and PNGs
            if ($Global:PowerShellVersion.Major -ge 4)
            {
    			# PowerShell Major Version <= 3 -> System.Windows.Forms.Checkbox not available
				$Checkbox = New-Object System.Windows.Forms.Checkbox 
    			$Checkbox.Location = New-Object System.Drawing.Size(10,415) 
    			$Checkbox.Size = New-Object System.Drawing.Size(520,20)
            }

			# Creation and Settings for the Windows Forms Button with Icons and PNGs
			$ContinueButton = New-Object System.Windows.Forms.Button
			$ContinueButton.Location = New-Object System.Drawing.Size(349,440)
			$ContinueButton.Size = New-Object System.Drawing.Size(100,23)
		}
		else
		{
			# Settings for the Windows Form without Icons and PNGs
			$form.Width = "815"
			$form.Height = "492"

			# Creation and Settings for the Windows Forms TextBox without Icons and PNGs
			$textBoxDisclaimer = New-Object System.Windows.Forms.TextBox
			$textBoxDisclaimer.Location = New-Object System.Drawing.Point(10, 5)
			$textBoxDisclaimer.Width = "778"
			$textBoxDisclaimer.Height = "140"

			# Creation and Settings for the Windows Forms TextBox without Icons and PNGs
			$richtextBoxDisclaimer = New-Object System.Windows.Forms.RichTextBox
			$richtextBoxDisclaimer.Location = New-Object System.Drawing.Point(10, 150)
			$richtextBoxDisclaimer.Width = "778"
			$richtextBoxDisclaimer.Height = "231"

			# Creation and Settings for the Windows Forms Checkbox without Icons and PNGs
			if ($Global:PowerShellVersion.Major -ge 4)
            {
    			# PowerShell Major Version <= 3 -> System.Windows.Forms.Checkbox not available
				$Checkbox = New-Object System.Windows.Forms.Checkbox 
				$Checkbox.Location = New-Object System.Drawing.Size(10,385) 
				$Checkbox.Size = New-Object System.Drawing.Size(500,20)
			}

			# Creation and Settings for the Windows Forms Button without Icons and PNGs
			$ContinueButton = New-Object System.Windows.Forms.Button
			$ContinueButton.Location = New-Object System.Drawing.Size(349,420)
		}

		# Common settings for the Windows Form
		$form.FormBorderStyle ="FixedSingle"
		$form.StartPosition = "CenterScreen"
		$form.Text = "{0}: Disclaimer" -f ("$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion)")
		$form.BackColor = "White"				

		# Creation and common Settings for the Windows Forms TextBox		
		$richtextBoxDisclaimer.Multiline = $true
		$richtextBoxDisclaimer.WordWrap = $true
		$richtextBoxDisclaimer.ScrollBars = "Vertical"
		$richtextBoxDisclaimer.AutoSize = $true		
		$richtextBoxDisclaimer.Font = "Arial,9.75"
		$richtextBoxDisclaimer.HideSelection = $true
		$richtextBoxDisclaimer.BackColor = "White"
		$richtextBoxDisclaimer.TabStop = $false
		$richtextBoxDisclaimer.ReadOnly = $true
 
		# Disclaimer Text for the Windows Forms TextBox
		# $richtextBoxDisclaimer.Text = $textBoxDisclaimerText -f $Global:BVMSHardeningTool

		$textBoxText = @'
The {0} secures the Windows Operating System by applying Microsoft recommended security parameters and other security settings.

NOTES:
* All security parameters applied by the Hardening Tool can be found in the “{0} User Guide”.
* The minimum password length will be set to 10 characters and must meet the Windows Operating System complexity requirements.
* The password will expire after 90 days.

With using the {0} you agree that you have read all warnings and accept the Disclaimer.
'@		

		$textBoxDisclaimer.Text = $textBoxText -f $Global:BVMSHardeningTool
		
		# Creation and common Settings for the Windows Forms TextBox		
		$textBoxDisclaimer.Multiline = $true
		$textBoxDisclaimer.WordWrap = $true
		#$textBoxDisclaimer.ScrollBars = "Vertical"
		$textBoxDisclaimer.AutoSize = $true		
		$textBoxDisclaimer.Font = "Arial,8.00"
		$textBoxDisclaimer.HideSelection = $true
		$textBoxDisclaimer.BackColor = "White"
		$textBoxDisclaimer.TabStop = $false
		$textBoxDisclaimer.ReadOnly = $true
		$form.Controls.Add($textBoxDisclaimer)
		
		# $appendText = $appendText -f $Global:BVMSHardeningTool
		
		$richtextBoxDisclaimer.LoadFile($Global:bvmsEula)
		# $richtextBoxDisclaimer.AppendText($appendText)
		$form.Controls.Add($richtextBoxDisclaimer)

		# Creation and common Settings for the Windows Forms Checkbox
        if ($Global:PowerShellVersion.Major -ge 4)
        {
    		# PowerShell Major Version <= 3 -> System.Windows.Forms.Checkbox not available
			$Checkbox.Font = "Arial,9.75"
    		$Checkbox.Text = "I have read all warnings and accept the Disclaimer"        
    		$Checkbox.Add_CheckStateChanged({
    			if ($Checkbox.Checked)
    			{
    				$ContinueButton.Enabled = $true
    			}
    			else
    			{
    				$ContinueButton.Enabled = $false
    			}
    		})
    		$form.Controls.Add($Checkbox)
        }

		# Creation and common Settings for the Windows Forms Button
		$ContinueButton.Size = New-Object System.Drawing.Size(100,23)
		$ContinueButton.Font = "Microsoft Sans Serif,8.25"
		$ContinueButton.Text = "Continue"
		$ContinueButton.BackColor = "ButtonHighlight"
		$ContinueButton.FlatAppearance.BorderSize = 1
		$ContinueButton.FlatStyle = "Standard"
		$ContinueButton.ForeColor = "ControlText"
		$ContinueButton.UseVisualStyleBackColor = $true
        if ($Global:PowerShellVersion.Major -ge 4)
        {
			# PowerShell Major Version <= 3 -> disable Continue button
			$ContinueButton.Enabled = $false
        }
        else
        {
            # PowerShell Major Version <= 3 -> enable Continue button per default
			$ContinueButton.Enabled = $true
        }
		$ContinueButton.Add_Click({$form.DialogResult = "Yes"})
		$form.Controls.Add($ContinueButton)
		
		if ($Global:UnattendedMode -eq $false)
		{
			# Show the dialog
			$Result = $form.ShowDialog()
			Write-Log -Message "Answer of the User for the Continue Request of the Disclaimer Dialog: '$Result'" -Type Verbose
		}
		else
		{
			$Result = "Yes"
			Write-Log -Message "$($unattendedString): Answer of the User for the Continue Request of the Disclaimer Dialog was implicitly: '$Result'" -Type Verbose						
		}
		
		return $Result
	}
}

Function CheckLGPOExeAvailable
{
	<#    
		.Synopsis
			Checks if the LGPO.exe is available at the expected path, if not the Script execution will be cancelled.
​​
		.Description
			Checks if the LGPO.exe is available at the expected path, if not it hints the user to download it and copy it to the expected path and cancels the script execution

		.Example
	#>

	[CmdletBinding()]
	Param
	(

	)

	Process
	{
		Write-Log -Message "Checking if 'LGPO.exe' is available ..." -Type Verbose

		if ( !(Test-Path "$Global:ScriptPath\LGPO.exe") )
		{
			Write-Log -Message "The Tool '$Global:ScriptPath\LGPO.exe' is not available" -Type Error

			$MessageText = "The Tool '$Global:ScriptPath\LGPO.exe' is not available, please download the LGPO.zip from the following URL:

							'https://www.microsoft.com/en-us/download/details.aspx?id=55319&751be11f-ede8-5a0c-058c-2ee190a24fa6=True',

							and extract the 'LGPO.exe' to the folder mentioned below:

							'$Global:ScriptPath'

							This script cannot continue, quit execution of the script !!!"

			ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check Tool 'LGPO.exe' available" -MessageBoxIcon Error
			throw "!!! The Tool '$Global:ScriptPath\LGPO.exe' is not available, quit execution of the script !!!"
		}
		else
		{
			Write-Log -Message "'$Global:ScriptPath\LGPO.exe' is available" -Type Verbose
		}
	}
}

Function CheckEULAAvailable
{
	<#    
		.Synopsis
			Checks if the Bosch_EULA.rtf is available at the expected path, if not the Script execution will be cancelled.
​​
		.Description
			Checks if the Bosch_EULA.rtf is available at the expected path, if not it hints the user and cancels the script execution

		.Example
	#>

	[CmdletBinding()]
	Param
	(

	)

	Process
	{
		Write-Log -Message "Checking if '$Global:bvmsEula' is available ..." -Type Verbose

		if ( !(Test-Path "$Global:bvmsEula") )
		{
			Write-Log -Message "The '$Global:bvmsEula' is not available" -Type Error

			$MessageText = "The '$Global:bvmsEula' is not available, please get the 'Bosch_EULA.rtf' and copy it to the folder mentioned below:

							'$Global:ScriptPath'

							This script cannot continue, quit execution of the script !!!"

			ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Check '$Global:bvmsEula' available" -MessageBoxIcon Error
			throw "!!! The '$Global:bvmsEula' is not available, quit execution of the script !!!"
		}
		else
		{
			Write-Log -Message "'$Global:bvmsEula' is available" -Type Verbose
		}
	}
}

Function RestartComputer
{
	<#    
		.Synopsis
			Restarts the PC depending on the User input
​​
		.Description
			Restarts the PC depending on the User input

		.Example
	#>

	[CmdletBinding()]
	Param
	(
		
	)

	Process
	{
		$Message = "The $Global:BVMSHardeningTool made all necessary changes to your computer.`r`nTo take the changes effect a Reboot is necessary.`r`n`r`n Do you want to Reboot now?"

		if ($Global:UnattendedMode -eq $false)
		{
			Write-Log -Message "'$Message'" -Type Verbose
			$Result = ShowMessageBoxYesNo -Message $Message -Header "$Global:BVMSHardeningTool, $Global:BVMSHardeningToolVersion" -MessageBoxIcon Exclamation		
		}
		else
		{
			$Result = "No"
			Write-Log -Message "$($unattendedString): $Message" -Type Verbose			
		}
		
		if ($Result -eq "Yes")
		{			
			if ($Global:UnattendedMode -eq $false)
			{
				Write-Log -Message "Answer of the User for the Reboot Request was 'Yes'" -Type Verbose
			}
			else
			{				
				Write-Log -Message "$($unattendedString): Answer of the User for the Reboot Request was implicitly: '$Result'" -Type Verbose						
			}
						
			if ($Global:UnattendedMode -eq $false)
			{
				Restart-Computer -Force	
			}			
		}
		else
		{			
			if ($Global:UnattendedMode -eq $false)
			{
				Write-Log -Message "Answer of the User for the Reboot Request was 'No'" -Type Verbose
			}
			else
			{				
				Write-Log -Message "$($unattendedString): Answer of the User for the Reboot Request was implicitly: 'No'" -Type Verbose						
			}
		}
	}
}

Function Write-Log()
{
    [CmdletBinding()]
    Param
	(
		[Parameter(Mandatory=$False)]
		[ValidateSet("Host","Warning","Verbose","Error")]
		[String]
		$Type = "Host",

		[Parameter(Mandatory=$True)]
		[string]
		$Message
    )

    Process
    {
        $logTime = $(Get-Date -f G)
        $logMessage = "$logTime $Message"

		if ( -not (Test-Path "$Global:LogFile") )
		{
			New-Item "$Global:LogFile" -type file | Out-Null
		}
		
        switch ($Type) 
        { 
            "Warning" 
						{ 
							Write-Warning $logMessage
							Add-Content "$Global:LogFile" $logMessage
						} 
            "Verbose" 
						{ 
							Write-Verbose $logMessage
							Add-Content "$Global:LogFile" $logMessage
						} 
            "Error" 
						{ 
							Write-Error $logMessage
							Add-Content "$Global:LogFile" $logMessage
						}
            "Host" 
						{ 
							Write-Host $logMessage
							Add-Content "$Global:LogFile" $logMessage
						}
			default 
						{ 
							Write-Host $logMessage
							Add-Content "$Global:LogFile" $logMessage
						}
        }
    }
}




# ============================================================================
# ================================= MAIN =====================================
# ============================================================================


    
<# 
	.Synopsis
		Sets the Local Policies for BVMS System
​​
	.Description
		Sets the Local Policies for BVMS System as described in the 'Bosch IP Video and Data Security Guidebook' in 
		section 7 'Hardening Servers' and 
		section 8 'Hardening Clients'
		
	.Example
		BvmsOsHardeningTool.ps1 -Verbose

		powershell.exe -noprofile -executionpolicy Unrestricted -file .\BvmsOsHardeningTool.ps1 -Verbose			
#>

# Definition of logging path and file name, please don't change the path and file name as it will be searched and included 
# from the BVMS ConfigCollector as well
$LogPath = "$env:ProgramData\Bosch\VMS\Log\BvmsOsHardeningTool"

if ( -not (Test-Path -Path $LogPath -PathType Container) )
{
	New-Item -ItemType Directory -Force -Path $LogPath
}
$Global:LogFile = "$LogPath\BvmsOsHardeningTool.log"

Write-Host -BackgroundColor Black -ForegroundColor Yellow @'

                                 ______________   _________    _________                                    
                                 \______   \   \ /   /     \  /   _____/                                    
                                  |    |  _/\   Y   /  \ /  \ \_____  \                                     
                                  |    |   \ \     /    Y    \/        \                                    
                                  |______  /  \___/\____|__  /_______  /                                    
                                         \/                \/        \/                                     
________                              __  .__                   _________               __                  
\_____  \ ______   ________________ _/  |_|__| ____    ____    /   _____/__.__. _______/  |_  ____   _____  
 /   |   \\____ \_/ __ \_  __ \__  \\   __\  |/    \  / ___\   \_____  <   |  |/  ___/\   __\/ __ \ /     \ 
/    |    \  |_> >  ___/|  | \// __ \|  | |  |   |  \/ /_/  >  /        \___  |\___ \  |  | \  ___/|  Y Y  \
\_______  /   __/ \___  >__|  (____  /__| |__|___|  /\___  /  /_______  / ____/____  > |__|  \___  >__|_|  /
        \/|__|        \/           \/             \//_____/           \/\/         \/            \/      \/ 
         ___ ___                  .___            .__                 ___________           .__             
        /   |   \_____ _______  __| _/____   ____ |__| ____    ____   \__    ___/___   ____ |  |            
       /    ~    \__  \\_  __ \/ __ |/ __ \ /    \|  |/    \  / ___\    |    | /  _ \ /  _ \|  |            
       \    Y    // __ \|  | \/ /_/ \  ___/|   |  \  |   |  \/ /_/  >   |    |(  <_> |  <_> )  |__          
        \___|_  /(____  /__|  \____ |\___  >___|  /__|___|  /\___  /    |____| \____/ \____/|____/          
              \/      \/           \/    \/     \/        \//_____/                                         


To enable Scripts to be running in Powershell use the command 'Set-Executionpolicy -Scope Process -ExecutionPolicy Bypass -Force -Verbose'
This script needs Administrator rights, please launch your Powershell command line windows with 'Run as Administrator' and will enable the execution rights only for the current Process call

To run the script in unattended mode use the following command line:
.\BvmsOsHardeningTool.ps1 -RunUnattendedAndAcceptDisclaimer $true -Verbose

'@
	
	# Definition of global variables
	$Global:BVMSHardeningTool = "BVMS Operating System Hardening Tool"
	$Global:BVMSHardeningToolVersion = "Version 11.0.0.9"

	$Global:ScriptPath = get-location
	$Global:PowerShellVersion = $PSVersionTable.PSVersion

	$Global:bvmsEula = "$Global:ScriptPath\Bosch_EULA.rtf"
	$Global:bvmsIcon = "$Global:ScriptPath\BVMS_Client.ico"
	$Global:panelBitmap = "$Global:ScriptPath\Supergraphic_800x8.png"
	$Global:panelBoschBitmap = "$Global:ScriptPath\Logo_128px_transparent_mit_Raum.png"

	Write-Log -Message "====================================================="
	Write-Log -Message "$Global:BVMSHardeningTool, $Global:BVMSHardeningToolVersion"
	Write-Log -Message "====================================================="
	Write-Log -Message "ScriptPath = $Global:ScriptPath" -Type Verbose
	Write-Log -Message "LogFile = $Global:LogFile" -Type Verbose
	Write-Log -Message "PowerShellVersion = $Global:PowerShellVersion" -Type Verbose
	
	$Global:UnattendedMode = $RunUnattendedAndAcceptDisclaimer
	if ($Global:UnattendedMode -eq $true)
	{
		Write-Log -Message "$unattendedString - Disclaimer was accepted implicitly by the user" -Type Verbose		
	}
		
	# Check if we run as Administrator, if not restart with RunAs Administrator
	if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
	{ 
		$AdminScriptStart = $true

		$MessageText = "This script can only be executed with RunAs Administrator / or Administrator rights."
		Write-Log -Message $MessageText -Type Warning

		if ($Global:UnattendedMode -eq $false)
		{
			ShowMessageBoxOK -Message $MessageText -Header "$($Global:BVMSHardeningTool, $Global:BVMSHardeningToolVersion): Administrator rights necessary" -MessageBoxIcon Exclamation
		}		
	}
	else
	{				
		# Check some Launch Conditions	
		# Check if the Bosch_EULA.rtf is available
		CheckEULAAvailable

		# Check if an external Tool (LGPO.exe) from Microsoft is available
		CheckLGPOExeAvailable

		# Check and display the status of the Windows Firewall
		CheckFirewallStatus

		# Check ProductType and Version of the Operating System
		CheckOSProductType
		CheckOSVersion
		
		# Check if PC in Domain joined
		CheckDomainJoined			

		# Display Disclaimer before changes were done to the target system and ask user if he want`s to continue.
		$Result = ShowDisclaimerAskToContinue		
		
		if ($Result -eq "Yes")
		{
			# Set some variables
			$Global:TempFolder = $env:TEMP
			Write-Log -Message "TempFolder = '$Global:TempFolder'" -Type Verbose
			$Global:Sys32Folder = $("$env:SystemRoot\System32")
			Write-Log -Message "System32 Folder = '$Global:Sys32Folder'" -Type Verbose
			
			if ($Global:OSProductType -eq 1)
			{
				$LGPOContext = "Client"
			}
			else
			{
				$LGPOContext = "Server"
			}
		
			# Start the applying the Local Group Policies
			Write-Log -Message " "
			Write-Log -Message "Starting to apply the Local Group Policies for '$LGPOContext'." -Type Verbose

			try
			{
				# all errors shall be terminating
				$ErrorActionPreference = "Stop";

				DisableWindowsServices -LGPOContext $LGPOContext

				Write-Log -Message " "
				Write-Log -Message "========================================================="
				Write-Log -Message "'Apply Administrative Templates - Computer Configuration'"
				Write-Log -Message "========================================================="
				ApplyAdministrativeTemplates -LGPOTextFile "WinMachineReg.txt" -Verbose

				Write-Log -Message " "
				Write-Log -Message "====================================================="
				Write-Log -Message "'Apply Administrative Templates - User Configuration'"
				Write-Log -Message "====================================================="				
				ApplyAdministrativeTemplates -LGPOTextFile "WinUserReg.txt" -Verbose

				ApplyRegistryPolicies -LGPOContext $LGPOContext -Verbose

				Write-Log -Message " "
				Write-Log -Message "======================================"
				Write-Log -Message "'Secedit.exe: Apply Security Policies'"
				Write-Log -Message "======================================"
				Write-Log -Message "Local Group Policy -> Windows Settings -> Security Settings -> Account Policies -> Password Policy" -Type Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "PasswordHistorySize" -PolicyValue "10" -PolicyDescription "Enforce password history" -PolicyValueDescription "10 passwords remembered" -Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "MaximumPasswordAge" -PolicyValue "90" -PolicyDescription "Maximum Password Age" -PolicyValueDescription "90 days" -Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "MinimumPasswordAge" -PolicyValue "1" -PolicyDescription "Minimum Password Age" -PolicyValueDescription "1 day" -Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "MinimumPasswordLength" -PolicyValue "10" -PolicyDescription "Minimum Password Length" -PolicyValueDescription "10 characters" -Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "PasswordComplexity" -PolicyValue "1" -PolicyDescription "Password must meet complexity requirements" -PolicyValueDescription "Enabled" -Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "ClearTextPassword" -PolicyValue "0" -PolicyDescription "Store password using reversible encryption for all users in the domain" -PolicyValueDescription "Disabled" -Verbose
				Write-Log -Message "Local Group Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Account Policies -> Account Lockout Policy" -Type Verbose
				ApplySecurityPoliciesWithSecedit -PolicyName "LockoutBadCount" -PolicyValue "10" -PolicyDescription "Account lockout threshold" -PolicyValueDescription "10 invalid logon attempts and 15 minutes for Account lockout duration and Reset account lockout counter after 15 minutes" -Verbose
				
				Write-Log -Message " "
				Write-Log -Message "================================"
				Write-Log -Message "'Secedit.exe: Apply User Rights'"
				Write-Log -Message "================================"
				Write-Log -Message "Local Group Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment" -Type Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeTrustedCredManAccessPrivilege" -PolicyValue "" -PolicyDescription "Access Credential Manager as a trusted caller" -PolicyValueDescription "No one" -Verbose				
				ApplyUserRightsWithSecedit -PolicyName "SeNetworkLogonRight" -PolicyValue "*S-1-5-11" -PolicyDescription "Access this computer from the network" -PolicyValueDescription "Authenticated users" -Verbose				
				ApplyUserRightsWithSecedit -PolicyName "SeTcbPrivilege" -PolicyValue "" -PolicyDescription "Act as part of the operating system" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeMachineAccountPrivilege" -PolicyValue "" -PolicyDescription "Add workstations to domain" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeRemoteInteractiveLogonRight" -PolicyValue "*S-1-5-32-544,*S-1-5-32-555" -PolicyDescription "Allow log on through Remote Desktop Services" -PolicyValueDescription "Administrators, Remote Desktop Users" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeSystemtimePrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Change the system time" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeTimeZonePrivilege" -PolicyValue "*S-1-5-32-544,*S-1-5-19" -PolicyDescription "Change the time zone" -PolicyValueDescription "Administrators, Local Service" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeCreatePagefilePrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Create a page file" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeCreateTokenPrivilege" -PolicyValue "" -PolicyDescription "Create a token object" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeCreatePermanentPrivilege" -PolicyValue "" -PolicyDescription "Create permanent shared objects" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeDenyNetworkLogonRight" -PolicyValue "*S-1-5-32-546,*S-1-5-7" -PolicyDescription "Deny access to this computer from the network" -PolicyValueDescription "Anonymous Logon, Guests" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeDenyBatchLogonRight" -PolicyValue "*S-1-5-32-546,*S-1-5-7" -PolicyDescription "Deny log on as a batch job" -PolicyValueDescription "Anonymous Logon, Guests" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeDenyServiceLogonRight" -PolicyValue "" -PolicyDescription "Deny log on as a service" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeDenyInteractiveLogonRight" -PolicyValue "*S-1-5-32-546,*S-1-5-7" -PolicyDescription "Deny log on locally" -PolicyValueDescription "Anonymous Logon, Guests" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeDenyRemoteInteractiveLogonRight" -PolicyValue "*S-1-5-32-546,*S-1-5-7" -PolicyDescription "Deny log on through Remote Desktop Services" -PolicyValueDescription "Anonymous Logon, Guests" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeEnableDelegationPrivilege" -PolicyValue "" -PolicyDescription "Enable computer and user accounts to be trusted for delegation" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeRemoteShutdownPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Force shutdown from a remote system" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeAuditPrivilege" -PolicyValue "*S-1-5-19,*S-1-5-20" -PolicyDescription "Generate security audits" -PolicyValueDescription "Local Service, Network Service" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeIncreaseBasePriorityPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Increase scheduling priority" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeLoadDriverPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Load and unload device drivers" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeRelabelPrivilege" -PolicyValue "" -PolicyDescription "Modify an object label" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeSystemEnvironmentPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Modify firmware environment values" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeManageVolumePrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Perform volume maintenance tasks" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeProfileSingleProcessPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Profile single process" -PolicyValueDescription "Administrators" -Verbose				
				ApplyUserRightsWithSecedit -PolicyName "SeUndockPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Remove computer from docking station" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeRestorePrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Restore files and directories" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeShutdownPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Shut down the system" -PolicyValueDescription "Administrators" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeSyncAgentPrivilege" -PolicyValue "" -PolicyDescription "Synchronize directory service data" -PolicyValueDescription "No one" -Verbose
				ApplyUserRightsWithSecedit -PolicyName "SeTakeOwnershipPrivilege" -PolicyValue "*S-1-5-32-544" -PolicyDescription "Take ownership of files or other objects" -PolicyValueDescription "Administrators" -Verbose

				RestartComputer				
			}
			catch
			{
				$failureHeader = "Executing the script failed with Error: '$($_.Exception.Message)'"
				$ErrorDescription = "$($failureHeader): '$($_.Exception.Message)'"			
				Write-Log -Message "$ErrorDescription" -Type Verbose
				$global:failedActions += "$ErrorDescription"				

				if ($Global:UnattendedMode -eq $false)
				{
					[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
					[System.Windows.Forms.MessageBox]::Show($ErrorDescription,"$($Global:BVMSHardeningTool), $($Global:BVMSHardeningToolVersion): Error",[System.Windows.Forms.MessageBoxButtons]::Ok,[System.Windows.Forms.MessageBoxIcon]::Error)
				}				
			}

			if ($AdminScriptStart)
			{
				$SecondsToWait = 20
				Write-Log -Message "This Window will be closed in $SecondsToWait seconds" -Type Verbose
				Start-Sleep -s $SecondsToWait
			}
		}
		else
		{
			Write-Log -Message "Execution was canceled by the user." -Type Verbose
		}
	}

	# Write the Failures (if there are any) to the log
    if ($global:failedActions.Count -gt 1)
    {                                		
        # log the failedActions		
		Foreach ($failedAction in $global:failedActions)
		{
 			Write-Log -Message "$failedAction" -Type Verbose
		}
                        
        $ErrorCode = $global:failedActions.Count - 1;
        Write-Log -Message "BvmsOsHardeningTool.ErrorCode: $ErrorCode" -Type Verbose
    }
    else
    {
        # Success message
        Write-Log -Message "The BVMS Operating System Hardening Tool has finished successfully." -Type Verbose
        $ErrorCode = 0;
        Write-Log -Message "BvmsOsHardeningTool.ErrorCode: $ErrorCode" -Type Verbose
    }
			
	Write-Log -Message "===================================================="
	Write-Log -Message "================== End of Logging =================="
	Write-Log -Message "===================================================="

	Exit $ErrorCode
	
# SIG # Begin signature block
# MIINHgYJKoZIhvcNAQcCoIINDzCCDQsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU9MNrHiF2t5crkbwUVYGV5OhS
# L9ugggpVMIIFBjCCA+6gAwIBAgIQMmR8RfHxmGOEHNIKufzH4DANBgkqhkiG9w0B
# AQsFADCBhDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0
# aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTUwMwYDVQQDEyxT
# eW1hbnRlYyBDbGFzcyAzIFNIQTI1NiBDb2RlIFNpZ25pbmcgQ0EgLSBHMjAeFw0x
# OTA2MTkwMDAwMDBaFw0yMTA2MTgyMzU5NTlaMIGXMQswCQYDVQQGEwJERTEPMA0G
# A1UECAwGQmF5ZXJuMRIwEAYDVQQHDAlHcmFzYnJ1bm4xJjAkBgNVBAoMHUJvc2No
# IFNpY2hlcmhlaXRzc3lzdGVtZSBHbWJIMRMwEQYDVQQLDApCVC1TQy9QQVM0MSYw
# JAYDVQQDDB1Cb3NjaCBTaWNoZXJoZWl0c3N5c3RlbWUgR21iSDCCASIwDQYJKoZI
# hvcNAQEBBQADggEPADCCAQoCggEBAK9ZfmkwyluRqkQ0Sm11xwprJNzLdDXX4ha8
# JN276h7k+k5cxdNQOC2VL+f/uZnBtHVCEKF/ANZMTCZmToH5K56ydzMir3/T46VF
# TqRLeOIEBpMpY7ZXCacWeQElqMGPV+A9Y0qD/sQwgCqHY75D9OKxQYLuVLHHvFKu
# sRhrbUQlWdmGSTpZFy/9SUpAlgFgUuH8sIlgAvvBVOG/0Lqe7Szq26Hbj6WM8GGQ
# tBJZqEpMczm9XgbZHb3CzKRIPTBqKC+0llM2JOd4NE9m9sdRSEERr7lA6U56UOM3
# 45Ia9Pa2ICqu5pcFzxvrUO/wV7iKuHSh6RKe4U0XAA6VN2dhst0CAwEAAaOCAV0w
# ggFZMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMCsGA1UdHwQkMCIwIKAeoByG
# Gmh0dHA6Ly9yYi5zeW1jYi5jb20vcmIuY3JsMGEGA1UdIARaMFgwVgYGZ4EMAQQB
# MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUF
# BwICMBkMF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMBMGA1UdJQQMMAoGCCsGAQUF
# BwMDMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDovL3JiLnN5bWNk
# LmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3JiLnN5bWNiLmNvbS9yYi5jcnQwHwYD
# VR0jBBgwFoAU1MAGIknrOUvdk+JcobhHdglyA1gwHQYDVR0OBBYEFF6tfV+TQM5L
# Dg1wHNU24+Ijvf9lMA0GCSqGSIb3DQEBCwUAA4IBAQAaCVGSnpdKGl3b8/qiI3B+
# 4hNHb9/5rkW0QpSXOQPcFGkyzJKU8tvbD28ETMUw9YGd8YzSTPvL+NJh2lEjMZ0i
# PH5NBackkb5nN4J11jZXyBM1QbDlkaC0RU81aHHR4Qhr92O92Z1gzXrJ7pP6HE2h
# qbd4aN79b44fuZEUxHwvIytd0MRy6PmETU4Ng9jspD5e1ifq6nHhhItckxcj4Xef
# n+WNe2eW/RYLJQX49369Fer3fHACoL2ya3K2IdIYKhm8kdEQioU5fDg5F6Qvc97s
# oT5jIJlE99oLNcYq+AlcAEMMoK7amI525RsqTSgrAGz74/FY/EXnC5gB79K3LSmn
# MIIFRzCCBC+gAwIBAgIQfBs1NUrn23TnQV8RacprqDANBgkqhkiG9w0BAQsFADCB
# vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
# ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
# U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
# ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
# Fw0xNDA3MjIwMDAwMDBaFw0yNDA3MjEyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEd
# MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj
# IFRydXN0IE5ldHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU0hBMjU2
# IENvZGUgU2lnbmluZyBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
# CgKCAQEA15VD1NzfZ645+1KktiYxBHDpt45bKro3aTWVj7vAMOeG2HO73+vRdj+K
# Vo7rLUvwVxhOsY2lM9MLdSPVankn3aPT9w6HZbXerRzx9TW0IlGvIqHBXUuQf8BZ
# TqudeakC1x5JsTtNh/7CeKu/71KunK8I2TnlmlE+aV8wEE5xY2xY4fAgMxsPdL5b
# yxLh24zEgJRyu/ZFmp7BJQv7oxye2KYJcHHswEdMj33D3hnOPu4Eco4X0//wsgUy
# GUzTsByf/qV4IEJwQbAmjG8AyDoAEUF6QbCnipEEoJl49He082Aq5mxQBLcUYP8N
# UfSoi4T+IdpcXn31KXlPsER0b21y/wIDAQABo4IBeDCCAXQwLgYIKwYBBQUHAQEE
# IjAgMB4GCCsGAQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wEgYDVR0TAQH/BAgw
# BgEB/wIBADBmBgNVHSAEXzBdMFsGC2CGSAGG+EUBBxcDMEwwIwYIKwYBBQUHAgEW
# F2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8v
# ZC5zeW1jYi5jb20vcnBhMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNi
# LmNvbS91bml2ZXJzYWwtcm9vdC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDgYD
# VR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BL
# SS0xLTcyNDAdBgNVHQ4EFgQU1MAGIknrOUvdk+JcobhHdglyA1gwHwYDVR0jBBgw
# FoAUtnf6aUhHn1MS1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQELBQADggEBAH/ryqfq
# i3ZC6z6OIFQw47e53PpIPhbHD0WVEM0nhqNm8wLtcfiqwlWXkXCD+VJ+Umk8yfHg
# lEaAGLuh1KRWpvMdAJHVhvNIh+DLxDRoIF60y/kF7ZyvcFMnueg+flGgaXGL3FHt
# gDolMp9Er25DKNMhdbuX2IuLjP6pBEYEhfcVnEsRjcQsF/7Vbn+a4laS8ZazrS35
# 9N/aiZnOsjhEwPdHe8olufoqaDObUHLeqJ/UzSwLNL2LMHhA4I2OJxuQbxq+CBWB
# Xesv4lHnUR7JeCnnHmW/OO8BSgEJJA4WxBR5wUE3NNA9kVKUneFo7wjw4mmcZ26Q
# CxqTcdQmAsPAWiMxggIzMIICLwIBATCBmTCBhDELMAkGA1UEBhMCVVMxHTAbBgNV
# BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVz
# dCBOZXR3b3JrMTUwMwYDVQQDEyxTeW1hbnRlYyBDbGFzcyAzIFNIQTI1NiBDb2Rl
# IFNpZ25pbmcgQ0EgLSBHMgIQMmR8RfHxmGOEHNIKufzH4DAJBgUrDgMCGgUAoHAw
# EAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK
# KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFLYyQNnF
# Hm1DXHKynZfg5Krn/3vUMA0GCSqGSIb3DQEBAQUABIIBAKWdnjY+zq9g0RPwsMW8
# Nek461oa2IYtNI9XgVpwsTwxyJNZ4LwOLO1UHz4qmyx1W8qn9zouaS7O1Vovuu2X
# sTiDLb3Xax6yvggdN0Kjqo7E5NMziPFaq75v/OjlEvURiWCZtBqqHJPxHWtcBpS8
# /UcutLNf7XFSrzHfiOw/znSld2/Kz3dLc6t8eEwhomsUyj9hPdOngBQmbD801wID
# b0HGGdKaVZATj/1al2IbRYXJ9GHhbg5KQ2Ej9V8LNgpmy56SSsPoALkJdhjFXH9N
# LdphtE/ZlEbFc8eMtuTP9I/yENrINzLugq8P66wPv/tF1NTQUhHpk6RRInoBxZwM
# jrQ=
# SIG # End signature block
