Confirm-SystemCompliance.psm1

# To parse the ini file from the output of the "secedit /export /cfg c:\\security_policy.inf"
function ConvertFrom-IniFile {
    [CmdletBinding()]
    Param ([string]$IniFile)
            
    # Don't prompt to continue if '-Debug' is specified.
    $DebugPreference = 'Continue'
          
    [hashtable]$IniObject = @{}
    [string]$SectionName = ''
    switch -regex -file $IniFile {
        '^\[(.+)\]$' {
            # Header of the section
            $SectionName = $matches[1]
            #Write-Debug "Section: $SectionName"
            $IniObject[$SectionName] = @{}
            continue
        }
        '^(.+?)\s*=\s*(.*)$' {
            # Name/value pair
            [string]$KeyName, [string]$KeyValue = $matches[1..2]
            #Write-Debug "Name: $KeyName"
            # Write-Debug "Value: $KeyValue"
            $IniObject[$SectionName][$KeyName] = $KeyValue
            continue
        }
        default {
            # Ignore blank lines or comments
            continue
        }
    }
    return [pscustomobject]$IniObject
}

# Main function that also parses the output of "gpresult /Scope Computer /x GPResult.xml"
function Confirm-SystemCompliance {   
    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]        
        [switch]$ExportToCSV,
        [parameter(Mandatory = $false)]        
        [switch]$ShowAsObjectsOnly,
        [parameter(Mandatory = $false)]        
        [switch]$DetailedDisplay        
    )
    begin {

        Write-Progress -Activity 'Starting' -Status 'Processing...' -PercentComplete 5
       
        # Make sure the latest version of the module is installed and if not, automatically update it, clean up any old versions
        function Update-self {            
            $CurrentVersion = (Test-modulemanifest "$psscriptroot\Harden-Windows-Security-Module.psd1" -ErrorAction Stop).Version.ToString()
            
            try {             
                $LatestVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/main/Harden-Windows-Security%20Module/version.txt"             
            }
            catch {   
                Write-Error -Message "Couldn't verify if the latest version of the module is installed, please check your Internet connection." -ErrorAction Stop
            }
            
            if ($CurrentVersion -ne $LatestVersion) {
                Write-Output "$($PSStyle.Foreground.FromRGB(255,105,180))The currently installed module's version is $CurrentVersion while the latest version is $LatestVersion - Auto Updating the module... 💓$($PSStyle.Reset)"
                Remove-Module -Name 'Harden-Windows-Security-Module' -Force
                # Do this if the module was installed properly using Install-moodule cmdlet
                try {
                    Uninstall-Module -Name 'Harden-Windows-Security-Module' -AllVersions -Force -ErrorAction Stop
                    Install-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force -ErrorAction Stop              
                    Import-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force -Global -ErrorAction Stop
                }
                # Do this if module files/folder was just copied to Documents folder and not properly installed - Should rarely happen
                catch {
                    Install-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force -ErrorAction Stop
                    Import-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force -Global -ErrorAction Stop
                }      
                # Make sure the old versio isn't run after update
                Write-Output "$($PSStyle.Foreground.FromRGB(152,255,152))Update successful, please run the Confirm-SystemCompliance cmdlet again.$($PSStyle.Reset)"          
                break
                return          
            }
        }

        # Make sure this cmdlet is invoked with Admin privileges
        if (![bool]([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Write-Error -Message "Confirm-SystemCompliance cmdlet requires Administrator privileges." -ErrorAction Stop
        }

        Write-Progress -Activity 'Checking for updates' -Status 'Processing...' -PercentComplete 10

        # Self update the module
        Update-self -ErrorAction Stop

        # Stop operation as soon as there is an error anywhere, unless explicitly specified otherwise
        $ErrorActionPreference = 'SilentlyContinue'
        
        Write-Progress -Activity 'Gathering Security Policy Information' -Status 'Processing...' -PercentComplete 15

        Secedit /export /cfg .\security_policy.inf | Out-Null
        # Storing the output of the ini file parsing function
        $SecurityPoliciesIni = ConvertFrom-IniFile -IniFile .\security_policy.inf
        
        Write-Progress -Activity 'Downloading Registry CSV File from GitHub or Azure DevOps' -Status 'Processing...' -PercentComplete 20

        # Download Registry CSV file from GitHub or Azure DevOps
        try {
            Invoke-WebRequest -Uri "https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/main/Payload/Registry.csv" -OutFile ".\Registry.csv" -ErrorAction Stop                
        }
        catch {
            Write-Host "Using Azure DevOps..." -ForegroundColor Yellow
            Invoke-WebRequest -Uri "https://dev.azure.com/SpyNetGirl/011c178a-7b92-462b-bd23-2c014528a67e/_apis/git/repositories/5304fef0-07c0-4821-a613-79c01fb75657/items?path=/Payload/Registry.csv" -OutFile ".\Registry.csv" -ErrorAction Stop
        }
        # Import the registry.csv file as CSV
        $CSVFileContent = Import-Csv -Path ".\Registry.csv"

        Write-Progress -Activity 'Downloading Group-Policies.json file from GitHub' -Status 'Processing...' -PercentComplete 25

        # Download Group-Policies.json file from GitHub
        try {
            Invoke-WebRequest -Uri "https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/a38f5fb1e219e31826c772f626df9a2bdc6bb55d/Payload/Group-Policies.json" -OutFile ".\Group-Policies.json" -ErrorAction Stop
        }
        catch {
            Write-Error -Message "Group-Policies.json file couldn't be downloaded, exitting..."  
        }
        # Hash table to store Hardening Script's Policy Categories and Names
        # Importing it from the JSON file as hashtable
        $HashPol = Get-Content -Path ".\Group-Policies.json" -ErrorAction Stop | ConvertFrom-Json -Depth 100 -AsHashtable -ErrorAction Stop
       
       
        Write-Progress -Activity 'Gathering Group Policy Information' -Status 'Processing...' -PercentComplete 30

        Gpresult /Scope Computer /x .\GPResult.xml /f
        # Load the xml file into a variable
        $GroupPolicyXmlContent = [xml](Get-Content -Path .\GPResult.xml -ErrorAction Stop)


        # An array to store each Group Policy "<q6:Policy>" element as a separate object
        $PoliciesOutput = @()
        # Use dot notation to access the Group Policy elements
        $GroupPolicyXmlContent.Rsop.ComputerResults.ExtensionData.Extension.Policy | Where-Object { $null -ne $_.name } | ForEach-Object {   
            # All the sub-elements of the "<q6:Policy>" that we need to verify
            $PoliciesOutput += [PSCustomObject]@{
                Name                 = $_.Name
                State                = $_.State
                Category             = $_.Category
                DropDownListName     = $_.DropDownList.Name
                DropDownListState    = $_.DropDownList.State
                DropDownListValue    = $_.DropDownList.Value.Name
                CheckboxName         = $_.Checkbox.Name
                CheckboxState        = $_.Checkbox.State
                Numeric              = $_.Numeric
                NumericName          = $_.Numeric.Name
                NumericState         = $_.Numeric.State
                NumericValue         = $_.Numeric.Value
                ListBox              = $_.ListBox
                ListBoxName          = $_.ListBox.Name
                ListBoxState         = $_.ListBox.State
                ListBoxExplicitValue = $_.ListBox.ExplicitValue
                ListBoxAdditive      = $_.ListBox.Additive
                ListBoxValue         = $_.ListBox.Value
                MultiTextName        = $_.MultiText.Name
                MultiTextState       = $_.MultiText.State
                MultiTextValue       = $_.MultiText.Value
                EditTextName         = $_.EditText.Name
                EditTextState        = $_.EditText.State
                EditTextValue        = $_.EditText.Value
            }
        }


        # An array to store Group Policy Firewall settings as an object
        $FirewallPoliciesOutput = @()
        # Use dot notation to access the Group Policy elements - sometimes the type is q4 or q3 or q7, so using wildcard for the number
        $FirewallGroupPolicySettings = $GroupPolicyXmlContent.Rsop.ComputerResults.ExtensionData.Extension | Where-Object { $_.type -like 'q*:WindowsFirewallSettings' } 

        $FirewallPoliciesOutput += [PSCustomObject]@{

            GlobalSettingsPolicyVersion      = $FirewallGroupPolicySettings.GlobalSettings.PolicyVersion.Value
            # Domain profile policies
            DomainDefaultInboundAction       = $FirewallGroupPolicySettings.DomainProfile.DefaultInboundAction.value
            DomainDefaultOutboundAction      = $FirewallGroupPolicySettings.DomainProfile.DefaultOutboundAction.value
            DomainDisableNotifications       = $FirewallGroupPolicySettings.DomainProfile.DisableNotifications.value
            DomainDoNotAllowExceptions       = $FirewallGroupPolicySettings.DomainProfile.DoNotAllowExceptions.value
            DomainEnableFirewall             = $FirewallGroupPolicySettings.DomainProfile.EnableFirewall.value
            DomainLogFilePath                = $FirewallGroupPolicySettings.DomainProfile.LogFilePath.value
            DomainLogFileSize                = $FirewallGroupPolicySettings.DomainProfile.LogFileSize.value        
            DomainLogDroppedPackets          = $FirewallGroupPolicySettings.DomainProfile.LogDroppedPackets.value
            DomainLogSuccessfulConnections   = $FirewallGroupPolicySettings.DomainProfile.LogSuccessfulConnections.value
            # Public profile policies
            PublicAllowLocalIPsecPolicyMerge = $FirewallGroupPolicySettings.PublicProfile.AllowLocalIPsecPolicyMerge.value
            PublicAllowLocalPolicyMerge      = $FirewallGroupPolicySettings.PublicProfile.AllowLocalPolicyMerge.value
            PublicDefaultInboundAction       = $FirewallGroupPolicySettings.PublicProfile.DefaultInboundAction.value
            PublicDefaultOutboundAction      = $FirewallGroupPolicySettings.PublicProfile.DefaultOutboundAction.value
            PublicDisableNotifications       = $FirewallGroupPolicySettings.PublicProfile.DisableNotifications.value
            PublicDoNotAllowExceptions       = $FirewallGroupPolicySettings.PublicProfile.DoNotAllowExceptions.value
            PublicEnableFirewall             = $FirewallGroupPolicySettings.PublicProfile.EnableFirewall.value
            PublicLogFilePath                = $FirewallGroupPolicySettings.PublicProfile.LogFilePath.value
            PublicLogFileSize                = $FirewallGroupPolicySettings.PublicProfile.LogFileSize.value        
            PublicLogDroppedPackets          = $FirewallGroupPolicySettings.PublicProfile.LogDroppedPackets.value
            PublicLogSuccessfulConnections   = $FirewallGroupPolicySettings.PublicProfile.LogSuccessfulConnections.value        
            # Private profile policies
            PrivateDefaultInboundAction      = $FirewallGroupPolicySettings.PrivateProfile.DefaultInboundAction.value
            PrivateDefaultOutboundAction     = $FirewallGroupPolicySettings.PrivateProfile.DefaultOutboundAction.value
            PrivateDisableNotifications      = $FirewallGroupPolicySettings.PrivateProfile.DisableNotifications.value           
            PrivateEnableFirewall            = $FirewallGroupPolicySettings.PrivateProfile.EnableFirewall.value
            PrivateLogFilePath               = $FirewallGroupPolicySettings.PrivateProfile.LogFilePath.value
            PrivateLogFileSize               = $FirewallGroupPolicySettings.PrivateProfile.LogFileSize.value        
            PrivateLogDroppedPackets         = $FirewallGroupPolicySettings.PrivateProfile.LogDroppedPackets.value
            PrivateLogSuccessfulConnections  = $FirewallGroupPolicySettings.PrivateProfile.LogSuccessfulConnections.value
        }


        # An array to store each Group Policy "<q6:RegistrySetting>" element as a separate object
        $RegistriesOutput = @()
        # Use dot notation to access the Policy element
        $GroupPolicyXmlContent.Rsop.ComputerResults.ExtensionData.Extension.RegistrySetting | Where-Object { $null -ne $_.Value.Name } | ForEach-Object {
    
            $RegistriesOutput += [PSCustomObject]@{
                KeyPath = $_.KeyPath
                Name    = $_.Value.Name
                Number  = $_.Value.Number      
            }     
        }

  
        # An object to store the FINAL results
        $FinalMegaObject = [PSCustomObject]@{}
   
        # Hash table to store Hardening Script's Registry Policy Categories and Names
        # They are still Group Policies but instead of being in "<q6:Policy>" element they are in "<q6:RegistrySetting>"
        $HashReg = @{
            # Device Guard
            'Device Guard' = @{
                1 = @{
                    KeyPath = "Software\Policies\Microsoft\Windows\System"
                    Name    = "RunAsPPL"              
                }
            }
        }
    }

    process {
        
        #Region Microsoft-Defender-Category
        Write-Progress -Activity 'Validating Microsoft Defender Category' -Status 'Processing...' -PercentComplete 35
        # An array to store the nested custom objects (Results of the foreach loop), inside the main output object
        $NestedObjectArray = @()
        $CatName = "Microsoft Defender"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            # Get the correct object from the PoliciesOutput Object that contains all the group policies in the xml file
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListState -eq 'NotConfigured') ? $True : $False  # It's actually Enabled but Gpresult shows NotConfigured!
                }
                2 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                3 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Advanced MAPS') ? $True : $False 
                }
                4 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Send file samples when further analysis is required' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Send all samples'
                    ) ? $True : $False 
                }
                5 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Configure the guard my folders feature' `
                            -and $Item.DropDownListState -eq 'NotConfigured' ` # It's actually Enabled but Gpresult shows NotConfigured!
                    ) ? $True : $False 
                }
                6 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListState -eq 'NotConfigured' # It's actually Enabled but Gpresult shows NotConfigured!
                    ) ? $True : $False 
                }
                7 {            
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Specify the extended cloud check time in seconds' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '50'                
                    ) ? $True : $False 
                }
                8 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False 
                }
                9 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Select cloud blocking level' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Zero tolerance blocking level' 
                    ) ? $True : $False
                }
                10 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Configure removal of items from Quarantine folder' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '3'                
                    ) ? $True : $False
                }
                11 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Define the maximum size of downloaded files and attachments to be scanned' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '10000000'                
                    ) ? $True : $False            
                }
                12 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False          
                }
                13 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                14 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                15 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                16 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Specify the maximum depth to scan archive files' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '4294967295'                
                    ) ? $True : $False                        
                }
                17 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                18 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                19 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                20 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                21 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False            
                }
                22 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Define the number of days before spyware security intelligence is considered out of date' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '2'                
                    ) ? $True : $False     
                }
                23 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Define the number of days before virus security intelligence is considered out of date' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '2'                
                    ) ? $True : $False     
                }
                24 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Specify the interval to check for security intelligence updates' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '3'                
                    ) ? $True : $False     
                }
                25 {
                    # ListBox 1
                    $1index = $Item.ListBoxValue.element.Name.IndexOf("4")
                    # Write-Host "$1index" -ForegroundColor Yellow
                    $1ListData = $Item.ListBoxValue.element.Data[$1index]
                    # Write-Host "$1ListData" -ForegroundColor Yellow
    
                    # ListBox 2
                    $2index = $Item.ListBoxValue.element.Name.IndexOf("2")
                    # Write-Host "$2index" -ForegroundColor Yellow
                    $2ListData = $Item.ListBoxValue.element.Data[$2index]
                    # Write-Host "$2ListData" -ForegroundColor Yellow
    
                    # ListBox 3
                    $3index = $Item.ListBoxValue.element.Name.IndexOf("1")
                    # Write-Host "$3index" -ForegroundColor Yellow
                    $3ListData = $Item.ListBoxValue.element.Data[$3index]
                    # Write-Host "$3ListData" -ForegroundColor Yellow
    
                    # ListBox 4
                    $4index = $Item.ListBoxValue.element.Name.IndexOf("5")
                    # Write-Host "$4index" -ForegroundColor Yellow
                    $4ListData = $Item.ListBoxValue.element.Data[$4index]
                    # Write-Host "$4ListData" -ForegroundColor Yellow
            
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.ListBoxName -eq 'Specify threat alert levels at which default action should not be taken when detected' `
                            -and $Item.ListBoxState -eq 'Enabled' `
                            -and $Item.ListBoxExplicitValue -eq 'true' `
                            -and $Item.ListBoxAdditive -eq 'true' `
                            -and $1ListData -eq '3' `
                            -and $2ListData -eq '2' `
                            -and $3ListData -eq '2' `
                            -and $4ListData -eq '3' `
                    ) ? $True : $False 
                }
                26 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False  
                }
                27 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False  
                }
                28 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False  
                }
                29 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False  
                }            
            }
    
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }    
        }
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $((Get-MpPreference).AllowSwitchToAsyncInspection)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'AllowSwitchToAsyncInspection'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"            
        }
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $((Get-MpPreference).oobeEnableRtpAndSigUpdate)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'oobeEnableRtpAndSigUpdate'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"            
        }
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $((Get-MpPreference).IntelTDTEnabled)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'IntelTDTEnabled'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"            
        }
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $((Get-ProcessMitigation -System -ErrorAction Stop).aslr.ForceRelocateImages)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Mandatory ASLR'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult -eq 'on' ? $True : $false
            Category  = $CatName
            Method    = "Cmdlet"            
        }
    
        # For BCDEDIT NX value verification
        # IMPORTANT: bcdedit /enum requires an ELEVATED session.
        # Answer by mklement0: https://stackoverflow.com/a/50949849
        $bcdOutput = (bcdedit /enum) -join "`n" # collect bcdedit's output as a *single* string
    
        # Initialize the output list.
        $entries = New-Object System.Collections.Generic.List[pscustomobject] -ErrorAction Stop
    
        # Parse bcdedit's output.
    ($bcdOutput -split '(?m)^(.+\n-)-+\n' -ne '').ForEach({
                if ($_.EndsWith("`n-")) {
                    # entry header
                    $entries.Add([pscustomobject] @{ Name = ($_ -split '\n')[0]; Properties = [ordered] @{} })
                }
                else {
                    # block of property-value lines
    ($_ -split '\n' -ne '').ForEach({
                            $propAndVal = $_ -split '\s+', 2 # split line into property name and value
                            if ($propAndVal[0] -ne '') {
                                # [start of] new property; initialize list of values
                                $currProp = $propAndVal[0]
                                $entries[-1].Properties[$currProp] = New-Object Collections.Generic.List[string] -ErrorAction Stop
                            }
                            $entries[-1].Properties[$currProp].Add($propAndVal[1]) # add the value
                        })
                }
            })
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $(($entries | Where-Object { $_.properties.identifier -eq "{current}" }).properties.nx)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'BCDEDIT NX Value'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult -eq 'AlwaysOn' ? $True : $false   
            Category  = $CatName
            Method    = "Cmdlet"             
        }
    
        # For PowerShell Cmdlet
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Smart App Control State'
            Value     = $((Get-MpComputerStatus).SmartAppControlState)
            Compliant = 'N/A'
            Category  = $CatName
            Method    = "Cmdlet"            
        }
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $((Get-ScheduledTask -TaskPath "\MSFT Driver Block list update\" -TaskName "MSFT Driver Block list update" -ErrorAction SilentlyContinue) ? $True : $false)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Fast weekly Microsoft recommended driver block list update'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"           
        }
    
    
        $DefenderPlatformUpdatesChannels = @{
            0 = 'NotConfigured'
            2 = 'Beta'
            3 = 'Preview'
            4 = 'Staged'
            5 = 'Broad'
            6 = 'Delayed'
        }
        # For PowerShell Cmdlet
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Microsoft Defender Platform Updates Channel'
            Value     = $($DefenderPlatformUpdatesChannels[[int](get-mppreference).PlatformUpdatesChannel])
            Compliant = 'N/A'
            Category  = $CatName
            Method    = "Cmdlet"           
        }
    
    
        $DefenderEngineUpdatesChannels = @{
            0 = 'NotConfigured'
            2 = 'Beta'
            3 = 'Preview'
            4 = 'Staged'
            5 = 'Broad'
            6 = 'Delayed'
        }
        # For PowerShell Cmdlet
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Microsoft Defender Engine Updates Channel'
            Value     = $($DefenderEngineUpdatesChannels[[int](get-mppreference).EngineUpdatesChannel])
            Compliant = 'N/A'
            Category  = $CatName
            Method    = "Cmdlet"            
        }
    
        # For PowerShell Cmdlet
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Controlled Folder Access Exclusions'
            Value     = [pscustomobject]@{Count = $((Get-MpPreference).ControlledFolderAccessAllowedApplications.count); Programs = $((Get-MpPreference).ControlledFolderAccessAllowedApplications) }
            Compliant = 'N/A'
            Category  = $CatName
            Method    = "Cmdlet"            
        }   
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Microsoft-Defender-Category
    
        #Region Attack-Surface-Reduction-Rules-Category
        Write-Progress -Activity 'Validating Attack Surface Reduction Rules Category' -Status 'Processing...' -PercentComplete 40
        $NestedObjectArray = @()
        $CatName = "ASR"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {
                    $1index = $Item.ListBoxValue.element.Name.IndexOf("92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B")
                    $1ListData = $Item.ListBoxValue.element.Data[$1index]                
    
                    $2index = $Item.ListBoxValue.element.Name.IndexOf("e6db77e5-3df2-4cf1-b95a-636979351e5b")
                    $2ListData = $Item.ListBoxValue.element.Data[$2index]
    
                    $3index = $Item.ListBoxValue.element.Name.IndexOf("d1e49aac-8f56-4280-b9ba-993a6d77406c")
                    $3ListData = $Item.ListBoxValue.element.Data[$3index]                
    
                    $4index = $Item.ListBoxValue.element.Name.IndexOf("3b576869-a4ec-4529-8536-b80a7769e899")
                    $4ListData = $Item.ListBoxValue.element.Data[$4index]
    
                    $5index = $Item.ListBoxValue.element.Name.IndexOf("be9ba2d9-53ea-4cdc-84e5-9b1eeee46550")
                    $5ListData = $Item.ListBoxValue.element.Data[$5index]
    
                    $6index = $Item.ListBoxValue.element.Name.IndexOf("75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84")
                    $6ListData = $Item.ListBoxValue.element.Data[$6index]
    
                    $7index = $Item.ListBoxValue.element.Name.IndexOf("56a863a9-875e-4185-98a7-b882c64b5ce5")
                    $7ListData = $Item.ListBoxValue.element.Data[$7index]
    
                    $8index = $Item.ListBoxValue.element.Name.IndexOf("01443614-cd74-433a-b99e-2ecdc07bfc25")
                    $8ListData = $Item.ListBoxValue.element.Data[$8index]
    
                    $9index = $Item.ListBoxValue.element.Name.IndexOf("b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4")
                    $9ListData = $Item.ListBoxValue.element.Data[$9index]
    
                    $10index = $Item.ListBoxValue.element.Name.IndexOf("d4f940ab-401b-4efc-aadc-ad5f3c50688a")
                    $10ListData = $Item.ListBoxValue.element.Data[$10index]
    
                    $11index = $Item.ListBoxValue.element.Name.IndexOf("5beb7efe-fd9a-4556-801d-275e5ffc04cc")
                    $11ListData = $Item.ListBoxValue.element.Data[$11index]
    
                    $12index = $Item.ListBoxValue.element.Name.IndexOf("c1db55ab-c21a-4637-bb3f-a12568109d35")
                    $12ListData = $Item.ListBoxValue.element.Data[$12index]
    
                    $13index = $Item.ListBoxValue.element.Name.IndexOf("9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2")
                    $13ListData = $Item.ListBoxValue.element.Data[$13index]
    
                    $14index = $Item.ListBoxValue.element.Name.IndexOf("7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c")
                    $14ListData = $Item.ListBoxValue.element.Data[$14index]
    
                    $15index = $Item.ListBoxValue.element.Name.IndexOf("26190899-1602-49e8-8b27-eb1d0a1ce869")
                    $15ListData = $Item.ListBoxValue.element.Data[$15index]
    
                    $16index = $Item.ListBoxValue.element.Name.IndexOf("d3e037e1-3eb8-44c8-a917-57927947596d")
                    $16ListData = $Item.ListBoxValue.element.Data[$16index]
                
                    # Use ternary operator instead of if-else statements
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.ListBoxName -eq 'Set the state for each ASR rule:' `
                            -and $Item.ListBoxState -eq 'Enabled' `
                            -and $Item.ListBoxExplicitValue -eq 'true' `
                            -and $Item.ListBoxAdditive -eq 'true' `
                            -and $1ListData -eq 1 `
                            -and $2ListData -eq 1 `
                            -and $3ListData -eq 1 `
                            -and $4ListData -eq 1 `
                            -and $5ListData -eq 1 `
                            -and $6ListData -eq 1 `
                            -and $7ListData -eq 1 `
                            -and $8ListData -eq 1 `
                            -and $9ListData -eq 1 `
                            -and $10ListData -eq 1 `
                            -and $11ListData -eq 1 `
                            -and $12ListData -eq 1 `
                            -and $13ListData -eq 1 `
                            -and $14ListData -eq 1 `
                            -and $15ListData -eq 1 `
                            -and $16ListData -eq 1 `
                    ) ? $True : $False   
                }
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"               
            }    
        }
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Attack-Surface-Reduction-Rules-Category
    
        #Region Bitlocker-Category
        Write-Progress -Activity 'Validating Bitlocker Category' -Status 'Processing...' -PercentComplete 45
        $NestedObjectArray = @()
        $CatName = "Bitlocker"


        # This PowerShell script can be used to find out if the DMA Protection is ON \ OFF.
        # The Script will show this by emitting True \ False for On \ Off respectively.

        # bootDMAProtection check - checks for Kernel DMA Protection status in System information or msinfo32
        $bootDMAProtectionCheck =
        @"
  namespace SystemInfo
    {
      using System;
      using System.Runtime.InteropServices;
 
      public static class NativeMethods
      {
        internal enum SYSTEM_DMA_GUARD_POLICY_INFORMATION : int
        {
            /// </summary>
            SystemDmaGuardPolicyInformation = 202
        }
 
        [DllImport("ntdll.dll")]
        internal static extern Int32 NtQuerySystemInformation(
          SYSTEM_DMA_GUARD_POLICY_INFORMATION SystemDmaGuardPolicyInformation,
          IntPtr SystemInformation,
          Int32 SystemInformationLength,
          out Int32 ReturnLength);
 
        public static byte BootDmaCheck() {
          Int32 result;
          Int32 SystemInformationLength = 1;
          IntPtr SystemInformation = Marshal.AllocHGlobal(SystemInformationLength);
          Int32 ReturnLength;
 
          result = NativeMethods.NtQuerySystemInformation(
                    NativeMethods.SYSTEM_DMA_GUARD_POLICY_INFORMATION.SystemDmaGuardPolicyInformation,
                    SystemInformation,
                    SystemInformationLength,
                    out ReturnLength);
 
          if (result == 0) {
            byte info = Marshal.ReadByte(SystemInformation, 0);
            return info;
          }
 
          return 0;
        }
      }
    }
