Confirm-SystemCompliance.psm1
# To parse the ini file from the output of the "Secedit /export /cfg .\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 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 { [version]$CurrentVersion = (Test-modulemanifest "$psscriptroot\Harden-Windows-Security-Module.psd1").Version try { [version]$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." } if ($CurrentVersion -lt $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 Install-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force Import-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force -Global } # 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 Import-Module -Name 'Harden-Windows-Security-Module' -RequiredVersion $LatestVersion -Force -Global } # Make sure the old version 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 } } # Makes 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.' } Write-Progress -Activity 'Checking for updates' -Status 'Processing...' -PercentComplete 10 # Self update the module Update-self # Stop operation as soon as there is an error anywhere, unless explicitly specified otherwise $ErrorActionPreference = 'Stop' Write-Progress -Activity 'Gathering Security Policy Information' -Status 'Processing...' -PercentComplete 15 # Get the security group policies Secedit /export /cfg .\security_policy.inf | Out-Null # Storing the output of the ini file parsing function [PSCustomObject]$SecurityPoliciesIni = ConvertFrom-IniFile -IniFile .\security_policy.inf Write-Progress -Activity 'Downloading Registry CSV File from GitHub' -Status 'Processing...' -PercentComplete 20 # Import the CSV file Invoke-WebRequest -Uri "https://raw.githubusercontent.com/HotCakeX/Harden-Windows-Security/main/Harden-Windows-Security%20Module/Resources/Registry%20resources.csv" -OutFile '.\Registry resources.csv' $CSVResource = Import-Csv -Path '.\Registry resources.csv' # An object to hold all the initial registry items $AllRegistryItems = @() # Loop through each row in the CSV file foreach ($Row in $CSVResource) { $AllRegistryItems += [PSCustomObject]@{ FriendlyName = $Row.FriendlyName category = $Row.Category key = $Row.Key value = $Row.Value name = $Row.Name type = $Row.Type regPath = "Registry::$($Row.Key)" # Build the registry path Method = $Row.Origin } } # An object to store the FINAL results $FinalMegaObject = [PSCustomObject]@{} # Function for processing each item in $AllRegistryItems for each category function Invoke-CategoryProcessing { param( [string]$CatName, [string]$Method ) # an array to hold the output [System.Array]$output = @() foreach ($item in $AllRegistryItems | Where-Object { $_.category -eq $CatName } | Where-Object { $_.Method -eq $Method }) { # Initialize a flag to indicate if the key exists [bool]$keyExists = $false # Initialize a flag to indicate if the value exists and matches the type [bool]$valueMatches = $false # Try to get the registry key try { $regKey = Get-Item -Path $item.regPath # If no error is thrown, the key exists $keyExists = $true # Try to get the registry value and type try { $regValue = Get-ItemPropertyValue -Path $item.regPath -Name $item.name # If no error is thrown, the value exists # Check if the value matches the expected one if ($regValue -eq $item.value) { # If it matches, set the flag to true $valueMatches = $true } } catch { # If an error is thrown, the value does not exist or is not accessible # Do nothing, the flag remains false } } catch { # If an error is thrown, the key does not exist or is not accessible # Do nothing, the flag remains false } # Create a custom object with the results for this row $output += [PSCustomObject]@{ # Category = $item.category # Key = $item.key # Name = $item.name # KeyExists = $keyExists # ValueMatches = $valueMatches # Type = $item.type # Value = $item.value FriendlyName = $item.FriendlyName Compliant = $valueMatches Value = $item.value Name = $item.name Category = $CatName Method = $Method } } return $output } } process { #Region Microsoft-Defender-Category Write-Progress -Activity 'Validating Microsoft Defender Category' -Status 'Processing...' -PercentComplete 35 # An array to store the nested custom objects, inside the main output object [System.Array]$NestedObjectArray = @() [String]$CatName = 'Microsoft Defender' # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # For PowerShell Cmdlet $IndividualItemResult = $((Get-MpPreference).AllowSwitchToAsyncInspection) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'AllowSwitchToAsyncInspection' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'AllowSwitchToAsyncInspection' Category = $CatName Method = 'Cmdlet' } # For PowerShell Cmdlet $IndividualItemResult = $((Get-MpPreference).oobeEnableRtpAndSigUpdate) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'oobeEnableRtpAndSigUpdate' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'oobeEnableRtpAndSigUpdate' Category = $CatName Method = 'Cmdlet' } # For PowerShell Cmdlet $IndividualItemResult = $((Get-MpPreference).IntelTDTEnabled) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'IntelTDTEnabled' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'IntelTDTEnabled' Category = $CatName Method = 'Cmdlet' } # For PowerShell Cmdlet $IndividualItemResult = $((Get-ProcessMitigation -System).aslr.ForceRelocateImages) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Mandatory ASLR' Compliant = $IndividualItemResult -eq 'on' ? $True : $false Value = $IndividualItemResult Name = 'Mandatory ASLR' 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] # 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] } $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]@{ FriendlyName = (Get-Culture).name -eq 'en-US' ? 'BCDEDIT NX Value' : "(Not accurate on non-English system languages) - BCDEDIT NX Value" Compliant = $IndividualItemResult -eq 'AlwaysOn' ? $True : $false Value = $IndividualItemResult Name = (Get-Culture).name -eq 'en-US' ? 'BCDEDIT NX Value' : "(Not accurate on non-English system languages) - BCDEDIT NX Value" Category = $CatName Method = 'Cmdlet' } # For PowerShell Cmdlet $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Smart App Control State' Compliant = 'N/A' Value = $((Get-MpComputerStatus).SmartAppControlState) Name = 'Smart App Control State' 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]@{ FriendlyName = 'Fast weekly Microsoft recommended driver block list update' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Fast weekly Microsoft recommended driver block list update' Category = $CatName Method = 'Cmdlet' } [hashtable]$DefenderPlatformUpdatesChannels = @{ 0 = 'NotConfigured' 2 = 'Beta' 3 = 'Preview' 4 = 'Staged' 5 = 'Broad' 6 = 'Delayed' } # For PowerShell Cmdlet $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Microsoft Defender Platform Updates Channel' Compliant = 'N/A' Value = $($DefenderPlatformUpdatesChannels[[int](get-mppreference).PlatformUpdatesChannel]) Name = 'Microsoft Defender Platform Updates Channel' Category = $CatName Method = 'Cmdlet' } [hashtable]$DefenderEngineUpdatesChannels = @{ 0 = 'NotConfigured' 2 = 'Beta' 3 = 'Preview' 4 = 'Staged' 5 = 'Broad' 6 = 'Delayed' } # For PowerShell Cmdlet $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Microsoft Defender Engine Updates Channel' Compliant = 'N/A' Value = $($DefenderEngineUpdatesChannels[[int](get-mppreference).EngineUpdatesChannel]) Name = 'Microsoft Defender Engine Updates Channel' Category = $CatName Method = 'Cmdlet' } # For PowerShell Cmdlet $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Controlled Folder Access Exclusions' Compliant = 'N/A' Value = [PSCustomObject]@{Count = $((Get-MpPreference).ControlledFolderAccessAllowedApplications.count); Programs = $((Get-MpPreference).ControlledFolderAccessAllowedApplications) } Name = 'Controlled Folder Access Exclusions' 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 #EndRegion Microsoft-Defender-Category #Region Attack-Surface-Reduction-Rules-Category Write-Progress -Activity 'Validating Attack Surface Reduction Rules Category' -Status 'Processing...' -PercentComplete 40 [System.Array]$NestedObjectArray = @() [String]$CatName = 'ASR' # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #EndRegion Attack-Surface-Reduction-Rules-Category #Region Bitlocker-Category Write-Progress -Activity 'Validating Bitlocker Category' -Status 'Processing...' -PercentComplete 45 [System.Array]$NestedObjectArray = @() [String]$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 [string]$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 [bool]$BootDMAProtection = ([SystemInfo.NativeMethods]::BootDmaCheck()) -ne 0 # Get the status of Bitlocker DMA protection [int]$BitlockerDMAProtectionStatus = Get-ItemPropertyValue -Path "Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\FVE" -Name "DisableExternalDMAUnderLock" -ErrorAction SilentlyContinue # 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 ($BitlockerDMAProtectionStatus -eq '1')) ? $True : $False # Create a custom object with 5 properties to store them as nested objects inside the main output object $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "DMA protection" Compliant = $ItemState Value = $ItemState Name = "DMA protection" Category = $CatName Method = 'Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # For PowerShell Cmdlet $IndividualItemResult = $($((Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Power -name HibernateEnabled -ErrorAction SilentlyContinue).hibernateEnabled) -eq 1 ? $True : $False) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Hibernate enabled and set to full' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Hibernate enabled and set to full' 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 #EndRegion Bitlocker-Category #Region TLS-Category Write-Progress -Activity 'Validating TLS Category' -Status 'Processing...' -PercentComplete 50 [System.Array]$NestedObjectArray = @() [String]$CatName = 'TLS' # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # ECC Curves [System.Array]$ECCCurves = Get-TlsEccCurve [System.Array]$list = ('nistP521', 'curve25519', 'NistP384', 'NistP256') # Make sure both arrays are completely identical in terms of members and their exact position # If this variable is empty that means both arrays are completely identical $IndividualItemResult = Compare-Object $ECCCurves $list -SyncWindow 0 $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'ECC Curves and their positions' Compliant = [bool]($IndividualItemResult ? $false : $True) Value = $list Name = 'ECC Curves and their positions' Category = $CatName Method = 'Cmdlet' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #EndRegion TLS-Category #Region LockScreen-Category Write-Progress -Activity 'Validating Lock Screen Category' -Status 'Processing...' -PercentComplete 55 [System.Array]$NestedObjectArray = @() [String]$CatName = 'LockScreen' $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\InactivityTimeoutSecs'] -eq '4,120') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Machine inactivity limit' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Machine inactivity limit' Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableCAD'] -eq '4,0') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Interactive logon: Do not require CTRL+ALT+DEL' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Interactive logon: Do not require CTRL+ALT+DEL' Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\MaxDevicePasswordFailedAttempts'] -eq '4,5') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Interactive logon: Machine account lockout threshold' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Interactive logon: Machine account lockout threshold' Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLockedUserId'] -eq '4,4') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Interactive logon: Display user information when the session is locked' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Interactive logon: Display user information when the session is locked' Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayUserName'] -eq '4,1') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Interactive logon: Don't display username at sign-in" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Interactive logon: Don't display username at sign-in" Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'System Access'['LockoutBadCount'] -eq '5') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Account lockout threshold" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Account lockout threshold" Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'System Access'['LockoutDuration'] -eq '1440') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Account lockout duration" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Account lockout duration" Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'System Access'['ResetLockoutCount'] -eq '1440') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Reset account lockout counter after" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Reset account lockout counter after" Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName'] -eq '4,1') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Interactive logon: Don't display last signed-in" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Interactive logon: Don't display last signed-in" 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 #EndRegion LockScreen-Category #Region User-Account-Control-Category Write-Progress -Activity 'Validating User Account Control Category' -Status 'Processing...' -PercentComplete 60 [System.Array]$NestedObjectArray = @() [String]$CatName = "UAC" $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin'] -eq '4,2') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "UAC: Behavior of the elevation prompt for administrators in Admin Approval Mode" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "UAC: Behavior of the elevation prompt for administrators in Admin Approval Mode" 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' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "UAC: Behavior of the elevation prompt for standard users" Compliant = $ConsentPromptBehaviorUserCompliance Value = $IndividualItemResult Name = "UAC: Behavior of the elevation prompt for standard users" Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]($($SecurityPoliciesIni.'Registry Values'['MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ValidateAdminCodeSignatures'] -eq '4,1') ? $True : $False) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'UAC: Only elevate executables that are signed and validated' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'UAC: Only elevate executables that are signed and validated' 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 #EndRegion User-Account-Control-Category #Region Device-Guard-Category Write-Progress -Activity 'Validating Device Guard Category' -Status 'Processing...' -PercentComplete 65 [System.Array]$NestedObjectArray = @() [String]$CatName = "Device Guard" $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #EndRegion Device-Guard-Category #Region Windows-Firewall-Category Write-Progress -Activity 'Validating Windows Firewall Category' -Status 'Processing...' -PercentComplete 70 [System.Array]$NestedObjectArray = @() [String]$CatName = 'Windows Firewall' $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #EndRegion Windows-Firewall-Category #Region Optional-Windows-Features-Category Write-Progress -Activity 'Validating Optional Windows Features Category' -Status 'Processing...' -PercentComplete 75 [System.Array]$NestedObjectArray = @() [String]$CatName = 'Optional Windows Features' # Disable PowerShell v2 (needs 2 commands) [bool]$IndividualItemResult = ((get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2).state -eq 'disabled') ` -and [bool]((get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root).state -eq 'disabled') ? $True : $false # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "PowerShell v2 is disabled" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "PowerShell v2 is disabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName WorkFolders-Client).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Work Folders client is disabled" Compliant = [bool]($IndividualItemResult -eq 'disabled') Value = $IndividualItemResult Name = "Work Folders client is disabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName Printing-Foundation-Features).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Internet Printing Client is disabled" Compliant = [bool]($IndividualItemResult -eq 'disabled') Value = $IndividualItemResult Name = "Internet Printing Client is disabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName WindowsMediaPlayer).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Windows Media Player (legacy) is disabled" Compliant = [bool]($IndividualItemResult -eq 'disabled') Value = $IndividualItemResult Name = "Windows Media Player (legacy) is disabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName Windows-Defender-ApplicationGuard).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Microsoft Defender Application Guard is enabled" Compliant = [bool]($IndividualItemResult -eq 'enabled') Value = $IndividualItemResult Name = "Microsoft Defender Application Guard is enabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Windows Sandbox is enabled" Compliant = [bool]($IndividualItemResult -eq 'enabled') Value = $IndividualItemResult Name = "Windows Sandbox is enabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Hyper-V is enabled" Compliant = [bool]($IndividualItemResult -eq 'enabled') Value = $IndividualItemResult Name = "Hyper-V is enabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Virtual Machine Platform is enabled" Compliant = [bool]($IndividualItemResult -eq 'enabled') Value = $IndividualItemResult Name = "Virtual Machine Platform is enabled" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (Get-WindowsCapability -Online | Where-Object { $_.Name -like '*wmic*' }).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "WMIC is not present" Compliant = [bool]($IndividualItemResult -eq 'NotPresent') Value = $IndividualItemResult Name = "WMIC is not present" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (Get-WindowsCapability -Online | Where-Object { $_.Name -like '*Browser.InternetExplorer*' }).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Internet Explorer mode functionality for Edge is not present" Compliant = [bool]($IndividualItemResult -eq 'NotPresent') Value = $IndividualItemResult Name = "Internet Explorer mode functionality for Edge is not present" Category = $CatName Method = 'Optional Windows Features' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array [string]$IndividualItemResult = (Get-WindowsCapability -Online | Where-Object { $_.Name -like '*Microsoft.Windows.Notepad.System*' }).state $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Legacy Notepad is not present" Compliant = [bool]($IndividualItemResult -eq 'NotPresent') Value = $IndividualItemResult Name = "Legacy Notepad is not present" 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 #EndRegion Optional-Windows-Features-Category #Region Windows-Networking-Category Write-Progress -Activity 'Validating Windows Networking Category' -Status 'Processing...' -PercentComplete 80 [System.Array]$NestedObjectArray = @() [String]$CatName = "Windows Networking" $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # Check network location of all connections to see if they are public $Condition = Get-NetConnectionProfile | ForEach-Object { $_.NetworkCategory -eq 'public' } [bool]$IndividualItemResult = -not ($condition -contains $false) ? $True : $false # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Network Location of all connections set to Public" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Network Location of all connections set to Public" Category = $CatName Method = 'Cmdlet' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]((Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Parameters" -Name "EnableLMHOSTS" -ErrorAction SilentlyContinue) -eq '0') $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Disable LMHOSTS lookup protocol on all network adapters" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Disable LMHOSTS lookup protocol on all network adapters" Category = $CatName Method = "Registry Key" } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine'] -eq '7,') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Network access: Remotely accessible registry paths' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Network access: Remotely accessible registry paths' Category = $CatName Method = 'Security Group Policy' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]$($SecurityPoliciesIni.'Registry Values'['MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine'] -eq '7,') ? $True : $False $NestedObjectArray += [PSCustomObject]@{ FriendlyName = 'Network access: Remotely accessible registry paths and subpaths' Compliant = $IndividualItemResult Value = $IndividualItemResult Name = 'Network access: Remotely accessible registry paths and subpaths' 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 #EndRegion Windows-Networking-Category #Region Miscellaneous-Category Write-Progress -Activity 'Validating Miscellaneous Category' -Status 'Processing...' -PercentComplete 85 [System.Array]$NestedObjectArray = @() [String]$CatName = "Miscellaneous" $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]((Get-SmbServerConfiguration).encryptdata) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "SMB Encryption" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "SMB Encryption" Category = $CatName Method = 'Cmdlet' } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool](((auditpol /get /subcategory:"Other Logon/Logoff Events" /r | ConvertFrom-Csv).'Inclusion Setting' -eq 'Success and Failure') ? $True : $False) $NestedObjectArray += [PSCustomObject]@{ FriendlyName = (Get-Culture).name -eq 'en-US' ? "Audit policy for Other Logon/Logoff Events" : "(Not accurate on non-English system languages) - Audit policy for Other Logon/Logoff Events" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = (Get-Culture).name -eq 'en-US' ? "Audit policy for Other Logon/Logoff Events" : "(Not accurate on non-English system languages) - Audit policy for Other Logon/Logoff Events" 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 | Where-Object { $_.Enabled -eq 'True' }).Name | Sort-Object # Get the members of the Hyper-V Administrators security group using their SID [string[]]$groupMembers = (Get-LocalGroupMember -SID 'S-1-5-32-578').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 [System.Array]$MatchHyperVUsers = @() # An array of bool values for ($i = 0; $i -lt $enabledUsers.Count; $i++) { $MatchHyperVUsers += ($enabledUsers[$i] -ceq $groupMembers[$i]) ? $True : $false } # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "All users are part of the Hyper-V Administrators group" Compliant = [bool]($MatchHyperVUsers -notcontains $false) Value = [bool]($MatchHyperVUsers -notcontains $false) Name = "All users are part of the Hyper-V Administrators group" Category = $CatName Method = 'Cmdlet' } $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #EndRegion Miscellaneous-Category #Region Windows-Update-Category Write-Progress -Activity 'Validating Windows Update Category' -Status 'Processing...' -PercentComplete 90 [System.Array]$NestedObjectArray = @() [String]$CatName = 'Windows Update' $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $CatName -Method "Group Policy") # Process the registry keys for this category based on the selected method and category name, then save the output Custom Object in the Array $IndividualItemResult = [bool]((Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -Name "RestartNotificationsAllowed2" -ErrorAction SilentlyContinue) -eq '1') $NestedObjectArray += [PSCustomObject]@{ FriendlyName = "Enable restart notification for Windows update" Compliant = $IndividualItemResult Value = $IndividualItemResult Name = "Enable restart notification for Windows update" 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 #EndRegion Windows-Update-Category #Region Edge-Category Write-Progress -Activity 'Validating Edge Browser Category' -Status 'Processing...' -PercentComplete 95 [System.Array]$NestedObjectArray = @() [String]$CatName = "Edge" $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #EndRegion Edge-Category #Region Non-Admin-Category Write-Progress -Activity 'Validating Non-Admin Category' -Status 'Processing...' -PercentComplete 100 [System.Array]$NestedObjectArray = @() [String]$CatName = 'Non-Admin' $NestedObjectArray += [PSCustomObject](Invoke-CategoryProcessing -catname $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 #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 | Out-File '.\Output.CSV' -Force } if ($ShowAsObjectsOnly) { # return the main object that contains multiple nested objects return $FinalMegaObject } else { #Region Colors [scriptblock]$WritePlum = { Write-Output "$($PSStyle.Foreground.FromRGB(221,160,221))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteOrchid = { Write-Output "$($PSStyle.Foreground.FromRGB(218,112,214))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteFuchsia = { Write-Output "$($PSStyle.Foreground.FromRGB(255,0,255))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteMediumOrchid = { Write-Output "$($PSStyle.Foreground.FromRGB(186,85,211))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteMediumPurple = { Write-Output "$($PSStyle.Foreground.FromRGB(147,112,219))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteBlueViolet = { Write-Output "$($PSStyle.Foreground.FromRGB(138,43,226))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteDarkViolet = { Write-Output "$($PSStyle.Foreground.FromRGB(148,0,211))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WritePink = { Write-Output "$($PSStyle.Foreground.FromRGB(255,192,203))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteHotPink = { Write-Output "$($PSStyle.Foreground.FromRGB(255,105,180))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteDeepPink = { Write-Output "$($PSStyle.Foreground.FromRGB(255,20,147))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteMintGreen = { Write-Output "$($PSStyle.Foreground.FromRGB(152,255,152))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteOrange = { Write-Output "$($PSStyle.Foreground.FromRGB(255,165,0))$($args[0])$($PSStyle.Reset)" } [scriptblock]$WriteSkyBlue = { Write-Output "$($PSStyle.Foreground.FromRGB(135,206,235))$($args[0])$($PSStyle.Reset)" } [scriptblock]$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 } [scriptblock]$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 * & $WriteOrchid "`n-------------Attack Surface Reduction Rules Category-------------" $FinalMegaObject.ASR | Format-list * & $WriteFuchsia "`n-------------Bitlocker Category-------------" $FinalMegaObject.Bitlocker | Format-list * & $WriteMediumOrchid "`n-------------TLS Category-------------" $FinalMegaObject.TLS | Format-list * & $WriteMediumPurple "`n-------------Lock Screen Category-------------" $FinalMegaObject.LockScreen | Format-list * & $WriteBlueViolet "`n-------------User Account Control Category-------------" $FinalMegaObject.UAC | Format-list * & $WriteDarkViolet "`n-------------Device Guard Category-------------" $FinalMegaObject.'Device Guard' | Format-list * & $WritePink "`n-------------Windows Firewall Category-------------" $FinalMegaObject.'Windows Firewall' | Format-list * & $WriteSkyBlue "`n-------------Optional Windows Features Category-------------" $FinalMegaObject.'Optional Windows Features' | Format-list * & $WriteHotPink "`n-------------Windows Networking Category-------------" $FinalMegaObject.'Windows Networking' | Format-list * & $WriteDeepPink "`n-------------Miscellaneous Category-------------" $FinalMegaObject.Miscellaneous | Format-list * & $WriteMintGreen "`n-------------Windows Update Category-------------" $FinalMegaObject.'Windows Update' | Format-list * & $WriteOrange "`n-------------Microsoft Edge Category-------------" $FinalMegaObject.Edge | Format-list * & $WriteSkyBlue "`n-------------Non-Admin Category-------------" $FinalMegaObject.'Non-Admin' | Format-list * } # Show properties that matter in a table else { & $WritePlum "`n-------------Microsoft Defender Category-------------" $FinalMegaObject.'Microsoft Defender' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteOrchid "`n-------------Attack Surface Reduction Rules Category-------------" $FinalMegaObject.ASR | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteFuchsia "`n-------------Bitlocker Category-------------" $FinalMegaObject.Bitlocker | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteMediumOrchid "`n-------------TLS Category-------------" $FinalMegaObject.TLS | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteMediumPurple "`n-------------Lock Screen Category-------------" $FinalMegaObject.LockScreen | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteBlueViolet "`n-------------User Account Control Category-------------" $FinalMegaObject.UAC | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteDarkViolet "`n-------------Device Guard Category-------------" $FinalMegaObject.'Device Guard' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WritePink "`n-------------Windows Firewall Category-------------" $FinalMegaObject.'Windows Firewall' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteSkyBlue "`n-------------Optional Windows Features Category-------------" $FinalMegaObject.'Optional Windows Features' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteHotPink "`n-------------Windows Networking Category-------------" $FinalMegaObject.'Windows Networking' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteDeepPink "`n-------------Miscellaneous Category-------------" $FinalMegaObject.Miscellaneous | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteMintGreen "`n-------------Windows Update Category-------------" $FinalMegaObject.'Windows Update' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteOrange "`n-------------Microsoft Edge Category-------------" $FinalMegaObject.Edge | Format-Table -Property FriendlyName, Compliant, Value -AutoSize & $WriteSkyBlue "`n-------------Non-Admin Category-------------" $FinalMegaObject.'Non-Admin' | Format-Table -Property FriendlyName, Compliant, Value -AutoSize } # Counting the number of $True Compliant values in the Final Output Object [int]$TotalTrueCompliantValuesInOutPut = ($FinalMegaObject.'Microsoft Defender' | Where-Object { $_.Compliant -eq $True }).Count + # 40 [int]($FinalMegaObject.ASR | Where-Object { $_.Compliant -eq $True }).Count + # 17 [int]($FinalMegaObject.Bitlocker | Where-Object { $_.Compliant -eq $True }).Count + # 22 [int]($FinalMegaObject.TLS | Where-Object { $_.Compliant -eq $True }).Count + # 21 [int]($FinalMegaObject.LockScreen | Where-Object { $_.Compliant -eq $True }).Count + # 16 [int]($FinalMegaObject.UAC | Where-Object { $_.Compliant -eq $True }).Count + # 4 [int]($FinalMegaObject.'Device Guard' | Where-Object { $_.Compliant -eq $True }).Count + # 8 [int]($FinalMegaObject.'Windows Firewall' | Where-Object { $_.Compliant -eq $True }).Count + # 19 [int]($FinalMegaObject.'Optional Windows Features' | Where-Object { $_.Compliant -eq $True }).Count + # 11 [int]($FinalMegaObject.'Windows Networking' | Where-Object { $_.Compliant -eq $True }).Count + # 9 [int]($FinalMegaObject.Miscellaneous | Where-Object { $_.Compliant -eq $True }).Count + # 21 [int]($FinalMegaObject.'Windows Update' | Where-Object { $_.Compliant -eq $True }).Count + # 14 [int]($FinalMegaObject.Edge | Where-Object { $_.Compliant -eq $True }).Count + # 16 [int]($FinalMegaObject.'Non-Admin' | Where-Object { $_.Compliant -eq $True }).Count # 11 #Region ASCII-Arts [string]$WhenValue1To20 = @" OH N O O o o o o 。 。 . . . . "@ [string]$WhenValue21To40 = @" ⣿⣿⣷⡁⢆⠈⠕⢕⢂⢕⢂⢕⢂⢔⢂⢕⢄⠂⣂⠂⠆⢂⢕⢂⢕⢂⢕⢂⢕⢂ ⣿⣿⣿⡷⠊⡢⡹⣦⡑⢂⢕⢂⢕⢂⢕⢂⠕⠔⠌⠝⠛⠶⠶⢶⣦⣄⢂⢕⢂⢕ ⣿⣿⠏⣠⣾⣦⡐⢌⢿⣷⣦⣅⡑⠕⠡⠐⢿⠿⣛⠟⠛⠛⠛⠛⠡⢷⡈⢂⢕⢂ ⠟⣡⣾⣿⣿⣿⣿⣦⣑⠝⢿⣿⣿⣿⣿⣿⡵⢁⣤⣶⣶⣿⢿⢿⢿⡟⢻⣤⢑⢂ ⣾⣿⣿⡿⢟⣛⣻⣿⣿⣿⣦⣬⣙⣻⣿⣿⣷⣿⣿⢟⢝⢕⢕⢕⢕⢽⣿⣿⣷⣔ ⣿⣿⠵⠚⠉⢀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣗⢕⢕⢕⢕⢕⢕⣽⣿⣿⣿⣿ ⢷⣂⣠⣴⣾⡿⡿⡻⡻⣿⣿⣴⣿⣿⣿⣿⣿⣿⣷⣵⣵⣵⣷⣿⣿⣿⣿⣿⣿⡿ ⢌⠻⣿⡿⡫⡪⡪⡪⡪⣺⣿⣿⣿⣿⣿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃ ⠣⡁⠹⡪⡪⡪⡪⣪⣾⣿⣿⣿⣿⠋⠐⢉⢍⢄⢌⠻⣿⣿⣿⣿⣿⣿⣿⣿⠏⠈ ⡣⡘⢄⠙⣾⣾⣾⣿⣿⣿⣿⣿⣿⡀⢐⢕⢕⢕⢕⢕⡘⣿⣿⣿⣿⣿⣿⠏⠠⠈ ⠌⢊⢂⢣⠹⣿⣿⣿⣿⣿⣿⣿⣿⣧⢐⢕⢕⢕⢕⢕⢅⣿⣿⣿⣿⡿⢋⢜⠠⠈ ⠄⠁⠕⢝⡢⠈⠻⣿⣿⣿⣿⣿⣿⣿⣷⣕⣑⣑⣑⣵⣿⣿⣿⡿⢋⢔⢕⣿⠠⠈ ⠨⡂⡀⢑⢕⡅⠂⠄⠉⠛⠻⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⢕⣿⣿⠠⠈ ⠄⠪⣂⠁⢕⠆⠄⠂⠄⠁⡀⠂⡀⠄⢈⠉⢍⢛⢛⢛⢋⢔⢕⢕⢕⣽⣿⣿⠠⠈ "@ [string]$WhenValue41To60 = @" ⣿⡟⠙⠛⠋⠩⠭⣉⡛⢛⠫⠭⠄⠒⠄⠄⠄⠈⠉⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⡇⠄⠄⠄⠄⣠⠖⠋⣀⡤⠄⠒⠄⠄⠄⠄⠄⠄⠄⠄⠄⣈⡭⠭⠄⠄⠄⠉⠙ ⣿⡇⠄⠄⢀⣞⣡⠴⠚⠁⠄⠄⢀⠠⠄⠄⠄⠄⠄⠄⠄⠉⠄⠄⠄⠄⠄⠄⠄⠄ ⣿⡇⠄⡴⠁⡜⣵⢗⢀⠄⢠⡔⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄ ⣿⡇⡜⠄⡜⠄⠄⠄⠉⣠⠋⠠⠄⢀⡄⠄⠄⣠⣆⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⢸ ⣿⠸⠄⡼⠄⠄⠄⠄⢰⠁⠄⠄⠄⠈⣀⣠⣬⣭⣛⠄⠁⠄⡄⠄⠄⠄⠄⠄⢀⣿ ⣏⠄⢀⠁⠄⠄⠄⠄⠇⢀⣠⣴⣶⣿⣿⣿⣿⣿⣿⡇⠄⠄⡇⠄⠄⠄⠄⢀⣾⣿ ⣿⣸⠈⠄⠄⠰⠾⠴⢾⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⢁⣾⢀⠁⠄⠄⠄⢠⢸⣿⣿ ⣿⣿⣆⠄⠆⠄⣦⣶⣦⣌⣿⣿⣿⣿⣷⣋⣀⣈⠙⠛⡛⠌⠄⠄⠄⠄⢸⢸⣿⣿ ⣿⣿⣿⠄⠄⠄⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠈⠄⠄⠄⠄⠄⠈⢸⣿⣿ ⣿⣿⣿⠄⠄⠄⠘⣿⣿⣿⡆⢀⣈⣉⢉⣿⣿⣯⣄⡄⠄⠄⠄⠄⠄⠄⠄⠈⣿⣿ ⣿⣿⡟⡜⠄⠄⠄⠄⠙⠿⣿⣧⣽⣍⣾⣿⠿⠛⠁⠄⠄⠄⠄⠄⠄⠄⠄⠃⢿⣿ ⣿⡿⠰⠄⠄⠄⠄⠄⠄⠄⠄⠈⠉⠩⠔⠒⠉⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠐⠘⣿ ⣿⠃⠃⠄⠄⠄⠄⠄⠄⣀⢀⠄⠄⡀⡀⢀⣤⣴⣤⣤⣀⣀⠄⠄⠄⠄⠄⠄⠁⢹ "@ [string]$WhenValue61To80 = @" ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⡿⠋⠈⠻⣮⣳⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⡿⠋⠀⠀⠀⠀⠙⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⡿⠟⠛⠉⠀⠀⠀⠀⠀⠀⠀⠈⠛⠛⠿⠿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⣾⡿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣶⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⣀⣠⣤⣤⣀⡀⠀⠀⣀⣴⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣄⠀⠀ ⢀⣤⣾⡿⠟⠛⠛⢿⣿⣶⣾⣿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠿⣿⣷⣦⣀⣀⣤⣶⣿⡿⠿⢿⣿⡀⠀ ⣿⣿⠏⠀⢰⡆⠀⠀⠉⢿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⢿⡿⠟⠋⠁⠀⠀⢸⣿⠇⠀ ⣿⡟⠀⣀⠈⣀⡀⠒⠃⠀⠙⣿⡆⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠇⠀ ⣿⡇⠀⠛⢠⡋⢙⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠀⠀ ⣿⣧⠀⠀⠀⠓⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠋⠀⠀⢸⣧⣤⣤⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⡿⠀⠀ ⣿⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠻⣷⣶⣶⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⠁⠀⠀ ⠈⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⡏⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⡄⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠻⠿⢿⣿⣷⣶⣦⣤⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⡄⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⣶⣤⣤⣀⡀⠀⠀⠀⢀⣴⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⡿⣄ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠿⠿⣿⣷⣶⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣹ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⠀⢸⣧ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣆⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣶⣾⣿⣿⣿⣿⣤⣄⣀⡀⠀⠀⠀⣿ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢿⣻⣷⣶⣾⣿⣿⡿⢯⣛⣛⡋⠁⠀⠀⠉⠙⠛⠛⠿⣿⣿⡷⣶⣿ "@ [string]$WhenValue81To88 = @" ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠔⠶⠒⠉⠈⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠪⣦⢄⣀⡠⠁⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣤⣤⣤⣤⣄⣀⣀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⣰⣶⣶⣦⠶⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠉⠉⢷⡔⠒⠚⢽⠃⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣰⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⢅⢰⣾⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⣀⡴⠞⠛⠉⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣧⠀⠀⠀⠀⠀ ⠀⣀⣀⣤⣤⡞⠋⠀⠀⠀⢠⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠀ ⢸⡏⠉⣴⠏⠀⠀⠀⠀⠀⢸⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀ ⠈⣧⢰⠏⠀⠀⠀⠀⠀⠀⢸⡆⠀⠀⠀⠀⠀⠀⠀⠀⠰⠯⠥⠠⠒⠄⠀⠀⠀⠀⠀⠀⢠⠀⣿⠀⠀⠀⠀ ⠀⠈⣿⠀⠀⠀⠀⠀⠀⠀⠈⡧⢀⢻⠿⠀⠲⡟⣞⠀⠀⠀⠀⠈⠀⠁⠀⠀⠀⠀⠀⢀⠆⣰⠇⠀⠀⠀⠀ ⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⣧⡀⠃⠀⠀⠀⠱⣼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣂⡴⠋⠀⣀⡀⠀⠀ ⠀⠀⢹⡄⠀⠀⠀⠀⠀⠀⠀⠹⣜⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠿⡻⢦⣄⣰⠏⣿⠀⠀ ⠀⠀⠀⢿⡢⡀⠀⠀⠀⠀⠀⠀⠙⠳⢮⣥⣤⣤⠶⠖⠒⠛⠓⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢌⢻⣴⠏⠀⠀ ⠀⠀⠀⠀⠻⣮⣒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣧⣤⣀⣀⣀⣤⡴⠖⠛⢻⡆⠀⠀⠀⠀⠀⠀⢣⢻⡄⠀⠀ ⠀⠀⠀⠀⠀⠀⠉⠛⠒⠶⠶⡶⢶⠛⠛⠁⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⠞⠁⠀⠀⠀⠀⠀⠀⠈⢜⢧⣄⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠃⠇⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠉⢻⠀⠀⠀⠀⠀⠀⠀⢀⣀⠀⠀⠉⠈⣷ ⠀⠀⠀⠀⠀⠀⠀⣼⠟⠷⣿⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠲⠶⢶⣶⠶⠶⢛⣻⠏⠙⠛⠛⠛⠁ ⠀⠀⠀⠀⠀⠀⠀⠈⠷⣤⣀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠉⠛⠓⠚⠋⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣟⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢹⡟⡟⢻⡟⠛⢻⡄⠀⠀⣸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠈⠷⠧⠾⠀⠀⠀⠻⣦⡴⠏⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠁⠀⠀⠀⠀⠈⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ "@ [string]$WhenValueAbove88 = @" ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⢠⣶⣶⣶⣦⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⠟⠛⢿⣶⡄⠀⢀⣀⣤⣤⣦⣤⡀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⢠⣿⠋⠀⠀⠈⠙⠻⢿⣶⣶⣶⣶⣶⣶⣶⣿⠟⠀⠀⠀⠀⠹⣿⡿⠟⠋⠉⠁⠈⢻⣷⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⣼⡧⠀⠀⠀⠀⠀⠀⠀⠉⠁⠀⠀⠀⠀⣾⡏⠀⠀⢠⣾⢶⣶⣽⣷⣄⡀⠀⠀⠀⠈⣿⡆⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⢸⣧⣾⠟⠉⠉⠙⢿⣿⠿⠿⠿⣿⣇⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⢸⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣷⣄⣀⣠⣼⣿⠀⠀⠀⠀⣸⣿⣦⡀⠀⠈⣿⡄⠀⠀⠀ ⠀⠀⠀⠀⠀⢠⣾⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠻⣷⣤⣤⣶⣿⣧⣿⠃⠀⣰⣿⠁⠀⠀⠀ ⠀⠀⠀⠀⠀⣾⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠹⣿⣀⠀⠀⣀⣴⣿⣧⠀⠀⠀⠀ ⠀⠀⠀⠀⢸⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⠿⠿⠛⠉⢸⣿⠀⠀⠀⠀ ⢀⣠⣤⣤⣼⣿⣤⣄⠀⠀⠀⡶⠟⠻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣶⣶⡄⠀⠀⠀⠀⢀⣀⣿⣄⣀⠀⠀ ⠀⠉⠉⠉⢹⣿⣩⣿⠿⠿⣶⡄⠀⠀⠀⠀⠀⠀⠀⢀⣤⠶⣤⡀⠀⠀⠀⠀⠀⠿⡿⠃⠀⠀⠀⠘⠛⠛⣿⠋⠉⠙⠃ ⠀⠀⠀⣤⣼⣿⣿⡇⠀⠀⠸⣿⠀⠀⠀⠀⠀⠀⠀⠘⠿⣤⡼⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⣼⣿⣀⠀⠀⠀ ⠀⠀⣾⡏⠀⠈⠙⢧⠀⠀⠀⢿⣧⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠟⠙⠛⠓⠀ ⠀⠀⠹⣷⡀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠻⣷⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⣶⣿⣯⡀⠀⠀⠀⠀ ⠀⠀⠀⠈⠻⣷⣄⠀⠀⠀⢀⣴⠿⠿⠗⠈⢻⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣾⠟⠋⠉⠛⠷⠄⠀⠀ ⠀⠀⠀⠀⠀⢸⡏⠀⠀⠀⢿⣇⠀⢀⣠⡄⢘⣿⣶⣶⣤⣤⣤⣤⣀⣤⣤⣤⣤⣶⣶⡿⠿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠘⣿⡄⠀⠀⠈⠛⠛⠛⠋⠁⣼⡟⠈⠻⣿⣿⣿⣿⡿⠛⠛⢿⣿⣿⣿⣡⣾⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠙⢿⣦⣄⣀⣀⣀⣀⣴⣾⣿⡁⠀⠀⠀⡉⣉⠁⠀⠀⣠⣾⠟⠉⠉⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⠛⠛⠛⠉⠀⠹⣿⣶⣤⣤⣷⣿⣧⣴⣾⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠻⢦⣭⡽⣯⣡⡴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ "@ #Endregion ASCII-Arts # Total number of Compliant values not equal to N/A [int]$TotalNumberOfTrueCompliantValues = 229 switch ($True) { ($TotalTrueCompliantValuesInOutPut -in 1..40) { & $WriteRainbow2 "$WhenValue1To20`nYour compliance score is $TotalTrueCompliantValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" } ($TotalTrueCompliantValuesInOutPut -in 41..80) { & $WriteRainbow1 "$WhenValue21To40`nYour compliance score is $TotalTrueCompliantValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" } ($TotalTrueCompliantValuesInOutPut -in 81..100) { & $WriteRainbow1 "$WhenValue41To60`nYour compliance score is $TotalTrueCompliantValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" } ($TotalTrueCompliantValuesInOutPut -in 101..140) { & $WriteRainbow2 "$WhenValue61To80`nYour compliance score is $TotalTrueCompliantValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" } ($TotalTrueCompliantValuesInOutPut -in 141..180) { & $WriteRainbow1 "$WhenValue81To88`nYour compliance score is $TotalTrueCompliantValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" } ($TotalTrueCompliantValuesInOutPut -gt 180) { & $WriteRainbow2 "$WhenValueAbove88`nYour compliance score is $TotalTrueCompliantValuesInOutPut out of $TotalNumberOfTrueCompliantValues!" } } } } # End of Process Block end { # Clean up Remove-Item -Path '.\security_policy.inf' -Force Remove-Item -Path '.\Registry resources.csv' -Force } <# .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 |