using namespace Microsoft.PowerShell.Commands

#region Initialization

$RootPath = Split-Path $MyInvocation.MyCommand.Path -Parent
. "$RootPath\Helpers\HashHelper.ps1"

$script:atapReportsPath = $env:ATAPReportPath
if (-not $script:atapReportsPath) {
	$script:atapReportsPath = [Environment]::GetFolderPath('MyDocuments') | Join-Path -ChildPath 'ATAPReports'
}

# for license status function. if called multiple times the cache will be used
$LicenseStatusCache = $null
#endregion

#region Classes
class AuditTest {
	[string] $Id
	[string] $Task
	[hashtable[]] $Constraints
	[scriptblock] $Test
}

enum AuditInfoStatus {
	True
	False
	Warning
	None
	Error
}

class AuditInfo {
	[string] $Id
	[string] $Task
	[AuditInfoStatus] $Status
	[string] $Message
}

class ReportSection {
	[string] $Title
	[string] $Description
	[AuditInfo[]] $AuditInfos
	[ReportSection[]] $SubSections
}

class Report {
	[string] $Title
	[string] $ModuleName
	[string] $AuditorVersion
	[hashtable] $HostInformation
	[string[]] $BasedOn
	[ReportSection[]] $Sections
	[RSFullReport] $RSReport
	[FoundationReport] $FoundationReport
}


###################################################
#######    SYSTEM INFORMATION Classes    ##########
###################################################
class SystemInformation {
	[SoftwareInformation] $SoftwareInformation
	[HardwareInformation] $HardwareInformation
}

class SoftwareInformation {
	[string] $Hostname
	[string] $SystemUptime
	[string] $OperatingSystem
	[string] $BuildNumber
	[string] $OSArchitecture
	[string] $LicenseStatus
	[string] $InstallationLanguage
	[string] $DomainRole
	[string] $KernelVersion
}

class HardwareInformation {
	[string] $SystemManufacturer
	[string] $SystemSKU
	[string] $SystemModel
	[string] $SystemSerialnumber
	[string] $BiosVersion
	[string] $FreeDiskSpace
	[string] $FreePhysicalMemory
}
### Begin Foundation Classes ###
class FoundationReport {
	[ReportSection[]] $Sections
}
### End Foundation Classes

# RiskScore Classes
enum RSEndResult {
	Critical
	High
	Medium
	Low
	Unknown
}

class RSFullReport {
	[RSSeverityReport] $RSSeverityReport
	[RSQuantityReport] $RSQuantityReport
}

class RSSeverityReport {
	[AuditInfo[]] $AuditInfos
	[ResultTable[]] $ResultTable
	[RSEndResult] $Endresult
}

class RSQuantityReport {

}

class ResultTable {
	[int] $Success
	[int] $Failed
}

#endregion

#region helpers
function IsIn-FullLanguageMode {
	try {
		$languageMode = $ExecutionContext.SessionState.LanguageMode
		if ($languageMode -eq "FullLanguage") {
			return $true
		}
	}
 catch {
		return $false
	}
	# returns alternate language modes if not FullLanguage
	return $languageMode
}

function Start-ModuleTest {
	$moduleList = @(Get-Module -ListAvailable).Name | Select-Object -Unique
	$necessaryModules = @(
		"Microsoft.PowerShell.LocalAccounts",
		"Microsoft.PowerShell.Management",
		"Microsoft.PowerShell.Security",
		"Microsoft.PowerShell.Utility",
		"TrustedPlatformModule",
		"NetSecurity",
		"CimCmdlets",
		"SmbShare",
		"Defender",
		"DISM"
		#Modules only necessary for specific server tests
		#"IISAdministration",
		#"SQLServer",
	)
	$missingModules = @()
	foreach ($module in $necessaryModules) {
		if ($moduleList -notcontains $module) {
			$missingModules += $module
		}
	}
	if ($missingModules.Count -gt 0) {
		Write-Warning "Missing module(s) found. Missing modules can lead to errors. Following modules are missing:"
		for ($i = 0; $i -lt $missingModules.Count; $i++) {
			Write-Warning $missingModules[$i]
		}
		Write-Warning "Check out this link on how to install modules: https://learn.microsoft.com/en-us/powershell/module/powershellget/install-module?view=powershellget-3.x"
	}

}

