Private/Checks/Esx/Test-VcfEsxExecInstalledOnlyCheck.ps1

# Copyright (c) 2026 Broadcom. All Rights Reserved.
# Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
# and/or its subsidiaries.
#
# =============================================================================
#
# SOFTWARE LICENSE AGREEMENT
#
# Copyright (c) CA, Inc. All rights reserved.
#
# You are hereby granted a non-exclusive, worldwide, royalty-free license
# under CA, Inc.'s copyrights to use, copy, modify, and distribute this
# software in source code or binary form for use in connection with CA, Inc.
# products.
#
# This copyright notice shall be included in all copies or substantial
# portions of the software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# =============================================================================
function Test-VcfEsxExecInstalledOnlyCheck {

    <#
        .SYNOPSIS
        Reports VMkernel.Boot.execInstalledOnly status for every ESX host managed by every
        vCenter attached to SDDC Manager.

        .DESCRIPTION
        Uses Get-VcfCheckHostAdvancedSettingForHost to query the 'VMkernel.Boot.execInstalledOnly'
        setting for each ESX host attached to each vCenter domain, one host at a time (PowerCLI's
        Get-AdvancedSetting makes one API round trip per host, so this can take a while on
        environments with many hosts - Write-VcfCheckSubProgress reports "Current/Total (hostname)"
        progress between hosts). This standard ESX security-hardening setting (part of the vSphere
        Security Configuration Guide) enforces that only digitally-signed, installed executables can run.

        Informational only: reports each host's execInstalledOnly state ('Enabled' or 'Disabled')
        in a per-vCenter table containing Cluster, Hostname, and Status. The check yields a
        'Pass' status regardless of whether individual hosts have the setting disabled. It returns
        an 'Error' status only if an execution failure occurs (e.g., unable to retrieve vCenters,
        connect to a vCenter, or fetch advanced settings).

        Iterates through all vCenters managed by SDDC Manager and delegates output generation
        to New-VcfCheckPerDomainResults to return results grouped per vCenter domain.

        .PARAMETER Context
        The VcfCheck.Context object. Must already be connected to SDDC Manager.

        .PARAMETER DisplayName
        Optional friendly name for the check, used when generating top-level error results.

        .OUTPUTS
        [PSObject[]] Per-domain check results produced by New-VcfCheckPerDomainResults.
    #>


    [CmdletBinding()]
    [OutputType([PSObject[]])]
    Param (
        [Parameter(Mandatory = $true)] [PSObject]$Context,
        [Parameter(Mandatory = $false)] [String]$DisplayName = ''
    )

    $startedAt = Get-Date
    $checkId = 'esx_execInstalledOnly_check'
    $catalogEntry = (Get-VcfCheckCatalog)[$checkId]
    $displayName = if ([String]::IsNullOrEmpty($DisplayName)) { $catalogEntry.displayName } else { $DisplayName }

    try {
        $vcenterFqdns = Get-VcfCheckAllVCenterFqdns -Context $Context
    } catch {
        return New-VcfCheckResult -CheckId $checkId -Status Error `
            -Exception $_.Exception.Message -StartedAt $startedAt -CompletedAt (Get-Date) -DisplayName $displayName
    }

    $outcomes = foreach ($vcenterFqdn in $vcenterFqdns) {
        $iterationStartedAt = Get-Date
        $outcome = try {
            Connect-VcfCheckVCenter -Context $Context -Fqdn $vcenterFqdn
            $vmHosts = @(Get-VcfCheckVMHostInventory -Server $vcenterFqdn)
            $hostIndex = 0
            $settings = @($vmHosts | ForEach-Object {
                $hostIndex++
                Write-VcfCheckSubProgress -Context $Context -Current $hostIndex -Total $vmHosts.Count -Label $_.Name
                Get-VcfCheckHostAdvancedSettingForHost -VMHost $_ -SettingName 'VMkernel.Boot.execInstalledOnly'
            })

            $hostsByCluster = @($settings | ForEach-Object {
                [PSCustomObject]@{
                    Cluster  = $_.Entity.Parent.Name
                    Hostname = $_.Entity.Name
                    Status   = if ([Boolean]$_.Value) { 'Enabled' } else { 'Disabled' }
                }
            } | Group-Object -Property Cluster)

            $rows = @()
            foreach ($clusterGroup in $hostsByCluster) {
                $clusterName = $clusterGroup.Name
                $hostsInCluster = @($clusterGroup.Group)
                $uniqueStatuses = @($hostsInCluster.Status | Select-Object -Unique)

                if ($uniqueStatuses.Count -eq 1) {
                    $commonStatus = $uniqueStatuses[0]
                    $hostCount = $hostsInCluster.Count
                    $rows += [PSCustomObject]@{
                        Cluster  = $clusterName
                        Hostname = "All $hostCount host$(if ($hostCount -ne 1) { 's' }) in the cluster have the identical state"
                        Status   = $commonStatus
                    }
                } else {
                    $rows += @($hostsInCluster | Sort-Object -Property Hostname)
                }
            }
            $rows = @($rows | Sort-Object -Property Cluster)

            [PSCustomObject]@{
                VCenterFqdn = $vcenterFqdn
                Status      = 'Pass'
                Detail      = $null
                Rows        = $rows
            }
        } catch {
            [PSCustomObject]@{
                VCenterFqdn = $vcenterFqdn
                Status      = 'Error'
                Detail      = $_.Exception.Message
                Rows        = @()
            }
        }
        $outcome | Add-Member -NotePropertyName StartedAt -NotePropertyValue $iterationStartedAt -Force
        $outcome | Add-Member -NotePropertyName CompletedAt -NotePropertyValue (Get-Date) -Force
        $outcome
    }

    return New-VcfCheckPerDomainResults -Context $Context -PerVCenterOutcome $outcomes -CheckId $checkId `
        -StartedAt $startedAt
}