"@

        Add-Type -TypeDefinition $bootDMAProtectionCheck
        # returns true or false depending on whether Kernel DMA Protection is on or off
        $bootDMAProtection = ([SystemInfo.NativeMethods]::BootDmaCheck()) -ne 0
  


        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                2 {
                    $1index = $Item.DropDownListName.IndexOf("Configure TPM startup:")                
                    $1DropDownState = $Item.DropDownListState[$1index]                
                    $1DropDownValue = $Item.DropDownListValue[$1index]
    
                    $2index = $Item.DropDownListName.IndexOf("Configure TPM startup PIN:")                
                    $2DropDownState = $Item.DropDownListState[$2index]                
                    $2DropDownValue = $Item.DropDownListValue[$2index]
    
                    $3index = $Item.DropDownListName.IndexOf("Configure TPM startup key:")                
                    $3DropDownState = $Item.DropDownListState[$3index]                
                    $3DropDownValue = $Item.DropDownListValue[$3index]
    
                    $4index = $Item.DropDownListName.IndexOf("Configure TPM startup key and PIN:")                
                    $4DropDownState = $Item.DropDownListState[$4index]                
                    $4DropDownValue = $Item.DropDownListValue[$4index]
                
                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.CheckboxName -eq 'Allow BitLocker without a compatible TPM (requires a password or a startup key on a USB flash drive)' `
                            -and $Item.CheckboxState -eq 'Disabled' `
                            -and $1DropDownState -eq 'Enabled' `
                            -and $1DropDownValue -eq 'Allow TPM' `
                            -and $2DropDownState -eq 'Enabled' `
                            -and $2DropDownValue -eq 'Allow startup PIN with TPM' `
                            -and $3DropDownState -eq 'Enabled' `
                            -and $3DropDownValue -eq 'Allow startup key with TPM' `
                            -and $4DropDownState -eq 'Enabled' `
                            -and $4DropDownValue -eq 'Allow startup key and PIN with TPM' `
                    ) ? $True : $False   
                }
                3 {
                    $1index = $Item.DropDownListName.IndexOf("Select the encryption method for operating system drives:")                
                    $1DropDownState = $Item.DropDownListState[$1index]                
                    $1DropDownValue = $Item.DropDownListValue[$1index]
    
                    $2index = $Item.DropDownListName.IndexOf("Select the encryption method for fixed data drives:")                
                    $2DropDownState = $Item.DropDownListState[$2index]                
                    $2DropDownValue = $Item.DropDownListValue[$2index]
    
                    $3index = $Item.DropDownListName.IndexOf("Select the encryption method for removable data drives:")                
                    $3DropDownState = $Item.DropDownListState[$3index]                
                    $3DropDownValue = $Item.DropDownListValue[$3index]
                
                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $1DropDownState -eq 'Enabled' `
                            -and $1DropDownValue -eq 'XTS-AES 256-bit' `
                            -and $2DropDownState -eq 'Enabled' `
                            -and $2DropDownValue -eq 'XTS-AES 256-bit' `
                            -and $3DropDownState -eq 'Enabled' `
                            -and $3DropDownValue -eq 'XTS-AES 256-bit'
                    ) ? $True : $False   
                }
                4 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Select the encryption type:' `
                            -and $Item.DropDownListState -eq 'NotConfigured' # It's actually set to "Full Encryption" but Gpresult shows NotConfigured!
                    ) ? $True : $False 
                }
                5 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'Minimum characters:' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '10'
                    ) ? $True : $False 
                }
                6 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Select the encryption type:' `
                            -and $Item.DropDownListState -eq 'NotConfigured' # NotConfigured actually means "Full Encryption" but Gpresult reports it NotConfigured
                    ) ? $True : $False 
                }
                7 {
                    [bool]$ItemState = ($Item.State -eq 'Disabled') ? $True : $False 
                }
                8 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Select the encryption type:' `
                            -and $Item.DropDownListState -eq 'NotConfigured' # It's actually set to "Full Encryption" but Gpresult shows NotConfigured!
                    ) ? $True : $False 
                }
                9 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False 
                }
                10 {
                    [bool]$ItemState = ($Item.State -eq 'Disabled') ? $True : $False 
                }
                11 {
                    [bool]$ItemState = ($Item.State -eq 'Disabled') ? $True : $False 
                }
                12 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False 
                }
                13 {
                    # Bitlocker DMA counter measure status
                    # Returns true if only either Kernel DMA protection is on and Bitlocker DMA protection if off
                    # or Kernel DMA protection is off and Bitlocker DMA protection is on
                    [bool]$ItemState = ($bootDMAProtection -xor ($Item.State -eq 'Enabled')) ? $True : $False
                }
            }
        
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }    
        }
    
        # For PowerShell Cmdlet
        $IndividualItemResult = $($((Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Power -name HibernateEnabled).hibernateEnabled) -eq 1 ? $True : $False)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Hibernate enabled and set to full'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"
        }               
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Bitlocker-Category
    
        #Region TLS-Category
        Write-Progress -Activity 'Validating TLS Category' -Status 'Processing...' -PercentComplete 50
        $NestedObjectArray = @()
        $CatName = "TLS"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {            
                1 {
                    # Write-Host "$($Item.MultiTextValue.string)" -ForegroundColor Yellow
                    # Make sure the content and their exact order is present in Group Policy
                    $ExpectedOrderAndContent = @('nistP521', 'curve25519', 'NistP384', 'NistP256')
    
                    # Loop through the array and compare each element with the expected value
                    foreach ($i in 0..3) {
                        # Use a ternary operator to set the result to false and break the loop if the element does not match
                        $ItemStateAux = $Item.MultiTextValue.string[$i] -eq $ExpectedOrderAndContent[$i] ? $True :  $false
                    }
                    # Write-Host "$ItemStateAux" -ForegroundColor Red
    
                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.MultiTextName -eq 'ECC Curve Order:' `
                            -and $Item.MultiTextState -eq 'Enabled' `
                            -and $ItemStateAux -eq $True
                    ) ? $True : $False   
                }
                2 {
                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.EditTextName -eq 'SSL Cipher Suites' `
                            -and $Item.EditTextState -eq 'Enabled' `
                            -and $Item.EditTextValue -eq 'TLS_CHACHA20_POLY1305_SHA256,TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256' # Checks the exact values and order
                    ) ? $True : $False   
                }
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }
        }
    
    
        $MatchRegistryKeys = @() # initialize the variable to false - an array that is going to hold only bool values
        foreach ($Item in $CSVFileContent) {
            if ($Item.category -eq 'TLS' -and $Item.Action -eq 'AddOrModify') {
                $path = $Item.Path
                $key = $Item.Key
                $value = $Item.value
            
                $regValue = Get-ItemPropertyValue -Path $path -Name $key
                # Store only boolean values in the $MatchRegistryKeys
                $MatchRegistryKeys += [bool]($regValue -eq $value)  
                <#
            Testing the key's value type
     
    Reg Type PS Type
    -------- -------
    REG_DWORD System.Int32
    REG_SZ System.String
    REG_QWORD System.Int64
    REG_BINARY System.Byte[]
    REG_MULTI_SZ System.String[]
    REG_EXPAND_SZ System.String
            
             (Get-ItemPropertyValue -Path $path -Name $key).GetType().name -eq $type
            (Get-ItemPropertyValue -Path $path -Name $key) -is [System.Int32]
     
    #>

            }
        }   

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        # Make sure the boolean array doesn't contain any $false values
        $IndividualItemResult = ($MatchRegistryKeys -notcontains $false)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Registry Keys All correct"            
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Registry Keys"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion TLS-Category
    
        #Region LockScreen-Category
        Write-Progress -Activity 'Validating Lock Screen Category' -Status 'Processing...' -PercentComplete 55
        $NestedObjectArray = @()
        $CatName = "LockScreen"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                2 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                3 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'PIN Expiration' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '180'
                    ) ? $True : $False   
                }
                4 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.NumericName -eq 'PIN History' `
                            -and $Item.NumericState -eq 'Enabled' `
                            -and $Item.NumericValue -eq '3'
                    ) ? $True : $False    
                }
                5 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                6 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.EditTextName -eq 'Exclude the following credential providers:' `
                            -and $Item.EditTextState -eq 'Enabled' `
                            -and $item.EditTextValue -eq '{60b78e88-ead8-445c-9cfd-0b87f74ea6cd},{F8A0B131-5F68-486c-8040-7E8FC3C85BB6},{8FD7E19C-3BF7-489B-A72C-846AB3678C96},{1ee7337f-85ac-45e2-a23c-37c753209769},{1b283861-754f-4022-ad47-a5eaaa618894}' ) ? $True : $False   
                }
                7 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.EditTextName -eq 'Assign the following credential provider as the default credential provider:' `
                            -and $Item.EditTextState -eq 'Enabled' `
                            -and $item.EditTextValue -eq '{D6886603-9D2F-4EB2-B667-1971041FA96B}' ) ? $True : $False   
                }
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\InactivityTimeoutSecs'] -eq '4,120') ? $True : $False   
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Machine inactivity limit'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableCAD'] -eq '4,0') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Interactive logon: Do not require CTRL+ALT+DEL'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\MaxDevicePasswordFailedAttempts'] -eq '4,5') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Interactive logon: Machine account lockout threshold'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLockedUserId'] -eq '4,4') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Interactive logon: Display user information when the session is locked'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayUserName'] -eq '4,1') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Interactive logon: Don't display username at sign-in"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'System Access'['LockoutBadCount'] -eq '5') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Account lockout threshold"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'System Access'['LockoutDuration'] -eq '1440') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Account lockout duration"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'System Access'['ResetLockoutCount'] -eq '1440') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Reset account lockout counter after"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName'] -eq '4,1') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Interactive logon: Don't display last signed-in"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion LockScreen-Category
    
        #Region User-Account-Control-Category
        Write-Progress -Activity 'Validating User Account Control Category' -Status 'Processing...' -PercentComplete 60
        $NestedObjectArray = @()
        $CatName = "UAC" 
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {            
                1 { 
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }                             
            }

            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }
        }


        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin'] -eq '4,2') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = "UAC: Behavior of the elevation prompt for administrators in Admin Approval Mode"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        
        # This particular policy can have 2 values and they are both acceptable depending on whichever user selects
        [string]$ConsentPromptBehaviorUserValue = $SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorUser']
        # This option is automatically applied when UAC category is run
        if ($ConsentPromptBehaviorUserValue -eq '4,1') {        
            $ConsentPromptBehaviorUserCompliance = $true
            $IndividualItemResult = 'Prompt for credentials on the secure desktop'
        }
        # This option prompts for additional confirmation before it's applied
        elseif ($ConsentPromptBehaviorUserValue -eq '4,0') {
            $ConsentPromptBehaviorUserCompliance = $true
            $IndividualItemResult = 'Automatically deny elevation requests'
        }
        # If none of them is applied then return false for compliance and N/A for value
        else {
            $ConsentPromptBehaviorUserCompliance = $false
            $IndividualItemResult = 'N/A'
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "UAC: Behavior of the elevation prompt for standard users"
            Value     = $IndividualItemResult
            Compliant = $ConsentPromptBehaviorUserCompliance
            Category  = $CatName
            Method    = "Security Group Policy"
        }   

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]($($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures'] -eq '4,1') ? $True : $False)
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'UAC: Only elevate executables that are signed and validated'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
                
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion User-Account-Control-Category
    
        #Region Device-Guard-Category
        Write-Progress -Activity 'Validating Device Guard Category' -Status 'Processing...' -PercentComplete 65
        $NestedObjectArray = @()
        $CatName = "Device Guard"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {
                    # Write-Host "$($Item.DropDownListName)" -ForegroundColor Yellow
                    # DropDown 1
                    $1index = $Item.DropDownListName.IndexOf("Select Platform Security Level:")
                    #Write-Host "$1index" -ForegroundColor Yellow
    
                    $1DropDownState = $Item.DropDownListState[$1index]
                    #Write-Host "$1DropDownState" -ForegroundColor Yellow
    
                    $1DropDownValue = $Item.DropDownListValue[$1index]
                    #Write-Host "$1DropDownValue" -ForegroundColor Yellow
    
                    # DropDown 2
                    $2index = $Item.DropDownListName.IndexOf("Virtualization Based Protection of Code Integrity:")
                    # Write-Host "$2index" -ForegroundColor Yellow
    
                    $2DropDownState = $Item.DropDownListState[$2index]
                    # Write-Host "$2DropDownState" -ForegroundColor Yellow
    
                    $2DropDownValue = $Item.DropDownListValue[$2index]
                    # Write-Host "$2DropDownValue" -ForegroundColor Yellow
               
                    # DropDown 3
                    $3index = $Item.DropDownListName.IndexOf("Credential Guard Configuration:")
                    # Write-Host "$3index" -ForegroundColor Yellow
    
                    $3DropDownState = $Item.DropDownListState[$3index]
                    # Write-Host "$3DropDownState" -ForegroundColor Yellow
    
                    $3DropDownValue = $Item.DropDownListValue[$3index]
                    # Write-Host "$3DropDownValue" -ForegroundColor Yellow
    
                    # DropDown 4
                    $4index = $Item.DropDownListName.IndexOf("Secure Launch Configuration:")
                    # Write-Host "$4index" -ForegroundColor Yellow
    
                    $4DropDownState = $Item.DropDownListState[$4index]
                    # Write-Host "$4DropDownState" -ForegroundColor Yellow
    
                    $4DropDownValue = $Item.DropDownListValue[$4index]
                    # Write-Host "$4DropDownValue" -ForegroundColor Yellow
    
                    # DropDown 5
                    $5index = $Item.DropDownListName.IndexOf("Kernel-mode Hardware-enforced Stack Protection:")
                    # Write-Host "$5index" -ForegroundColor Yellow
                
                    $5DropDownState = $Item.DropDownListState[$5index]
                    # Write-Host "$5DropDownState" -ForegroundColor Yellow
                
                    $5DropDownValue = $Item.DropDownListValue[$5index]
                    # Write-Host "$5DropDownValue" -ForegroundColor Yellow
    
                                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $1DropDownState -eq 'Enabled' `
                            -and $1DropDownValue -eq 'Secure Boot' `
                            -and $2DropDownState -eq 'Enabled' `
                            -and $2DropDownValue -eq 'Enabled with UEFI lock' `
                            -and $Item.CheckboxName -eq 'Require UEFI Memory Attributes Table' `
                            -and $Item.CheckboxState -eq 'Disabled' `
                            -and $3DropDownState -eq 'Enabled' `
                            -and $3DropDownValue -eq 'Enabled with UEFI lock' `
                            -and $4DropDownState -eq 'Enabled' `
                            -and $4DropDownValue -eq 'Enabled' `
                            -and $5DropDownState -eq 'Enabled' `
                            -and $5DropDownValue -eq 'Enabled in enforcement mode'                        
                    ) ? $True : $False   
                }
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"               
            }    
        }
    
    
        # Loop through each nested hash table inside the main Registeries hash table and check the item state using a switch statement
        foreach ($Key in $HashReg[$CatName].Keys) {
            # Get the correct object from the RegistriesOutput Object that contains all the group policies in the xml file
            $Item = $RegistriesOutput | Where-object { $_.Name -eq $HashReg[$CatName][$Key].Name -and $_.KeyPath -eq $HashReg[$CatName][$Key].KeyPath }
            switch ($Key) {
                1 {
                    [bool]$ItemState = ($Item.Number -eq '1') ? $True : $False   
                }
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashReg[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }    
        }
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Device-Guard-Category
        
        #Region Windows-Firewall-Category
        Write-Progress -Activity 'Validating Windows Firewall Category' -Status 'Processing...' -PercentComplete 70
        $NestedObjectArray = @()
        $CatName = "Windows Firewall"
              
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Default Inbound Action"
            Value     = $FirewallPoliciesOutput.DomainDefaultInboundAction
            Compliant = [bool]($FirewallPoliciesOutput.DomainDefaultInboundAction -eq $True ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }    
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Default Outbound Action"
            Value     = $FirewallPoliciesOutput.DomainDefaultOutboundAction
            Compliant = [bool]($FirewallPoliciesOutput.DomainDefaultOutboundAction -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Do Not Allow Exceptions"
            Value     = $FirewallPoliciesOutput.DomainDoNotAllowExceptions
            Compliant = [bool]($FirewallPoliciesOutput.DomainDoNotAllowExceptions -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Firewall Enabled"
            Value     = $FirewallPoliciesOutput.DomainEnableFirewall
            Compliant = [bool]($FirewallPoliciesOutput.DomainEnableFirewall -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Log File Path"
            Value     = $FirewallPoliciesOutput.DomainLogFilePath
            Compliant = [bool]($FirewallPoliciesOutput.DomainLogFilePath -eq '%systemroot%\system32\logfiles\firewall\domainfirewall.log' ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Log File Size"
            Value     = $FirewallPoliciesOutput.DomainLogFileSize
            Compliant = [bool]($FirewallPoliciesOutput.DomainLogFileSize -eq '32767' ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Log Dropped Packets"
            Value     = $FirewallPoliciesOutput.DomainLogDroppedPackets
            Compliant = [bool]($FirewallPoliciesOutput.DomainLogDroppedPackets -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Domain Profile Log Successful Connections"
            Value     = $FirewallPoliciesOutput.DomainLogSuccessfulConnections
            Compliant = [bool]($FirewallPoliciesOutput.DomainLogSuccessfulConnections -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Public Profile Disable Notifications"
            Value     = $FirewallPoliciesOutput.PublicDisableNotifications
            Compliant = [bool]($FirewallPoliciesOutput.PublicDisableNotifications -eq $false ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Public Profile Enable Firewall"
            Value     = $FirewallPoliciesOutput.PublicEnableFirewall
            Compliant = [bool]($FirewallPoliciesOutput.PublicEnableFirewall -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Public Profile Log File Path"
            Value     = $FirewallPoliciesOutput.PublicLogFilePath
            Compliant = [bool]($FirewallPoliciesOutput.PublicLogFilePath -eq '%systemroot%\system32\logfiles\firewall\publicfirewall.log' ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Public Profile Log File Size"
            Value     = $FirewallPoliciesOutput.PublicLogFileSize
            Compliant = [bool]($FirewallPoliciesOutput.PublicLogFileSize -eq '32767' ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Public Profile Log Dropped Packets"
            Value     = $FirewallPoliciesOutput.PublicLogDroppedPackets
            Compliant = [bool]($FirewallPoliciesOutput.PublicLogDroppedPackets -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Private Profile Disable Notifications"
            Value     = $FirewallPoliciesOutput.PrivateDisableNotifications
            Compliant = [bool]($FirewallPoliciesOutput.PrivateDisableNotifications -eq $false ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Private Profile Enable Firewall"
            Value     = $FirewallPoliciesOutput.PrivateEnableFirewall
            Compliant = [bool]($FirewallPoliciesOutput.PrivateEnableFirewall -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Private Profile Log File Path"
            Value     = $FirewallPoliciesOutput.PrivateLogFilePath
            Compliant = [bool]($FirewallPoliciesOutput.PrivateLogFilePath -eq '%systemroot%\system32\logfiles\firewall\privatefirewall.log' ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Private Profile Log File Size"
            Value     = $FirewallPoliciesOutput.PrivateLogFileSize
            Compliant = [bool]($FirewallPoliciesOutput.PrivateLogFileSize -eq '32767' ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Private Profile Log Dropped Packets"
            Value     = $FirewallPoliciesOutput.PrivateLogDroppedPackets
            Compliant = [bool]($FirewallPoliciesOutput.PrivateLogDroppedPackets -eq $true ? $True : $False)
            Category  = $CatName
            Method    = "Firewall Group Policy"
        }
    
        # Disables Multicast DNS (mDNS) UDP-in Firewall Rules for all 3 Firewall profiles - disables only 3 rules
        $RulesToDisable = get-NetFirewallRule -ErrorAction Stop |
        Where-Object { $_.RuleGroup -eq "@%SystemRoot%\system32\firewallapi.dll,-37302" -and $_.Direction -eq "inbound" } 
        # Check if the number of detected rules that need to be disabled match the number of rules with the same criteria that are disabled
        $RulesTarget = $RulesToDisable | Where-Object { $_.Enabled -eq 'False' }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool](($RulesTarget.count -eq $RulesToDisable.Count) ? $True : $false)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Firewall rules disabled for Multicast DNS (mDNS) UDP-in"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult            
            Category  = $CatName
            Method    = "Firewall Group Policy"
        } 
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Windows-Firewall-Category

        #Region Optional-Windows-Features-Category
        Write-Progress -Activity 'Validating Optional Windows Features Category' -Status 'Processing...' -PercentComplete 75
        $NestedObjectArray = @()
        $CatName = "Optional Windows Features"
         
        # Disable PowerShell v2 (needs 2 commands)
        [bool]$IndividualItemResult = ((get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2 -ErrorAction Stop).state -eq 'disabled') `
            -and [bool]((get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -ErrorAction Stop).state -eq 'disabled') ? $True : $false
      
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "PowerShell v2 is disabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName WorkFolders-Client -ErrorAction Stop).state -eq 'disabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Work Folders client is disabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName Printing-Foundation-Features -ErrorAction Stop).state -eq 'disabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Internet Printing Client is disabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName WindowsMediaPlayer -ErrorAction Stop).state -eq 'disabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Windows Media Player (legacy) is disabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName Windows-Defender-ApplicationGuard -ErrorAction Stop).state -eq 'enabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Microsoft Defender Application Guard is enabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM -ErrorAction Stop).state -eq 'enabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Windows Sandbox is enabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }
        
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -ErrorAction Stop).state -eq 'enabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Hyper-V is enabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -ErrorAction Stop).state -eq 'enabled')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Virtual Machine Platform is enabled"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((Get-WindowsCapability -Online -ErrorAction Stop | Where-Object { $_.Name -like '*wmic*' }).state -eq 'NotPresent')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "WMIC is not present"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((Get-WindowsCapability -Online -ErrorAction Stop | Where-Object { $_.Name -like '*Browser.InternetExplorer*' }).state -eq 'NotPresent')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Internet Explorer mode functionality for Edge is not present"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult            
            Category  = $CatName
            Method    = "Optional Windows Features"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((Get-WindowsCapability -Online -ErrorAction Stop | Where-Object { $_.Name -like '*Microsoft.Windows.Notepad.System*' }).state -eq 'NotPresent')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Legacy Notepad is not present"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult           
            Category  = $CatName
            Method    = "Optional Windows Features"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Optional-Windows-Features-Category

        #Region Windows-Networking-Category
        Write-Progress -Activity 'Validating Windows Networking Category' -Status 'Processing...' -PercentComplete 80
        $NestedObjectArray = @()
        $CatName = "Windows Networking"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                2 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                3 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Configure NetBIOS options:' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Disable NetBIOS name resolution'
                    ) ? $True : $False   
                }
                4 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                5 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }
        }
    
    
        # Check network location of all connections to see if they are public
        $Condition = Get-NetConnectionProfile -ErrorAction Stop | ForEach-Object { $_.NetworkCategory -eq 'public' }
        [bool]$IndividualItemResult = -not ($condition -contains $false) ? $True : $false 
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Network Location of all connections set to Public"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters" -Name "EnableLMHOSTS") -eq '0')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Disable LMHOSTS lookup protocol on all network adapters"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Registry Key"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine'] -eq '7,') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Network access: Remotely accessible registry paths'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine'] -eq '7,') ? $True : $False
        $NestedObjectArray += [pscustomobject]@{
            Name      = 'Network access: Remotely accessible registry paths and subpaths'
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Security Group Policy"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Windows-Networking-Category
        
        #Region Miscellaneous-Category
        Write-Progress -Activity 'Validating Miscellaneous Category' -Status 'Processing...' -PercentComplete 85
        $NestedObjectArray = @()
        $CatName = "Miscellaneous"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Send optional diagnostic data'                
                    ) ? $True : $False   
                }
                2 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                3 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                4 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                5 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                6 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Choose the boot-start drivers that can be initialized:' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Good only'                
                    ) ? $True : $False   
                }
                7 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False 
                }
                8 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False 
                }
                9 {
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'RPC Runtime Unauthenticated Client Restriction to Apply:' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Authenticated without exceptions'                
                    ) ? $True : $False   
                }
                10 { 
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $Item.DropDownListName -eq 'Mitigation Options' `
                            -and $Item.DropDownListState -eq 'Enabled' `
                            -and $Item.DropDownListValue -eq 'Block untrusted fonts and log events'                    
                    ) ? $True : $False   
                }   
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((Get-SmbServerConfiguration -ErrorAction Stop).encryptdata)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "SMB Encryption"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool](((auditpol /get /subcategory:"Other Logon/Logoff Events" /r | ConvertFrom-Csv -ErrorAction Stop).'Inclusion Setting' -eq 'Success and Failure') ? $True : $False)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Audit policy for Other Logon/Logoff Events"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Cmdlet"
        }
    

        # Checking if all user accounts are part of the Hyper-V security Group
        # Get all the enabled user accounts
        [string[]]$enabledUsers = (Get-LocalUser -ErrorAction Stop | Where-Object { $_.Enabled -eq "True" }).Name | Sort-Object
        # Get the members of the Hyper-V Administrators security group
        [string[]]$groupMembers = (Get-LocalGroupMember -Group "Hyper-V Administrators" -ErrorAction Stop).Name -replace "$($env:COMPUTERNAME)\\" | Sort-Object
    
        # Set the $MatchHyperVUsers variable to $True only if all enabled user accounts are part of the Hyper-V Security group, if one of them isn't part of the group then returns false
        [bool]$MatchHyperVUsers = $false # initialize the $MatchHyperVUsers variable to false
        for ($i = 0; $i -lt $enabledUsers.Count; $i++) {
            $MatchHyperVUsers = ($enabledUsers[$i] -ceq $groupMembers[$i]) ? $True : $false
        }

        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $NestedObjectArray += [pscustomobject]@{
            Name      = "All users are part of the Hyper-V Administrators group"
            Value     = $MatchHyperVUsers
            Compliant = $MatchHyperVUsers
            Category  = $CatName
            Method    = "Cmdlet"
        }
    
    
        $MatchRegistryKeys = @() # initialize the variable to false - an array that is going to hold only bool values
        foreach ($Item in $CSVFileContent) {
            if ($Item.category -eq 'Miscellaneous' -and $Item.Action -eq 'AddOrModify') {
                $path = $Item.Path
                $key = $Item.Key
                $value = $Item.value
        
                $regValue = Get-ItemPropertyValue -Path $path -Name $key
                # Store only boolean values in the $MatchRegistryKeys
                $MatchRegistryKeys += [bool]($regValue -eq $value)  
            }
        }
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]($MatchRegistryKeys -notcontains $false)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Registry Keys All correct"
            # Make sure the boolean array doesn't contain any $false values
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Registry Keys"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Miscellaneous-Category
    
        #Region Windows-Update-Category
        Write-Progress -Activity 'Validating Windows Update Category' -Status 'Processing...' -PercentComplete 90
        $NestedObjectArray = @()
        $CatName = "Windows Update"
        # Loop through each nested hash table inside the main Policies hash table and check the item state using a switch statement
        foreach ($Key in $HashPol[$CatName].Keys) {
            $Item = $PoliciesOutput | Where-object { $_.Name -eq $HashPol[$CatName][$Key].Name -and $_.Category -eq $HashPol[$CatName][$Key].Cat }
            switch ($Key) {
                1 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                2 {                
                    [bool]$ItemState = ($Item.State -eq 'Enabled') ? $True : $False   
                }
                3 {
                    # 2 Check boxes with the same name exists, but both of their States and Values are the same that's why this works
                    $1index = $Item.DropDownListName.IndexOf("Deadline (days):")                
                    $1DropDownState = $Item.DropDownListState[$1index]                
                    $1DropDownValue = $Item.DropDownListValue[$1index]
    
                    $2index = $Item.DropDownListName.IndexOf("Grace period (days):")                
                    $2DropDownState = $Item.DropDownListState[$2index]                
                    $2DropDownValue = $Item.DropDownListValue[$2index]
    
                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $1DropDownState -eq 'Enabled' `
                            -and $1DropDownValue -eq '0' `
                            -and $2DropDownState -eq 'Enabled' `
                            -and $2DropDownValue -eq '1' `
                            -and $Item.CheckboxName -eq "Don't auto-restart until end of grace period" `
                            -and $Item.CheckboxState -eq 'Disabled'                
                    ) ? $True : $False   
                }
                4 {
                    # 2 Check boxes with the same name exists, but both of their States and Values are the same that's why this works
                    $1index = $Item.DropDownListName.IndexOf("Configure automatic updating:")                
                    $1DropDownState = $Item.DropDownListState[$1index]                
                    $1DropDownValue = $Item.DropDownListValue[$1index]
    
                    $2index = $Item.CheckboxName.IndexOf("Install during automatic maintenance")
                    $2CheckBoxState = $Item.CheckboxState[$2index]
                
                    $3index = $Item.DropDownListName.IndexOf("Scheduled install day: ") # Has an extra space in the xml!
                    $3DropDownState = $Item.DropDownListState[$3index]                
                    $3DropDownValue = $Item.DropDownListValue[$3index]
    
                    $4index = $Item.DropDownListName.IndexOf("Scheduled install time:")                
                    $4DropDownState = $Item.DropDownListState[$4index]                
                    $4DropDownValue = $Item.DropDownListValue[$4index]
    
                    $5index = $Item.CheckboxName.IndexOf("Every week")
                    $5CheckBoxState = $Item.CheckboxState[$5index]
    
                    $6index = $Item.CheckboxName.IndexOf("First week of the month")
                    $6CheckBoxState = $Item.CheckboxState[$6index]
    
                    $7index = $Item.CheckboxName.IndexOf("Second week of the month")
                    $7CheckBoxState = $Item.CheckboxState[$7index]
    
                    $8index = $Item.CheckboxName.IndexOf("Third week of the month")
                    $8CheckBoxState = $Item.CheckboxState[$8index]
    
                    $9index = $Item.CheckboxName.IndexOf("Fourth week of the month")
                    $9CheckBoxState = $Item.CheckboxState[$9index]
    
                    $10index = $Item.CheckboxName.IndexOf("Install updates for other Microsoft products")
                    $10CheckBoxState = $Item.CheckboxState[$10index]
    
                
                    [bool]$ItemState = ($Item.State -eq 'Enabled' `
                            -and $1DropDownState -eq 'Enabled' `
                            -and $1DropDownValue -eq '4 - Auto download and schedule the install' `
                            -and $2CheckBoxState -eq 'Enabled' `
                            -and $3DropDownState -eq 'Enabled' `
                            -and $3DropDownValue -eq '0 - Every day' `
                            -and $4DropDownState -eq 'Enabled' `
                            -and $4DropDownValue -eq 'Automatic' `
                            -and $5CheckBoxState -eq 'Disabled' `
                            -and $6CheckBoxState -eq 'Disabled' `
                            -and $7CheckBoxState -eq 'Disabled' `
                            -and $8CheckBoxState -eq 'Disabled' `
                            -and $9CheckBoxState -eq 'Disabled' `
                            -and $10CheckBoxState -eq 'Enabled' `
                    ) ? $True : $False   
                }
            
            }
            # Create a custom object with 5 properties to store them as nested objects inside the main output object
            $NestedObjectArray += [pscustomobject]@{
                Name      = $HashPol[$CatName][$Key].Name
                Value     = $ItemState
                Compliant = $ItemState
                Category  = $CatName
                Method    = "Group Policy"                
            }
        }
    
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        $IndividualItemResult = [bool]((Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "RestartNotificationsAllowed2") -eq '1')
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Enable restart notification for Windows update"
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Registry Key"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Windows-Update-Category
        
        #Region Edge-Category
        Write-Progress -Activity 'Validating Edge Browser Category' -Status 'Processing...' -PercentComplete 95
        $NestedObjectArray = @()
        $CatName = "Edge"    
        $MatchRegistryKeys = @() # initialize the variable to false - an array that is going to hold only bool values
        foreach ($Item in $CSVFileContent) {
            if ($Item.category -eq 'Edge' -and $Item.Action -eq 'AddOrModify') {
                $path = $Item.Path
                $key = $Item.Key
                $value = $Item.value
            
                $regValue = Get-ItemPropertyValue -Path $path -Name $key
                # Store only boolean values in the $MatchRegistryKeys
                $MatchRegistryKeys += [bool]($regValue -eq $value)  
    
            }
        }   
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        # Make sure the boolean array doesn't contain any $false values
        $IndividualItemResult = [bool]($MatchRegistryKeys -notcontains $false)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Registry Keys All correct"            
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Registry Keys"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Edge-Category
        
        #Region Non-Admin-Category
        Write-Progress -Activity 'Validating Non-Admin Category' -Status 'Processing...' -PercentComplete 100
        $NestedObjectArray = @()
        $CatName = "Non-Admin"
    
        $MatchRegistryKeys = @() # initialize the variable to false - an array that is going to hold only bool values
        foreach ($Item in $CSVFileContent) {
            if ($Item.category -eq "NonAdmin" -and $Item.Action -eq 'AddOrModify') {
                $path = $Item.Path
                $key = $Item.Key
                $value = $Item.value
            
                $regValue = Get-ItemPropertyValue -Path $path -Name $key
                # Store only boolean values in the $MatchRegistryKeys
                $MatchRegistryKeys += [bool]($regValue -eq $value)  
    
            }
        }   
        # Create a custom object with 5 properties to store them as nested objects inside the main output object
        # Make sure the boolean array doesn't contain any $false values
        $IndividualItemResult = ($MatchRegistryKeys -notcontains $false)
        $NestedObjectArray += [pscustomobject]@{
            Name      = "Registry Keys All correct"            
            Value     = $IndividualItemResult
            Compliant = $IndividualItemResult
            Category  = $CatName
            Method    = "Registry Keys"
        }
    
        # Add the array of custom objects as a property to the $FinalMegaObject object outside the loop
        Add-Member -InputObject $FinalMegaObject -MemberType NoteProperty -Name $CatName -Value $NestedObjectArray -ErrorAction Stop
        #EndRegion Non-Admin-Category
   
        if ($ExportToCSV) {

            # An array to store the content of each category
            $CsvOutPutFileContent = @()
            $CsvOutPutFileContent += $FinalMegaObject.'Microsoft Defender'
            $CsvOutPutFileContent += $FinalMegaObject.ASR
            $CsvOutPutFileContent += $FinalMegaObject.Bitlocker
            $CsvOutPutFileContent += $FinalMegaObject.TLS
            $CsvOutPutFileContent += $FinalMegaObject.LockScreen
            $CsvOutPutFileContent += $FinalMegaObject.UAC
            $CsvOutPutFileContent += $FinalMegaObject.'Device Guard'
            $CsvOutPutFileContent += $FinalMegaObject.'Windows Firewall'
            $CsvOutPutFileContent += $FinalMegaObject.'Optional Windows Features'
            $CsvOutPutFileContent += $FinalMegaObject.'Windows Networking'
            $CsvOutPutFileContent += $FinalMegaObject.Miscellaneous
            $CsvOutPutFileContent += $FinalMegaObject.'Windows Update'
            $CsvOutPutFileContent += $FinalMegaObject.Edge
            $CsvOutPutFileContent += $FinalMegaObject.'Non-Admin'
            # Convert the array to CSV and store it in the Output.CSV file in the current working directory
            $CsvOutPutFileContent | ConvertTo-Csv -ErrorAction Stop | Out-File '.\Output.CSV' -Force -ErrorAction Stop        
        }
        
        if ($ShowAsObjectsOnly) {
            # return the main object that contains multiple nested objects
            return $FinalMegaObject
        }
        else {   

            #Region Colors
            $WritePlum = { Write-Output "$($PSStyle.Foreground.FromRGB(221,160,221))$($args[0])$($PSStyle.Reset)" }
            $WriteOrchid = { Write-Output "$($PSStyle.Foreground.FromRGB(218,112,214))$($args[0])$($PSStyle.Reset)" }
            $WriteFuchsia = { Write-Output "$($PSStyle.Foreground.FromRGB(255,0,255))$($args[0])$($PSStyle.Reset)" }
            $WriteMediumOrchid = { Write-Output "$($PSStyle.Foreground.FromRGB(186,85,211))$($args[0])$($PSStyle.Reset)" }
            $WriteMediumPurple = { Write-Output "$($PSStyle.Foreground.FromRGB(147,112,219))$($args[0])$($PSStyle.Reset)" }
            $WriteBlueViolet = { Write-Output "$($PSStyle.Foreground.FromRGB(138,43,226))$($args[0])$($PSStyle.Reset)" }
            $WriteDarkViolet = { Write-Output "$($PSStyle.Foreground.FromRGB(148,0,211))$($args[0])$($PSStyle.Reset)" }
            $WritePink = { Write-Output "$($PSStyle.Foreground.FromRGB(255,192,203))$($args[0])$($PSStyle.Reset)" }
            $WriteHotPink = { Write-Output "$($PSStyle.Foreground.FromRGB(255,105,180))$($args[0])$($PSStyle.Reset)" }
            $WriteDeepPink = { Write-Output "$($PSStyle.Foreground.FromRGB(255,20,147))$($args[0])$($PSStyle.Reset)" }
            $WriteMintGreen = { Write-Output "$($PSStyle.Foreground.FromRGB(152,255,152))$($args[0])$($PSStyle.Reset)" }
            $WriteOrange = { Write-Output "$($PSStyle.Foreground.FromRGB(255,165,0))$($args[0])$($PSStyle.Reset)" }            
            $WriteSkyBlue = { Write-Output "$($PSStyle.Foreground.FromRGB(135,206,235))$($args[0])$($PSStyle.Reset)" }
            
            $WriteRainbow1 = { 
                $text = $args[0]
                $colors = @(
                    [System.Drawing.Color]::Pink,
                    [System.Drawing.Color]::HotPink,
                    [System.Drawing.Color]::SkyBlue,
                    [System.Drawing.Color]::Pink,
                    [System.Drawing.Color]::HotPink,
                    [System.Drawing.Color]::SkyBlue,
                    [System.Drawing.Color]::Pink
                )

                $output = ""
                for ($i = 0; $i -lt $text.Length; $i++) {
                    $color = $colors[$i % $colors.Length]
                    $output += "$($PSStyle.Foreground.FromRGB($color.R, $color.G, $color.B))$($text[$i])$($PSStyle.Reset)"
                }
                Write-Output $output
            }          
              
            $WriteRainbow2 = { 
                $text = $args[0]
                $colors = @(
                    [System.Drawing.Color]::Pink,
                    [System.Drawing.Color]::HotPink,
                    [System.Drawing.Color]::SkyBlue,
                    [System.Drawing.Color]::HotPink,
                    [System.Drawing.Color]::SkyBlue,
                    [System.Drawing.Color]::LightSkyBlue,
                    [System.Drawing.Color]::Lavender,
                    [System.Drawing.Color]::LightGreen,
                    [System.Drawing.Color]::Coral,
                    [System.Drawing.Color]::Plum,
                    [System.Drawing.Color]::Gold
                )
              
                $output = ""
                for ($i = 0; $i -lt $text.Length; $i++) {
                    $color = $colors[$i % $colors.Length]
                    $output += "$($PSStyle.Foreground.FromRGB($color.R, $color.G, $color.B))$($text[$i])$($PSStyle.Reset)"
                }
                Write-Output $output
            }
            #Endregion Colors
    
            # Show all properties in list
            if ($DetailedDisplay) {
                & $WritePlum "`n-------------Microsoft Defender Category-------------"
                $FinalMegaObject.'Microsoft Defender' | Format-list * -ErrorAction Stop
    
                & $WriteOrchid "`n-------------Attack Surface Reduction Rules Category-------------"
                $FinalMegaObject.ASR | Format-list * -ErrorAction Stop
    
                & $WriteFuchsia "`n-------------Bitlocker Category-------------"
                $FinalMegaObject.Bitlocker | Format-list * -ErrorAction Stop
    
                & $WriteMediumOrchid "`n-------------TLS Category-------------"
                $FinalMegaObject.TLS | Format-list * -ErrorAction Stop
    
                & $WriteMediumPurple "`n-------------Lock Screen Category-------------"
                $FinalMegaObject.LockScreen | Format-list * -ErrorAction Stop
    
                & $WriteBlueViolet "`n-------------User Account Control Category-------------"
                $FinalMegaObject.UAC | Format-list * -ErrorAction Stop
    
                & $WriteDarkViolet "`n-------------Device Guard Category-------------"
                $FinalMegaObject.'Device Guard' | Format-list * -ErrorAction Stop
    
                & $WritePink "`n-------------Windows Firewall Category-------------"
                $FinalMegaObject.'Windows Firewall' | Format-list * -ErrorAction Stop

                & $WriteSkyBlue "`n-------------Optional Windows Features Category-------------"
                $FinalMegaObject.'Optional Windows Features' | Format-list * -ErrorAction Stop
    
                & $WriteHotPink "`n-------------Windows Networking Category-------------"
                $FinalMegaObject.'Windows Networking' | Format-list * -ErrorAction Stop
    
                & $WriteDeepPink "`n-------------Miscellaneous Category-------------"
                $FinalMegaObject.Miscellaneous | Format-list * -ErrorAction Stop
    
                & $WriteMintGreen "`n-------------Windows Update Category-------------"
                $FinalMegaObject.'Windows Update' | Format-list * -ErrorAction Stop
    
                & $WriteOrange "`n-------------Microsoft Edge Category-------------"
                $FinalMegaObject.Edge | Format-list * -ErrorAction Stop
    
                & $WriteSkyBlue "`n-------------Non-Admin Category-------------"
                $FinalMegaObject.'Non-Admin' | Format-list * -ErrorAction Stop
            }

            # Show properties that matter in a table
            else {
                
                & $WritePlum "`n-------------Microsoft Defender Category-------------"
                $FinalMegaObject.'Microsoft Defender' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteOrchid "`n-------------Attack Surface Reduction Rules Category-------------"
                $FinalMegaObject.ASR | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteFuchsia "`n-------------Bitlocker Category-------------"
                $FinalMegaObject.Bitlocker | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteMediumOrchid "`n-------------TLS Category-------------"
                $FinalMegaObject.TLS | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteMediumPurple "`n-------------Lock Screen Category-------------"
                $FinalMegaObject.LockScreen | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteBlueViolet "`n-------------User Account Control Category-------------"
                $FinalMegaObject.UAC | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteDarkViolet "`n-------------Device Guard Category-------------"
                $FinalMegaObject.'Device Guard' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WritePink "`n-------------Windows Firewall Category-------------"
                $FinalMegaObject.'Windows Firewall' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
    
                & $WriteSkyBlue "`n-------------Optional Windows Features Category-------------"
                $FinalMegaObject.'Optional Windows Features' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteHotPink "`n-------------Windows Networking Category-------------"
                $FinalMegaObject.'Windows Networking' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteDeepPink "`n-------------Miscellaneous Category-------------"
                $FinalMegaObject.Miscellaneous | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteMintGreen "`n-------------Windows Update Category-------------"
                $FinalMegaObject.'Windows Update' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteOrange "`n-------------Microsoft Edge Category-------------"
                $FinalMegaObject.Edge | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop
        
                & $WriteSkyBlue "`n-------------Non-Admin Category-------------"
                $FinalMegaObject.'Non-Admin' | Format-Table -AutoSize -Property Name, Compliant, Value -ErrorAction Stop               
            }
            
            # Counting the number of $True Compliant values in the Final Output Object
            [int]$TotalTrueValuesInOutPut = ($FinalMegaObject.'Microsoft Defender' | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.ASR | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.Bitlocker | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.TLS | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.LockScreen | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.UAC | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.'Device Guard' | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.'Windows Firewall' | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.'Optional Windows Features' | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.'Windows Networking' | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.Miscellaneous | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.'Windows Update' | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.Edge | Where-Object { $_.Compliant -eq $True }).value.Count + `
                [int]($FinalMegaObject.'Non-Admin' | Where-Object { $_.Compliant -eq $True }).value.Count


            #Region ASCII-Arts
            $WhenValue1To20 = @"
                OH
                 
                N
                    O
                    O
                     o
                     o
                      o
                     o
                     。
                    。
                   .
                   .
                    .
                    .
                 
"@

                         
                
            $WhenValue21To40 = @"
 
‎‏‏‎‏‏‎⣿⣿⣷⡁⢆⠈⠕⢕⢂⢕⢂⢕⢂⢔⢂⢕⢄⠂⣂⠂⠆⢂⢕⢂⢕⢂⢕⢂⢕⢂
‎‏‏‎‏‏‎⣿⣿⣿⡷⠊⡢⡹⣦⡑⢂⢕⢂⢕⢂⢕⢂⠕⠔⠌⠝⠛⠶⠶⢶⣦⣄⢂⢕⢂⢕
‎‏‏‎‏‏‎⣿⣿⠏⣠⣾⣦⡐⢌⢿⣷⣦⣅⡑⠕⠡⠐⢿⠿⣛⠟⠛⠛⠛⠛⠡⢷⡈⢂⢕⢂
‎‏‏‎‏‏‎⠟⣡⣾⣿⣿⣿⣿⣦⣑⠝⢿⣿⣿⣿⣿⣿⡵⢁⣤⣶⣶⣿⢿⢿⢿⡟⢻⣤⢑⢂
‎‏‏‎‏‏‎⣾⣿⣿⡿⢟⣛⣻⣿⣿⣿⣦⣬⣙⣻⣿⣿⣷⣿⣿⢟⢝⢕⢕⢕⢕⢽⣿⣿⣷⣔
‎‏‏‎‏‏‎⣿⣿⠵⠚⠉⢀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣗⢕⢕⢕⢕⢕⢕⣽⣿⣿⣿⣿
‎‏‏‎‏‏‎⢷⣂⣠⣴⣾⡿⡿⡻⡻⣿⣿⣴⣿⣿⣿⣿⣿⣿⣷⣵⣵⣵⣷⣿⣿⣿⣿⣿⣿⡿
‎‏‏‎‏‏‎⢌⠻⣿⡿⡫⡪⡪⡪⡪⣺⣿⣿⣿⣿⣿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃
‎‏‏‎‏‏‎⠣⡁⠹⡪⡪⡪⡪⣪⣾⣿⣿⣿⣿⠋⠐⢉⢍⢄⢌⠻⣿⣿⣿⣿⣿⣿⣿⣿⠏⠈
‎‏‏‎‏‏‎⡣⡘⢄⠙⣾⣾⣾⣿⣿⣿⣿⣿⣿⡀⢐⢕⢕⢕⢕⢕⡘⣿⣿⣿⣿⣿⣿⠏⠠⠈
‎‏‏‎‏‏‎⠌⢊⢂⢣⠹⣿⣿⣿⣿⣿⣿⣿⣿⣧⢐⢕⢕⢕⢕⢕⢅⣿⣿⣿⣿⡿⢋⢜⠠⠈
‎‏‏‎‏‏‎⠄⠁⠕⢝⡢⠈⠻⣿⣿⣿⣿⣿⣿⣿⣷⣕⣑⣑⣑⣵⣿⣿⣿⡿⢋⢔⢕⣿⠠⠈
‎‏‏‎‏‏‎⠨⡂⡀⢑⢕⡅⠂⠄⠉⠛⠻⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⢕⣿⣿⠠⠈
‎‏‏‎‏‏‎⠄⠪⣂⠁⢕⠆⠄⠂⠄⠁⡀⠂⡀⠄⢈⠉⢍⢛⢛⢛⢋⢔⢕⢕⢕⣽⣿⣿⠠⠈
 
"@

         
                
            $WhenValue41To60 = @"
   
            ⣿⡟⠙⠛⠋⠩⠭⣉⡛⢛⠫⠭⠄⠒⠄⠄⠄⠈⠉⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿
            ⣿⡇⠄⠄⠄⠄⣠⠖⠋⣀⡤⠄⠒⠄⠄⠄⠄⠄⠄⠄⠄⠄⣈⡭⠭⠄⠄⠄⠉⠙
            ⣿⡇⠄⠄⢀⣞⣡⠴⠚⠁⠄⠄⢀⠠⠄⠄⠄⠄⠄⠄⠄⠉⠄⠄⠄⠄⠄⠄⠄⠄
            ⣿⡇⠄⡴⠁⡜⣵⢗⢀⠄⢠⡔⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄
            ⣿⡇⡜⠄⡜⠄⠄⠄⠉⣠⠋⠠⠄⢀⡄⠄⠄⣠⣆⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢸
            ⣿⠸⠄⡼⠄⠄⠄⠄⢰⠁⠄⠄⠄⠈⣀⣠⣬⣭⣛⠄⠁⠄⡄⠄⠄⠄⠄⠄⢀⣿
            ⣏⠄⢀⠁⠄⠄⠄⠄⠇⢀⣠⣴⣶⣿⣿⣿⣿⣿⣿⡇⠄⠄⡇⠄⠄⠄⠄⢀⣾⣿
            ⣿⣸⠈⠄⠄⠰⠾⠴⢾⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⢁⣾⢀⠁⠄⠄⠄⢠⢸⣿⣿
            ⣿⣿⣆⠄⠆⠄⣦⣶⣦⣌⣿⣿⣿⣿⣷⣋⣀⣈⠙⠛⡛⠌⠄⠄⠄⠄⢸⢸⣿⣿
            ⣿⣿⣿⠄⠄⠄⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠈⠄⠄⠄⠄⠄⠈⢸⣿⣿
            ⣿⣿⣿⠄⠄⠄⠘⣿⣿⣿⡆⢀⣈⣉⢉⣿⣿⣯⣄⡄⠄⠄⠄⠄⠄⠄⠄⠈⣿⣿
            ⣿⣿⡟⡜⠄⠄⠄⠄⠙⠿⣿⣧⣽⣍⣾⣿⠿⠛⠁⠄⠄⠄⠄⠄⠄⠄⠄⠃⢿⣿
            ⣿⡿⠰⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠩⠔⠒⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠐⠘⣿
            ⣿⠃⠃⠄⠄⠄⠄⠄⠄⣀⢀⠄⠄⡀⡀⢀⣤⣴⣤⣤⣀⣀⠄⠄⠄⠄⠄⠄⠁⢹
 
"@

                
                
                
            $WhenValue61To80 = @"
                 
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⡿⠋⠈⠻⣮⣳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⡿⠋⠀⠀⠀⠀⠙⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀⠀⠈⠛⠛⠿⠿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣾⡿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣶⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⣀⣠⣤⣤⣀⡀⠀⠀⣀⣴⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣄⠀⠀
                ⢀⣤⣾⡿⠟⠛⠛⢿⣿⣶⣾⣿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣀⣀⣤⣶⣿⡿⠿⢿⣿⡀⠀
                ⣿⣿⠏⠀⢰⡆⠀⠀⠉⢿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⢿⡿⠟⠋⠁⠀⠀⢸⣿⠇⠀
                ⣿⡟⠀⣀⠈⣀⡀⠒⠃⠀⠙⣿⡆⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠇⠀
                ⣿⡇⠀⠛⢠⡋⢙⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠀⠀
                ⣿⣧⠀⠀⠀⠓⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠋⠀⠀⢸⣧⣤⣤⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⡿⠀⠀
                ⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠻⣷⣶⣶⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⠁⠀⠀
                ⠈⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⡏⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⡄⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⡄⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⣶⣤⣤⣀⡀⠀⠀⠀⢀⣴⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⡿⣄
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣹
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⠀⢸⣧
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣆⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣶⣾⣿⣿⣿⣿⣤⣄⣀⡀⠀⠀⠀⣿
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢿⣻⣷⣶⣾⣿⣿⡿⢯⣛⣛⡋⠁⠀⠀⠉⠙⠛⠛⠿⣿⣿⡷⣶⣿
                 
"@

                
                
            $WhenValue81To88 = @"
                 
                ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠔⠶⠒⠉⠈⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠪⣦⢄⣀⡠⠁⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣤⣤⣤⣤⣄⣀⣀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⣰⣶⣶⣦⠶⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠉⠉⢷⡔⠒⠚⢽⠃⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣰⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⢅⢰⣾⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⣀⡴⠞⠛⠉⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣧⠀⠀⠀⠀⠀
                ⠀⣀⣀⣤⣤⡞⠋⠀⠀⠀⢠⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠀
                ⢸⡏⠉⣴⠏⠀⠀⠀⠀⠀⢸⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀
                ⠈⣧⢰⠏⠀⠀⠀⠀⠀⠀⢸⡆⠀⠀⠀⠀⠀⠀⠀⠀⠰⠯⠥⠠⠒⠄⠀⠀⠀⠀⠀⠀⢠⠀⣿⠀⠀⠀⠀
                ⠀⠈⣿⠀⠀⠀⠀⠀⠀⠀⠈⡧⢀⢻⠿⠀⠲⡟⣞⠀⠀⠀⠀⠈⠀⠁⠀⠀⠀⠀⠀⢀⠆⣰⠇⠀⠀⠀⠀
                ⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⣧⡀⠃⠀⠀⠀⠱⣼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣂⡴⠋⠀⣀⡀⠀⠀
                ⠀⠀⢹⡄⠀⠀⠀⠀⠀⠀⠀⠹⣜⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠿⡻⢦⣄⣰⠏⣿⠀⠀
                ⠀⠀⠀⢿⡢⡀⠀⠀⠀⠀⠀⠀⠙⠳⢮⣥⣤⣤⠶⠖⠒⠛⠓⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢌⢻⣴⠏⠀⠀
                ⠀⠀⠀⠀⠻⣮⣒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣧⣤⣀⣀⣀⣤⡴⠖⠛⢻⡆⠀⠀⠀⠀⠀⠀⢣⢻⡄⠀⠀
                ⠀⠀⠀⠀⠀⠀⠉⠛⠒⠶⠶⡶⢶⠛⠛⠁⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⠞⠁⠀⠀⠀⠀⠀⠀⠈⢜⢧⣄⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠃⠇⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠉⢻⠀⠀⠀⠀⠀⠀⠀⢀⣀⠀⠀⠉⠈⣷
                ⠀⠀⠀⠀⠀⠀⠀⣼⠟⠷⣿⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠲⠶⢶⣶⠶⠶⢛⣻⠏⠙⠛⠛⠛⠁
                ⠀⠀⠀⠀⠀⠀⠀⠈⠷⣤⣀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠉⠛⠓⠚⠋⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣟⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢹⡟⡟⢻⡟⠛⢻⡄⠀⠀⣸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠈⠷⠧⠾⠀⠀⠀⠻⣦⡴⠏⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠁⠀⠀⠀⠀⠈⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                 
"@

                
                
            $WhenValueAbove88 = @"
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⢠⣶⣶⣶⣦⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⠟⠛⢿⣶⡄⠀⢀⣀⣤⣤⣦⣤⡀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⢠⣿⠋⠀⠀⠈⠙⠻⢿⣶⣶⣶⣶⣶⣶⣶⣿⠟⠀⠀⠀⠀⠹⣿⡿⠟⠋⠉⠁⠈⢻⣷⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⣼⡧⠀⠀⠀⠀⠀⠀⠀⠉⠁⠀⠀⠀⠀⣾⡏⠀⠀⢠⣾⢶⣶⣽⣷⣄⡀⠀⠀⠀⠈⣿⡆⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⢸⣧⣾⠟⠉⠉⠙⢿⣿⠿⠿⠿⣿⣇⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⢸⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣷⣄⣀⣠⣼⣿⠀⠀⠀⠀⣸⣿⣦⡀⠀⠈⣿⡄⠀⠀⠀
                ⠀⠀⠀⠀⠀⢠⣾⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠻⣷⣤⣤⣶⣿⣧⣿⠃⠀⣰⣿⠁⠀⠀⠀
                ⠀⠀⠀⠀⠀⣾⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠹⣿⣀⠀⠀⣀⣴⣿⣧⠀⠀⠀⠀
                ⠀⠀⠀⠀⢸⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⠿⠿⠛⠉⢸⣿⠀⠀⠀⠀
                ⢀⣠⣤⣤⣼⣿⣤⣄⠀⠀⠀⡶⠟⠻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣶⣶⡄⠀⠀⠀⠀⢀⣀⣿⣄⣀⠀⠀
                ⠀⠉⠉⠉⢹⣿⣩⣿⠿⠿⣶⡄⠀⠀⠀⠀⠀⠀⠀⢀⣤⠶⣤⡀⠀⠀⠀⠀⠀⠿⡿⠃⠀⠀⠀⠘⠛⠛⣿⠋⠉⠙⠃
                ⠀⠀⠀⣤⣼⣿⣿⡇⠀⠀⠸⣿⠀⠀⠀⠀⠀⠀⠀⠘⠿⣤⡼⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⣼⣿⣀⠀⠀⠀
                ⠀⠀⣾⡏⠀⠈⠙⢧⠀⠀⠀⢿⣧⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠟⠙⠛⠓⠀
                ⠀⠀⠹⣷⡀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠻⣷⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⣶⣿⣯⡀⠀⠀⠀⠀
                ⠀⠀⠀⠈⠻⣷⣄⠀⠀⠀⢀⣴⠿⠿⠗⠈⢻⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣾⠟⠋⠉⠛⠷⠄⠀⠀
                ⠀⠀⠀⠀⠀⢸⡏⠀⠀⠀⢿⣇⠀⢀⣠⡄⢘⣿⣶⣶⣤⣤⣤⣤⣀⣤⣤⣤⣤⣶⣶⡿⠿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠘⣿⡄⠀⠀⠈⠛⠛⠛⠋⠁⣼⡟⠈⠻⣿⣿⣿⣿⡿⠛⠛⢿⣿⣿⣿⣡⣾⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠙⢿⣦⣄⣀⣀⣀⣀⣴⣾⣿⡁⠀⠀⠀⡉⣉⠁⠀⠀⣠⣾⠟⠉⠉⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⠛⠛⠛⠉⠀⠹⣿⣶⣤⣤⣷⣿⣧⣴⣾⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠻⢦⣭⡽⣯⣡⡴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                 
"@

            #Endregion ASCII-Arts

            # Total number of Compliant values not equal to N/A
            [int]$TotalNumberOfTrueCompliantValues = 135
                
            switch ($True) {
                    ($TotalTrueValuesInOutPut -in 1..20) { & $WriteRainbow2 "$WhenValue1To20`nYour compliance score is $TotalTrueValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" }                    
                    ($TotalTrueValuesInOutPut -in 21..40) { & $WriteRainbow1 "$WhenValue21To40`nYour compliance score is $TotalTrueValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" }
                    ($TotalTrueValuesInOutPut -in 41..60) { & $WriteRainbow1 "$WhenValue41To60`nYour compliance score is $TotalTrueValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" }
                    ($TotalTrueValuesInOutPut -in 61..80) { & $WriteRainbow2 "$WhenValue61To80`nYour compliance score is $TotalTrueValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" }
                    ($TotalTrueValuesInOutPut -in 81..100) { & $WriteRainbow1 "$WhenValue81To88`nYour compliance score is $TotalTrueValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" }
                    ($TotalTrueValuesInOutPut -gt 100) { & $WriteRainbow2 "$WhenValueAbove88`nYour compliance score is $TotalTrueValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" }
            } 
        }
    
    } # End of Process Block

    end {
        # Clean up
        Remove-Item -Path ".\security_policy.inf" -Force -ErrorAction Stop
        Remove-Item -Path ".\Registry.csv" -Force -ErrorAction Stop
        Remove-Item -Path ".\Group-Policies.json" -Force -ErrorAction Stop
        Remove-Item -Path ".\GPResult.xml" -Force -ErrorAction Stop
    }

    <#