function Get-LicenseStatus {
	param(
		$SkipLicenseCheck
	)
	if ($LicenseStatusCache) {
		return $LicenseStatusCache
	}
	
	if ($SkipLicenseCheck -eq $true) {
		$LicenseStatusCache = "License check has been skipped."
		return $LicenseStatusCache
	}

	Write-Host "Checking operating system activation status. This may take a while..."
	$license = Get-CimInstance SoftwareLicensingProduct -Filter "Name like 'Windows%'" | Where-Object { $_.PartialProductKey } | Select-Object -First 1
	$LicenseStatusCache = switch ($license.LicenseStatus) {
		"0" { "Unlicensed" }
		"1" { "Licensed" }
		"2" { "OOBGrace" }
		"3" { "OOTGrace" }
		"4" { "NonGenuineGrace" }
		"5" { "Notification" }
		"6" { "ExtendedGrace" }
	}
	return $LicenseStatusCache
}

function IsIIS10Executable {
	if ((Get-Module -ListAvailable IISAdministration) -eq $null) {
		return $false
	}
	return $true
}

function Test-ArrayEqual {
	[OutputType([bool])]
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[AllowNull()]
		[AllowEmptyCollection()]
		[array]
		$Array1,

		[Parameter(Mandatory = $true)]
		[AllowNull()]
		[AllowEmptyCollection()]
		[array]
		$Array2
	)

	if ($null -eq $Array1) {
		$Array1 = @()
	}

	if ($null -eq $Array2) {
		$Array2 = @()
	}

	if ($Array1.Count -ne $Array2.Count) {
		return $false
	}

	foreach ($a in $Array1) {
		if ($a -notin $Array2) {
			return $false
		}
	}
	return $true
}

# Get domain role
# 0 {"Standalone Workstation"}
# 1 {"Member Workstation"}
# 2 {"Standalone Server"}
# 3 {"Member Server"}
# 4 {"Backup Domain Controller"}
# 5 {"Primary Domain Controller"}
function Get-DomainRole {
	$domainRole = (Get-CimInstance -Class Win32_ComputerSystem).DomainRole
	switch ($domainRole) {
		0 { $result = "Standalone Workstation" }
		1 { $result = "Member Workstation" }
		2 { $result = "Standalone Server" }
		3 { $result = "Member Server" }
		4 { $result = "Backup Domain Controller" }
		5 { $result = "Primary Domain Controller" }
	}
	return $result
}

function checkReportNameWithOSSystem {
	[CmdletBinding()]
	param (
		[Parameter()]
		[string]
		$ReportName
	)
	# helpers
	function handleReportNameDiscrepancy {
		param (
			[Parameter()]
			[string]
			$ReportName,
			[Parameter()]
			[string]
			$OsName,
			[Parameter()]
			[bool]
			$ShouldBeStandAlone = $False
		)
		if ($ShouldBeStandAlone -eq $True) {
			Write-Host "You chose the Reportname $ReportName but the operating system is domain-joined. Be aware that a different report type could affect the result."
		} 
		else {
			Write-Host "You chose the Reportname $ReportName but the operating system is $OsName. Be aware that a different report type could affect the result."
		}
		Write-Host ""
		Write-Host "Choose one of the following options:"
		Write-Host "[1] Continue     [2] Exit Script" -ForegroundColor Yellow
		$in = Read-Host
		switch ($in) {
			1 { 
				Write-Host "You chose to continue" 
				return $ReportName
			}
			2 { 
				Write-Host "You chose to exit the script" 
				return "Exit"
			}
			default { 
				Write-Host "Your input was invalid, call Save-ATAPHtmlReport again with your desired report" 
				return "Exit"
			}
		}
	}
	function returnSuitingReportName {
		[CmdletBinding()]
		param (
			[Parameter()]
			[string]
			$ReportName,
			[Parameter()]
			[string]
			$OsName,
			[Parameter()]
			[string]
			$OsType,
			[Parameter()]
			[bool]
			$ShouldBeStandAlone = $False
		)

		###
		# similarity check
		function isOsNameSimilarToType {
			[CmdletBinding()]
			param (
				[Parameter()]
				[string]
				$OsName,
				[Parameter()]
				[string]
				$OsType
			)
			if ($OsName -match $OsType) {
				return $true
			}
			return $false
		}
		if (-not(isOsNameSimilarToType -OsName $osName -OsType $osType)) {
			return handleReportNameDiscrepancy -ReportName $ReportName -OsName $osName
		}

		###
		# should be standalone
		if ($ShouldBeStandAlone -eq $True) {
			function IsDomainedJoined {		
				if ((Get-CimInstance win32_computersystem).partofdomain) {
					return $true
				} 
				return $false
			}
			$isDomainJoined = IsDomainedJoined
			if ($isDomainJoined -eq $True) {
				return handleReportNameDiscrepancy -ReportName $ReportName -OsName $osName -ShouldBeStandAlone $True
			}
		}
		return $ReportName
	}
	#helpers end
	try {
		$osName = (Get-ComputerInfo OsName).OsName
		if ([string]::IsNullOrEmpty($osName)) {
			return $ReportName # return initial ReportName and skip comparison
		}
		function Get-OsType {		
			switch ($ReportName) {
				"Microsoft Windows Server 2025" { return "Microsoft Windows Server 2025" }
				"Microsoft Windows Server 2022" { return "Microsoft Windows Server 2022" }
				"Microsoft Windows Server 2019" { return "Microsoft Windows Server 2019" }
				"Microsoft Windows Server 2016" { return "Microsoft Windows Server 2016" }
				"Microsoft Windows Server 2012" { return "Microsoft Windows Server 2012" }
				"Microsoft Windows 11" { return "Microsoft Windows 11" }
				"Microsoft Windows 11 Stand-alone" { return "Microsoft Windows 11" }
				"Microsoft Windows 10" { return "Microsoft Windows 10" }
				"Microsoft Windows 10 Stand-alone" { return "Microsoft Windows 10" }
				"Microsoft Windows 10 GDPR" { return "Microsoft Windows 10" }
				"Microsoft Windows 10 BSI" { return "Microsoft Windows 10" }
				"Microsoft Windows 7" { return "Microsoft Windows 7" }
			}
		}
		$osType = Get-OsType
		switch ($ReportName) {
			"Microsoft Windows Server 2025" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType
			}
			"Microsoft Windows Server 2022" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType
			}
			"Microsoft Windows Server 2019" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows Server 2016" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows Server 2012" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows 11" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows 11 Stand-alone" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType -ShouldBeStandAlone $True
			}
			"Microsoft Windows 10" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows 10 Stand-alone" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType -ShouldBeStandAlone $True
			}
			"Microsoft Windows 10 GDPR" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows 10 BSI" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
			"Microsoft Windows 7" { 
				return returnSuitingReportName -ReportName $ReportName -OsName $osName -OsType $osType 
			}
		}
		return $ReportName
	}
 catch {
		return $ReportName
	}
	
}

### begin Foundation functions ###
function Get-FoundationReport {
	[CmdletBinding()]
	[OutputType([FoundationReport])]
	
	$Sections = @(
		[ReportSection] @{
			Title       = "Security Base Data"
			SubSections = @(
				[ReportSection] @{
					Title      = 'Platform Security'
					AuditInfos = Test-AuditGroup "SBD - Platform Security"
				}
				[ReportSection] @{
					Title      = 'Windows Base Security'
					AuditInfos = Test-AuditGroup "SBD - Windows Base Security"
				}
				[ReportSection] @{
					Title      = 'PowerShell Security'
					AuditInfos = Test-AuditGroup "SBD - PowerShell Security"
				}
				[ReportSection] @{
					Title      = 'Connectivity Security'
					AuditInfos = Test-AuditGroup "SBD - Connectivity Security"
				}
				[ReportSection] @{
					Title      = 'Application Control'
					AuditInfos = Test-AuditGroup "SBD - Application Control"
				}
			)
		}
	)

	return ([FoundationReport]@{
			Sections = $Sections
		})
}


# region for RiskScore functions
# function that calls all RiskScore-Subfunctions and generates the RSFullReport
function Get-RSFullReport {
	[CmdletBinding()]
	[OutputType([RSFullReport])]
	
	$severity = Get-RSSeverityReport

	
	return ([RSFullReport]@{
			RSSeverityReport = $severity
		})
}
# function to generate RiskSeverityReport
function Get-RSSeverityReport {
	[CmdletBinding()]
	[OutputType([RSSeverityReport])]

	# Initialization
	[AuditInfo[]]$tests = Test-AuditGroup "RSSeverityTests"

	# gather results of tests and save it in resultTable
	$resultTable = [ResultTable]::new()
	foreach ($test in $tests) {
		if ($test.AuditInfoStatus -EQ "True") {
			$resultTable.Success += 1
		}
		if ($test.AuditInfostatus -ne "True") {
			$resultTable.Failed += 1
		}
	}

	return ([RSSeverityReport]@{
			AuditInfos  = $tests
			ResultTable = $resultTable
			Endresult   = Get-RSSeverityEndResult($resultTable)
		})
}

# helper for EndResult of RiskScoreSeverity
function Get-RSSeverityEndResult {
	[CmdletBinding()]
	[OutputType([RSEndResult])]

	param (
		[Parameter(Mandatory = $true)]
		[ResultTable[]]
		$resultTable
	)

	$result = "Unknown"

	$f = $resultTable.Failed
	if ($f -eq 0) {
		$result = "Low"
	}
	if ($f -ge 1) {
		$result = "Critical"
	}
	return $result
}

#endregion

<#
.SYNOPSIS
	Tests a single AuditGroup.
.DESCRIPTION
	This cmdlet tests a single AuditGroup from folder "AuditGroups". All tests are printed on the console. Can be combined to create own report.
.EXAMPLE
	PS C:\> Test-AuditGroup "Google Chrome-CIS-2.0.0#RegistrySettings"
	This runs tests defined in the AuditGroup file called 'Google Chrome-CIS-2.0.0#RegistrySettings'.
.PARAMETER GroupName
	The name of the AuditGroup.
#>
function Test-AuditGroup {
	[CmdletBinding()]
	[OutputType([AuditInfo[]])]
	param(
		[Parameter(Mandatory = $true)]
		[string]
		$GroupName
	)

	#Windows OS
	if ([System.Environment]::OSVersion.Platform -ne 'Unix') {
		$tests = . "$RootPath\AuditGroups\$($GroupName).ps1"
	}
	#Linux OS
	else {
		$tests = . "$RootPath/AuditGroups/$($GroupName).ps1"
	}


	$i = 1
	foreach ($test in $tests) {
		[int]$p = $i++ / $tests.Count * 100
		Write-Progress -Activity "Testing Report for '$GroupName'" -Status "Progress:" -PercentComplete $p
		Write-Verbose "Testing $($test.Id)"
		$message = "Test not implemented yet."
		$status = [AuditInfoStatus]::None
		#if audit test contains datatype "Constraints", proceed
		if ($test.Constraints) {
			$DomainRoleConstraint = $test.Constraints | Where-Object Property -EQ "DomainRole"
			#get domain role of system
			$currentRole = Get-DomainRole
			#get domain roles, which are listed in AuditTest
			$domainRoles = $DomainRoleConstraint.Values
			if ($currentRole -notin $domainRoles) {
				$roleValue = (Get-CimInstance -Class Win32_ComputerSystem).DomainRole
				switch ($roleValue) {
					0 {	
						$message = 'Not applicable. This audit does not apply to Standalone Workstation.'
						$status = [AuditInfoStatus]::None
					}
					1 {	
						$message = 'Not applicable. This audit does not apply to Member Workstation.'
						$status = [AuditInfoStatus]::None
					}
					2 {	
						$message = 'Not applicable. This audit does not apply to Standalone Server.'
						$status = [AuditInfoStatus]::None
					}
					3 {	
						$message = 'Not applicable. This audit does not apply to Member Server.'
						$status = [AuditInfoStatus]::None
					}
					4 {	
						$message = 'Not applicable. This audit does not apply to Backup Domain Controller.'
						$status = [AuditInfoStatus]::None
					}
					5 {	
						$message = 'Not applicable. This audit does not apply to Primary Domain Controller.'
						$status = [AuditInfoStatus]::None
					}
				}
				Write-Output ([AuditInfo]@{
						Id      = $test.Id
						Task    = $test.Task
						Message = $message
						Status  = $status
					})
				continue
			}
		}

		#Windows OS
		if ([System.Environment]::OSVersion.Platform -ne 'Unix') {
			$role = Get-Wmiobject -Class 'Win32_computersystem' -ComputerName $env:computername | Select-Object domainrole
			if ($test.Task -match "(DC only)") {
				if ($role.domainRole -ne 4 -and $role.domainRole -ne 5) {
					$message = 'Not applicable. This audit does not apply to Member Server systems.'
					$status = [AuditInfoStatus]::None
					Write-Output ([AuditInfo]@{
							Id      = $test.Id
							Task    = $test.Task
							Message = $message
							Status  = $status
						})
					continue
				}
			}
		}
		try {
			$innerResult = & $test.Test

			if ($null -ne $innerResult) {
				$message = $innerResult.Message
				$status = [AuditInfoStatus]$innerResult.Status
			}
		}
		catch {
			Write-Error $_
			$message = "An error occured!"
			$status = [AuditInfoStatus]::Error
		}

		Write-Output ([AuditInfo]@{
				Id      = $test.Id
				Task    = $test.Task
				Message = $message
				Status  = $status
			})
	}
}

<#
.SYNOPSIS
	Get an audit resource.
.DESCRIPTION
	A resource provides abstration over an existing system resource. It is used by AuditTests.
.PARAMETER Name
	The name of the resource.
.EXAMPLE
	PS C:\> Get-AuditResource -Name "WindowsSecurityPolicy"
	Gets the WindowsSecurityPolicy resource.
#>
function Get-AuditResource {
	[CmdletBinding()]
	param (
		[Parameter(Mandatory = $true)]
		[string]
		$Name
	)
	#Windows OS
	if ([System.Environment]::OSVersion.Platform -ne 'Unix') {
		if ($null -eq $script:loadedResources) {
			return & "$RootPath\Resources\$($Name).ps1"
		}
		if (-not $script:loadedResources.ContainsKey($Name)) {
			$script:loadedResources[$Name] = (& "$RootPath\Resources\$($Name).ps1")
		}
	}
	#Linuxs OS
	else {
		if ($null -eq $script:loadedResources) {
			return & "$RootPath/Resources/$($Name).ps1"
		}
		if (-not $script:loadedResources.ContainsKey($Name)) {
			$script:loadedResources[$Name] = (& "$RootPath/Resources/$($Name).ps1")
		}
	}
	return $script:loadedResources[$Name]
}

<#
.SYNOPSIS
	Get all reports.
.DESCRIPTION
	Find the reports installed on the system.
.PARAMETER ReportName
	The name of the report.
.EXAMPLE
	PS C:\> Get-ATAPReport
	Gets all reports.
#>
function Get-ATAPReport {
	[CmdletBinding()]
	param (
		[Parameter()]
		[string]
		$ReportName = "*"
	)
	#Windows OS
	if ([System.Environment]::OSVersion.Platform -ne 'Unix') {
		return Get-ChildItem "$RootPath\Reports\$ReportName.ps1" | Select-Object -Property BaseName
	}
	#Linux OS
	return Get-ChildItem "$RootPath/Reports/$ReportName.ps1" | Select-Object -Property BaseName
}

<#
.SYNOPSIS
	Invokes an ATAPReport
.DESCRIPTION
	Long description
.EXAMPLE
	PS C:\> ATAPReport -ReportName "Google Chrome"
	This runs the report and outputs the logical report data.
.PARAMETER ReportName
	The name of the report.
.OUTPUTS
	Logical report data.
#>
function Invoke-ATAPReport {
	[CmdletBinding()]
	param (
		[Alias('RN')]
		[Parameter(Mandatory = $true)]
		[string]
		$ReportName
	)

	$script:loadedResources = @{}
	# Load the module manifest

	#Windows OS
	try {
		if ([System.Environment]::OSVersion.Platform -ne 'Unix') {
			$moduleInfo = Import-PowerShellDataFile -Path "$RootPath\ATAPAuditor.psd1"
			[string]$ReportName = checkReportNameWithOSSystem -ReportName $ReportName
			try {
				if ($ReportName -eq "Exit") {
					throw
				}
			}
			catch {
				Write-Host "Script halted: Exiting..."
				break
			}
			[Report]$report = (& "$RootPath\Reports\$ReportName.ps1")
			$report.RSReport = Get-RSFullReport
			$report.FoundationReport = Get-FoundationReport
		}
		#Linux OS
		else {
			$moduleInfo = Import-PowerShellDataFile -Path "$RootPath/ATAPAuditor.psd1"
			[Report]$report = (& "$RootPath/Reports/$ReportName.ps1")
		}
	}
	catch [System.Management.Automation.CommandNotFoundException] {
		Write-Host "Either your input for -Reportname is faulty or the report does not resolve due to a bug. Please report this bug with the following errormessage: 
		1. ErrorException: $_
		2. PositionMessage: $($_.InvocationInfo.PositionMessage)
		3. ReportName: $ReportName"
		break
	}
	$report.AuditorVersion = $moduleInfo.ModuleVersion
	return $report
}

<#
.SYNOPSIS
	The Audit Test Automation Package creates transparents reports about hardening compliance status
.DESCRIPTION
	The Audit Test Automation Package gives you the ability to get an overview about the compliance status of several systems. 
	You can easily create HTML-reports and have a transparent overview over compliance and non-compliance of explicit setttings 
	and configurations in comparison to industry standards and hardening guides. 
.EXAMPLE
	PS C:\> Save-ATAPHtmlReport -ReportName "Microsoft Windows 10 Complete" -RiskScore -Path C:\Temp\report.html
	This runs the 'Microsoft Windows 10 Complete' report, adding RiskScore to it and stores the resulting html file under C:\Temp using the file name report.html
.EXAMPLE
	PS C:\> Save-ATAPHtmlReport -ReportName "Microsoft Windows 10 BSI" -RiskScore -Path C:\Temp
	This runs the 'Microsoft Windows 10 BSI' report, adding RiskScore to it and stores the resulting html file under C:\Temp using the standard naming convention for file names <ReportName_Date_Time>.html
.EXAMPLE
	PS C:\> Save-ATAPHtmlReport -ReportName "Microsoft Windows Server 2022" -Path C:\Temp
	This runs the 'Microsoft Windows Server 2022' report, without adding RiskScore to it and stores the resulting html file under C:\Temp using the standard naming convention for file names <ReportName_Date_Time>.html
.EXAMPLE
	PS C:\> Save-ATAPHtmlReport -ReportName "Google Chrome"
	This runs the 'Google Chrome' report and stores the resulting html file (by default) under ~\Documents\ATAPReports
.EXAMPLE
	PS C:\> Save-ATAPHtmlReport -ReportName "Ubuntu 20.04"
	This runs the 'Ubuntu 20.04' report and stores the resulting html file (by default) under ~\Documents\ATAPReports
.PARAMETER ReportName
	Determine, which OS shall be tested.
.PARAMETER Path
	The path where the result html document should be stored.
.PARAMETER RiskScore
	Add a RiskScore-Matrix to report (works only on Windows OS)
.PARAMETER MITRE
	Add a MITRE ATT&CK headmap to report (works only on Windows OS)
.PARAMETER Force
	If the parent directory doesn't exist it will be created.
.OUTPUTS
	None.
#>
function Save-ATAPHtmlReport {
	[CmdletBinding()]
	param(
		[Alias('RN')]
		[Parameter(Mandatory = $true)]
		[string]
		$ReportName,

		[Parameter(Mandatory = $false)]
		[string]
		$Path = ($script:atapReportsPath | Join-Path -ChildPath "$($ReportName)_$(Get-Date -UFormat %Y%m%d_%H%M%S).html"),

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

		[Parameter(Mandatory = $false)]
		[switch]
		$SkipLicenseCheck,
		# [Parameter(Mandatory = $false)]
		# [switch]
		# $MITRE,

		[Parameter()]
		[switch]
		$Force
	)

	if ([Environment]::Is64BitProcess -eq $false) {
		Write-Host "Please use 64-bit version of PowerShell in order to use AuditTAP. Closing..." -ForegroundColor red
		return;
	}

	if (($languagemode = IsIn-FullLanguageMode) -ne $true) {
		if ($languagemode -eq $false) {
			Write-Host "The current language mode could not be determined. Ensure that AuditTAP is run in `"FullLanguage`" mode. For further information, contact your administrator. Closing..." -ForegroundColor red
		}
		else {
			Write-Host "The current language mode is `"$languagemode`". Ensure that AuditTAP is run in `"FullLanguage`" mode. For further information, contact your administrator. Closing..." -ForegroundColor red
		}
		return
	}

	$parent = $path
	if ($Path -match ".html") {
		$parent = Split-Path -Path $Path
	}

	#if input path is not default one
	if ($parent -ne $script:atapReportsPath) {
		$pathCheck = Test-Path -Path $parent -PathType Container
		#if path doesn't exist
		if ($pathCheck -eq $False) {
			if (-not [string]::IsNullOrEmpty($parent) -and -not (Test-Path $parent)) {
				New-Item -ItemType Directory -Path $parent -Force | Out-Null
				Write-Warning "Could not find Path. Path will be created: $parent"
			}
			else {
				Write-Warning "Could not find Path. Report will be created inside default path: $($script:atapReportsPath)"
				$Path = $($script:atapReportsPath)
			}
		}
	}
	Write-Verbose "OS-Check"
	$isUnix = [System.Environment]::OSVersion.Platform -eq 'Unix'
	if ($isUnix) {
		[SystemInformation] $SystemInformation = (& "$PSScriptRoot\Helpers\ReportUnixOS.ps1")
	}
	else {
		[SystemInformation] $SystemInformation = (& "$PSScriptRoot\Helpers\ReportWindowsOS.ps1")
		Start-ModuleTest
		if ($ReportName -eq "Microsoft IIS10") {
			$isIIS10Executable = IsIIS10Executable
			if ($isIIS10Executable -eq $false) {
				Write-Warning "IIS10 Report not executable! IISAdministration module not available. Please install this module and try again. Exiting..."
				return;
			}
		}
		Write-Verbose "PS-Check"
		$psVersion = $PSVersionTable.PSVersion
		#PowerShell Major version not 5.*
		if (($psVersion.Major -ne 5)) {
			Write-Warning "ATAPAuditor is only compatible with PowerShell Version 5.1. Your version is $psVersion. Please open a PowerShell Version 5.1 session to continue!"
			return;
		}
		#PowerShell version not 5.1
		if (($psVersion.Major -eq 5) -and ($psVersion.Minor -eq 0)) {
			Write-Warning "ATAPAuditor is only compatible with PowerShell Version 5.1. Your version is $psVersion. You need to upgrade to a higher Windows version!"
			return;
		}
	}

	$report = Invoke-ATAPReport -ReportName $ReportName 
	#hashes for each recommendation
	if (!$isUnix) {
		$SystemInformation.SoftwareInformation.LicenseStatus = Get-LicenseStatus $SkipLicenseCheck
	}
	$hashtable_sha256 = GenerateHashTable $report
	
	$report | Get-ATAPHtmlReport -Path $Path -RiskScore:$RiskScore -MITRE:$MITRE -hashtable_sha256:$hashtable_sha256 -LicenseStatus:$LicenseStatus -SystemInformation:$SystemInformation
}

New-Alias -Name 'shr' -Value Save-ATAPHtmlReport

$completer = {
	param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

	Get-ChildItem "$RootPath\Reports\*.ps1" `
	| Select-Object -ExpandProperty BaseName `
	| ForEach-Object { "`"$_`"" } `
	| Where-Object { $_ -like "*$wordToComplete*" }
}.GetNewClosure()

Register-ArgumentCompleter -CommandName Save-ATAPHtmlReport -ParameterName ReportName -ScriptBlock $completer
Register-ArgumentCompleter -CommandName shr -ParameterName ReportName -ScriptBlock $completer
