Public/06_Apps_Features/Get-VBWindowsFeaturesInfo.ps1

# ============================================================
# FUNCTION : Get-VBWindowsFeaturesInfo
# VERSION : 1.0.2
# CHANGED : 10-04-2026 -- Initial VB-compliant release
# AUTHOR : Vibhu Bhatnagar
# PURPOSE : Query Windows Features, Roles, and Role Services status
# ENCODING : UTF-8 with BOM
# ============================================================

<#
.SYNOPSIS
    Retrieves Windows Features, Roles, and Role Services from local or remote computers.
 
.DESCRIPTION
    Enumerates installed Windows Features on Server SKUs (or Windows Optional
    Features on client SKUs). Returns one PSCustomObject per feature with name,
    display name, installation state, and feature type. Attempts Get-WindowsFeature
    (Server) first, then falls back to Get-WindowsOptionalFeature (client).
    Supports pipeline input and remote execution via WinRM.
 
.PARAMETER ComputerName
    Target computer(s) to query. Accepts pipeline input. Defaults to local
    machine ($env:COMPUTERNAME). Aliases: Name, Server, Host.
 
.PARAMETER Credential
    PSCredential object for remote execution. Required for cross-domain or
    credential-based remote queries.
 
.EXAMPLE
    Get-VBWindowsFeaturesInfo
 
.EXAMPLE
    Get-VBWindowsFeaturesInfo -ComputerName SERVER01
 
.EXAMPLE
    'SRV01','SRV02' | Get-VBWindowsFeaturesInfo -Credential (Get-Credential)
 
.OUTPUTS
    [PSCustomObject]: ComputerName, Name, DisplayName, InstallState, FeatureType,
    Status, CollectionTime
 
.NOTES
    Version : 1.0.2
    Author : Vibhu Bhatnagar
    Modified : 10-04-2026
    Category : Features
 
#>


function Get-VBWindowsFeaturesInfo {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Name', 'Server', 'Host')]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [PSCredential]$Credential
    )

    process {
        foreach ($computer in $ComputerName) {
            try {
                # Step 1 -- Define script block to enumerate features
                $scriptBlock = {
                    $Features = $null

                    # Step 2 -- Try Get-WindowsFeature for Server SKUs
                    if (Get-Command -Name Get-WindowsFeature -ErrorAction SilentlyContinue) {
                        $Features = Get-WindowsFeature -ErrorAction SilentlyContinue |
                            Where-Object { $_.InstallState -eq 'Installed' }
                    }
                    # Step 3 -- Fall back to Get-WindowsOptionalFeature for client SKUs
                    elseif (Get-Command -Name Get-WindowsOptionalFeature -ErrorAction SilentlyContinue) {
                        $Features = Get-WindowsOptionalFeature -Online -ErrorAction SilentlyContinue |
                            Where-Object { $_.State -eq 'Enabled' } |
                            Select-Object -Property @{
                                Name       = 'Name'
                                Expression = { $_.FeatureName }
                            },
                            @{
                                Name       = 'DisplayName'
                                Expression = { $_.FeatureName }
                            },
                            @{
                                Name       = 'InstallState'
                                Expression = { 'Installed' }
                            },
                            @{
                                Name       = 'FeatureType'
                                Expression = { 'Feature' }
                            }
                    }

                    # Step 4 -- Return results if found
                    if ($Features) {
                        $Features | Sort-Object Name
                    }
                }

                # Step 5 -- Execute locally or remotely
                if ($computer -eq $env:COMPUTERNAME) {
                    $results = & $scriptBlock
                } else {
                    $invokeParams = @{
                        ComputerName = $computer
                        ScriptBlock  = $scriptBlock
                    }
                    if ($PSBoundParameters.ContainsKey('Credential')) {
                        $invokeParams['Credential'] = $Credential
                    }
                    $results = Invoke-Command @invokeParams
                }

                # Step 6 -- Emit one PSCustomObject per feature
                foreach ($item in $results) {
                    [PSCustomObject]@{
                        ComputerName   = $computer
                        Name           = $item.Name
                        DisplayName    = $item.DisplayName
                        InstallState   = $item.InstallState
                        FeatureType    = $item.FeatureType
                        Status         = 'Success'
                        CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss')
                    }
                }
            }
            catch {
                # Step 7 -- Return error object on failure
                [PSCustomObject]@{
                    ComputerName   = $computer
                    Name           = $null
                    DisplayName    = $null
                    InstallState   = $null
                    FeatureType    = $null
                    Status         = 'Failed'
                    Error          = $_.Exception.Message
                }
            }
        }
    }
}