.SYNOPSIS
Checks the compliance of a system with the Harden Windows Security script guidelines
 
.LINK
https://github.com/HotCakeX/Harden-Windows-Security
 
.DESCRIPTION
Checks the compliance of a system with the Harden Windows Security script. Checks the applied Group policies, registry keys and PowerShell cmdlets used by the hardening script.
 
.COMPONENT
Gpresult, Secedit, PowerShell, Registry
 
.FUNCTIONALITY
Uses Gpresult and Secedit to first export the effective Group policies and Security policies, then goes through them and checks them against the Harden Windows Security's guidelines.
 
.EXAMPLE
($result.Microsoft Defender | Where-Object {$_.name -eq 'Controlled Folder Access Exclusions'}).value.programs
 
# Do this to get the Controlled Folder Access Programs list when using ShowAsObjectsOnly optional parameter to output an object
 
.EXAMPLE
$result.Microsoft Defender
 
# Do this to only see the result for the Microsoft Defender category when using ShowAsObjectsOnly optional parameter to output an object
 
.PARAMETER ExportToCSV
Export the output to a CSV file in the current working directory
 
.PARAMETER ShowAsObjectsOnly
Returns a nested object instead of writing strings on the PowerShell console, it can be assigned to a variable
 
.PARAMETER DetailedDisplay
Shows the output on the PowerShell console with more details and in the list format instead of table format
 
#>
    

}

# Set PSReadline tab completion to complete menu for easier access to available parameters - Only for the current session
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete