ADWinHostFirewallExport.psm1

#Region '.\prefix.ps1' -1

# Ensure C# class is loaded before module functions
if (-not ("ResourceStringResolver" -as [type])) {
    $csFilePath = Join-Path $PSScriptRoot "Types\ResourceStringResolver.cs"

    if (Test-Path $csFilePath) {
        Write-Verbose "Compiling and loading C# class from: $csFilePath"
        Add-Type -Path $csFilePath -ErrorAction Stop
    }
    else {
        Write-Warning "C# file not found: $csFilePath. Resource string resolution may not work."
    }
}
#EndRegion '.\prefix.ps1' 13
#Region '.\Classes\FirewallRule.ps1' -1

class FirewallRule {
    [string]$ComputerName
    [string]$Group
    [string]$DisplayName
    [string]$FWProfile
    [bool]$IsDomain
    [bool]$IsPrivate
    [bool]$IsPublic
    [bool]$IsAny
    [string]$Action
    [bool]$Enabled
    [string]$Direction
    [string]$Protocol
    [string]$LocalPort
    [string]$LocalAddress
    [string]$RemoteAddress
    [string]$Program

    FirewallRule([string]$computerName, [string]$group, [string]$displayName,
        [string]$FWProfile, [bool]$isDomain, [bool]$isPrivate, [bool]$isPublic,
        [bool]$isAny, [string]$action, [bool]$enabled, [string]$direction,
        [string]$protocol, [string]$localPort, [string]$localAddress, [string]$remoteAddress, [string]$program) {

        $this.ComputerName = $computerName
        $this.Group = $group
        $this.DisplayName = $displayName
        $this.FWProfile = $FWProfile
        $this.IsDomain = $isDomain
        $this.IsPrivate = $isPrivate
        $this.IsPublic = $isPublic
        $this.IsAny = $isAny
        $this.Action = $action
        $this.Enabled = $enabled
        $this.Direction = $direction
        $this.Protocol = $protocol
        $this.LocalPort = $localPort
        $this.LocalAddress = $localAddress
        $this.RemoteAddress = $remoteAddress
        $this.Program = $program
    }
}
#EndRegion '.\Classes\FirewallRule.ps1' 42
#Region '.\Private\Get-ADHostFirewallStatus.ps1' -1

<#
    .SYNOPSIS
        Retrieves the firewall status of a local or remote machine.
    .DESCRIPTION
        This function retrieves the firewall status for either a local or remote machine.
        It gathers firewall profile details including domain, private, and public profiles.
    .PARAMETER ComputerName
        The name(s) of the remote computer(s) to retrieve firewall status from.
    .PARAMETER Local
        Use this switch to retrieve firewall status from the local machine.
    .EXAMPLE
        Get-ADHostFirewallStatus -ComputerName "Server01"
        Retrieves firewall status from the remote machine "Server01".
    .EXAMPLE
        Get-ADHostFirewallStatus -Local
        Retrieves firewall status from the local machine.
    .OUTPUTS
        PSCustomObject. Each object contains firewall profile settings.
    .NOTES
        Requires administrative privileges to execute.
#>

function Get-ADHostFirewallStatus {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        DefaultParameterSetName = 'Remote',
        ConfirmImpact = 'Low'
    )]
    [OutputType([PSCustomObject])]
    param (
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'Remote'
        )]
        [string[]]$ComputerName,
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Local'
        )]
        [switch]$Local
    )
    begin {
        Write-TimestampedMessage 'Initializing Get-ADHostFirewallStatus function.'
        Write-Progress -Activity 'Initializing' -Status 'Preparing to query firewall status...' -PercentComplete 0
        $results = New-Object System.Collections.Generic.List[Object]
        $totalComputers = $ComputerName.Count
        $count = 0
    }
    process {
        try {
            if ($PSCmdlet.ShouldProcess('Firewall Status', 'Retrieve firewall information')) {
                switch ($PSCmdlet.ParameterSetName) {
                    'Remote' {
                        Write-TimestampedMessage "Retrieving firewall status for remote machines: $($ComputerName -join ', ')"
                        foreach ($server in $ComputerName) {
                            $count++
                            $percentComplete = [math]::Round(($count / $totalComputers) * 100)
                            Write-Progress -Activity 'Retrieving Firewall Status' -Status "Processing $server ($count of $totalComputers)" -PercentComplete $percentComplete
                            Write-TimestampedMessage "Processing machine: $server"
                            try {
                                $result = Invoke-Command -ComputerName $server -ScriptBlock {
                                    $profiles = Get-NetFirewallProfile | Group-Object -Property Name -AsHashTable -AsString
                                    [PSCustomObject]@{
                                        ComputerName                = $env:COMPUTERNAME
                                        Domain_Enabled              = $profiles.Domain.Enabled
                                        Domain_LogFileName          = $profiles.Domain.LogFileName
                                        Domain_LogMaxSizeKilobytes  = $profiles.Domain.LogMaxSizeKilobytes
                                        Domain_LogAllowed           = $profiles.Domain.LogAllowed
                                        Domain_LogBlocked           = $profiles.Domain.LogBlocked
                                        # Private
                                        Private_Enabled             = $profiles.Private.Enabled
                                        Private_LogFileName         = $profiles.Private.LogFileName
                                        Private_LogMaxSizeKilobytes = $profiles.Private.LogMaxSizeKilobytes
                                        Private_LogAllowed          = $profiles.Private.LogAllowed
                                        Private_LogBlocked          = $profiles.Private.LogBlocked
                                        # Public
                                        Public_Enabled              = $profiles.Public.Enabled
                                        Public_LogFileName          = $profiles.Public.LogFileName
                                        Public_LogMaxSizeKilobytes  = $profiles.Public.LogMaxSizeKilobytes
                                        Public_LogAllowed           = $profiles.Public.LogAllowed
                                        Public_LogBlocked           = $profiles.Public.LogBlocked
                                    }
                                } -ErrorAction Stop
                                $cleanResult = $result | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId
                                $results.Add($cleanResult) | Out-Null
                            }
                            catch {
                                throw "Error retrieving firewall status from $server`: $($_.Exception.Message)"
                            }
                        }
                    }
                    'Local' {
                        Write-TimestampedMessage 'Retrieving firewall status for the local machine.'
                        try {
                            Write-Progress -Activity 'Retrieving Firewall Status' -Status 'Processing Local Machine' -PercentComplete 100
                            $profiles = Get-NetFirewallProfile | Group-Object -Property Name -AsHashTable -AsString
                            $localResult = [PSCustomObject]@{
                                ComputerName                = $env:COMPUTERNAME
                                Domain_Enabled              = $profiles.Domain.Enabled
                                Domain_LogFileName          = $profiles.Domain.LogFileName
                                Domain_LogMaxSizeKilobytes  = $profiles.Domain.LogMaxSizeKilobytes
                                Domain_LogAllowed           = $profiles.Domain.LogAllowed
                                Domain_LogBlocked           = $profiles.Domain.LogBlocked
                                # Private
                                Private_Enabled             = $profiles.Private.Enabled
                                Private_LogFileName         = $profiles.Private.LogFileName
                                Private_LogMaxSizeKilobytes = $profiles.Private.LogMaxSizeKilobytes
                                Private_LogAllowed          = $profiles.Private.LogAllowed
                                Private_LogBlocked          = $profiles.Private.LogBlocked
                                # Public
                                Public_Enabled              = $profiles.Public.Enabled
                                Public_LogFileName          = $profiles.Public.LogFileName
                                Public_LogMaxSizeKilobytes  = $profiles.Public.LogMaxSizeKilobytes
                                Public_LogAllowed           = $profiles.Public.LogAllowed
                                Public_LogBlocked           = $profiles.Public.LogBlocked
                            }
                            $results.Add($localResult) | Out-Null
                        }
                        catch {
                            throw "Error retrieving local firewall status: $($_.Exception.Message)"
                        }
                    }
                }
            }
            else {
                throw 'Operation canceled by ShouldProcess check.'
            }
        }
        catch {
            throw "Unexpected error: $($_.Exception.Message)"
        }
    }
    end {
        Write-Progress -Activity 'Retrieving Firewall Status' -Status 'Completed processing all hosts.' -Completed
        return $results
    }
}
#EndRegion '.\Private\Get-ADHostFirewallStatus.ps1' 138
#Region '.\Private\Resolve-FirewallResourceString.ps1' -1

<#
    .SYNOPSIS
        Resolves a firewall resource string to its actual value.
    .DESCRIPTION
        This function takes a firewall resource string (e.g., "@C:\Windows\System32\firewallapi.dll,-28546")
        and resolves it to a human-readable string using the ResourceStringResolver class.
    .PARAMETER ResourceString
        The resource string to resolve. Can be null or empty.
    .EXAMPLE
        Resolve-FirewallResourceString -ResourceString "@C:\Windows\System32\firewallapi.dll,-28546"
        Returns the resolved string.
    .OUTPUTS
        [string] - The resolved firewall resource string.
    .NOTES
        This function relies on the ResourceStringResolver C# class.
#>

function Resolve-FirewallResourceString {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [AllowEmptyString()]
        [AllowNull()]
        [string]$ResourceString
    )
    try {
        # If input is null or empty, return it as-is
        if ([string]::IsNullOrWhiteSpace($ResourceString)) {
            Write-TimestampedMessage "Resource string is null or empty. Returning as-is."
            return $ResourceString
        }
        Write-TimestampedMessage "Resolving firewall resource string: $ResourceString"
        # Call the C# method to resolve
        $resolvedString = [ResourceStringResolver]::GetString($ResourceString)
        Write-TimestampedMessage "Resolved string: $resolvedString"
        return $resolvedString
    }
    catch {
        Write-Warning "Failed to resolve resource string: $ResourceString. Returning input value."
        return $ResourceString
    }
}
#EndRegion '.\Private\Resolve-FirewallResourceString.ps1' 43
#Region '.\Private\Write-TimeStampedMessage.ps1' -1

function Write-TimestampedMessage {
    param(
        [Parameter(Mandatory)]
        [string]$Message,
        [Parameter()]
        [ValidateSet("Verbose", "Warning", "Error", "Info", "Debug")]
        [string]$Type = "Verbose"
    )
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.ffff"
    switch ($Type) {
        "Verbose" { Write-Verbose "[$timestamp] $Message" }
        "Warning" { Write-Warning "[$timestamp] $Message" }
        "Error"   { Write-Error "[$timestamp] $Message" }
        "Info"    { Write-Information "[$timestamp] $Message" }
        "Debug"   { Write-Debug "[$timestamp] $Message" }
        default   { Write-Host "[$timestamp] $Message" }
    }
}
#EndRegion '.\Private\Write-TimeStampedMessage.ps1' 19
#Region '.\Public\Get-ADHostFirewallRulesExport.ps1' -1

<#
    .SYNOPSIS
        Retrieves firewall rules from remote machines with filtering options.
    .DESCRIPTION
        This function collects firewall rules from the specified remote computers.
        It allows filtering based on rule status (Enabled, Disabled, or All)
        and direction (Inbound, Outbound, or All).
        Additionally, it resolves rule group names, includes protocol and port information,
        and retrieves associated program paths, local addresses, and remote addresses.
    .PARAMETER ComputerName
        Specifies one or more remote computers from which to retrieve firewall rules.
    .PARAMETER RuleStatus
        Specifies which firewall rules to retrieve based on their enabled status.
        Options:
        - "All" : Retrieves all firewall rules (enabled and disabled).
        - "Enabled" : Retrieves only enabled firewall rules (Default).
        - "Disabled" : Retrieves only disabled firewall rules.
    .PARAMETER Direction
        Specifies the direction of firewall rules to retrieve.
        Options:
        - "All" : Retrieves both inbound and outbound rules.
        - "Inbound" : Retrieves only inbound firewall rules (Default).
        - "Outbound" : Retrieves only outbound firewall rules.
    .EXAMPLE
        Get-ADHostFirewallRulesExport -ComputerName "Server01"
        Retrieves only enabled inbound firewall rules from "Server01" (default behavior).
    .EXAMPLE
        Get-ADHostFirewallRulesExport -ComputerName "Server01" -RuleStatus All -Direction Outbound
        Retrieves all outbound firewall rules from "Server01".
    .EXAMPLE
        Get-ADHostFirewallRulesExport -ComputerName "Server01", "Server02" -RuleStatus Disabled
        Retrieves only disabled inbound firewall rules from both "Server01" and "Server02".
    .EXAMPLE
        "Server01", "Server02" | Get-ADHostFirewallRulesExport -Direction All
        Retrieves both inbound and outbound enabled firewall rules from "Server01" and "Server02".
    .OUTPUTS
        [FirewallRule] (Custom Class).
        Each object contains details about a firewall rule, including:
 
        - ComputerName : The remote host where the firewall rule is applied.
        - Group : The resolved name of the firewall rule group.
        - DisplayName : The display name of the firewall rule.
        - FWProfile : The firewall profile(s) associated with the rule.
        - IsDomain : Indicates whether the rule applies to the domain profile.
        - IsPrivate : Indicates whether the rule applies to the private profile.
        - IsPublic : Indicates whether the rule applies to the public profile.
        - IsAny : Indicates if the rule applies to all profiles.
        - Action : Whether the rule allows or blocks traffic.
        - Enabled : Indicates whether the rule is enabled.
        - Direction : Whether the rule applies to inbound or outbound traffic.
        - Protocol : The protocol associated with the firewall rule.
        - LocalPort : The local port(s) affected by the rule.
        - LocalAddress : The local IP addresses affected by the rule.
        - RemoteAddress : The remote IP addresses affected by the rule.
        - Program : The executable associated with the firewall rule (if applicable).
    .NOTES
        - Requires administrative privileges on remote machines.
        - Uses Invoke-Command to query firewall rules remotely.
        - Resolves localized firewall rule group names where applicable.
        - Optimized to retrieve address filters and application filters in batch.
        - Uses lookup tables to efficiently map rule instance IDs to addresses and programs.
    .COMPONENT
        Windows Defender Firewall with Advanced Security (NetSecurity module)
    .LINK
        https://docs.microsoft.com/en-us/powershell/module/netsecurity/get-netfirewallrule
    .LINK
        https://docs.microsoft.com/en-us/powershell/module/netsecurity/get-netfirewalladdressfilter
    .LINK
        https://docs.microsoft.com/en-us/powershell/module/netsecurity/get-netfirewallapplicationfilter
#>


function Get-ADHostFirewallRulesExport {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([FirewallRule])]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName,
        [Parameter()]
        [ValidateSet('All', 'Enabled', 'Disabled')]
        [string]$RuleStatus = 'Enabled',
        [Parameter()]
        [ValidateSet('All', 'Inbound', 'Outbound')]
        [string]$Direction = 'Inbound'
    )
    begin {
        Write-TimestampedMessage 'Initializing Get-ADHostFirewallRulesExport function.'
        $results = [System.Collections.Generic.List[FirewallRule]]::new()
        # Load C# class for resolving resource strings
        #$resolverDefinition = Get-Content -Path (Join-Path $PSScriptRoot 'Types\ResourceStringResolver.cs') -Raw
        $resolverDefinition = [ResourceStringResolver]::SourceCode
    }
    process {
        $total = $ComputerName.Count
        $count = 0
        foreach ($server in $ComputerName) {
            $count++
            Write-Progress -Activity 'Retrieving Firewall Rules' `
                -Status "Processing: $server ($count of $total)" `
                -PercentComplete (($count / $total) * 100)
            if ($PSCmdlet.ShouldProcess($server, 'Retrieve firewall rules')) {
                Write-TimestampedMessage "Processing firewall rules for: $server"
                try {
                    # Invoke command with the resolver type definition
                    $remoteData = Invoke-Command -ComputerName $server -ScriptBlock {
                        param($RuleStatus, $Direction, $resolverDefinition)
                        # Ensure NetSecurity is imported
                        Import-Module NetSecurity -ErrorAction Continue
                        # Load ResourceStringResolver if not already defined
                        if (-not ("ResourceStringResolver" -as [type])) {
                            Add-Type -TypeDefinition $resolverDefinition -Language CSharp
                        }
                        # Convert RuleStatus into string values for filtering
                        $enabledValue = switch ($RuleStatus) {
                            'Enabled' { $true }
                            'Disabled' { $false }
                            default { $null }   # "All"
                        }
                        # Retrieve all firewall rules once
                        $firewallRules = Get-NetFirewallRule
                        if ($null -ne $enabledValue) {
                            $firewallRules = $firewallRules | Where-Object { $_.Enabled -eq $enabledValue }
                        }
                        if ($Direction -ne 'All') {
                            $firewallRules = $firewallRules | Where-Object { $_.Direction -eq $Direction }
                        }
                        # Retrieve all address filters once
                        $addressFilters = Get-NetFirewallAddressFilter -All
                        # Retrieve all application filters once
                        $applicationFilters = Get-NetFirewallApplicationFilter -All
                        # Create lookup tables
                        $addressLookup = @{}
                        foreach ($filter in $addressFilters) {
                            $addressLookup[$filter.InstanceID] = @{
                                LocalAddress  = $filter.LocalAddress
                                RemoteAddress = $filter.RemoteAddress
                            }
                        }
                        $appLookup = @{}
                        foreach ($appFilter in $applicationFilters) {
                            $appLookup[$appFilter.InstanceID] = $appFilter.Program
                        }
                        $resolvedRules = @()
                        foreach ($rule in $firewallRules) {
                            # For port/protocol info
                            $portFilter = $rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue
                            # Resolve firewall rule group name remotely
                            $resolvedGroup = [ResourceStringResolver]::GetString($rule.Group)
                            # Convert FWProfile array to a comma-separated string
                            $profileList = $rule.Profile -join ', '
                            # Handle profile conditions correctly
                            $isDomain  = $rule.Profile -contains 'Domain'
                            $isPrivate = $rule.Profile -contains 'Private'
                            $isPublic  = $rule.Profile -contains 'Public'
                            # If 'Any' is in the profile list, assume all profiles apply
                            if ($rule.Profile -contains 'Any') {
                                $isDomain  = $true
                                $isPrivate = $true
                                $isPublic  = $true
                            }
                            $isAny = $isDomain -and $isPrivate -and $isPublic
                            # Get the associated addresses
                            $localAddress  = 'Any'
                            $remoteAddress = 'Any'
                            if ($addressLookup.ContainsKey($rule.InstanceID)) {
                                $localAddress  = $addressLookup[$rule.InstanceID].LocalAddress
                                $remoteAddress = $addressLookup[$rule.InstanceID].RemoteAddress
                            }
                            # Get the associated program
                            $program = 'N/A'
                            if ($appLookup.ContainsKey($rule.InstanceID)) {
                                $program = $appLookup[$rule.InstanceID]
                            }
                            # Build the resolved rule object
                            $resolvedRules += [PSCustomObject]@{
                                Group        = $resolvedGroup
                                DisplayName  = $rule.DisplayName
                                FWProfile    = $profileList
                                IsDomain     = $isDomain
                                IsPrivate    = $isPrivate
                                IsPublic     = $isPublic
                                IsAny        = $isAny
                                Action       = $rule.Action
                                Enabled      = $rule.Enabled
                                Direction    = $rule.Direction
                                Protocol     = $portFilter.Protocol
                                LocalPort    = $portFilter.LocalPort
                                LocalAddress = $localAddress
                                RemoteAddress = $remoteAddress
                                Program      = $program
                            }
                        }
                        return $resolvedRules
                    } -ArgumentList $RuleStatus, $Direction, $resolverDefinition -ErrorAction Stop
                    # Convert the PSCustomObjects from remote to FirewallRule objects locally
                    foreach ($ruleObj in $remoteData) {
                        $firewallRule = [FirewallRule]::new(
                            $server,
                            $ruleObj.Group,
                            $ruleObj.DisplayName,
                            $ruleObj.FWProfile,
                            $ruleObj.IsDomain,
                            $ruleObj.IsPrivate,
                            $ruleObj.IsPublic,
                            $ruleObj.IsAny,
                            $ruleObj.Action,
                            [System.Convert]::ToBoolean($ruleObj.Enabled),
                            $ruleObj.Direction,
                            $ruleObj.Protocol,
                            $ruleObj.LocalPort,
                            $ruleObj.LocalAddress,
                            $ruleObj.RemoteAddress,
                            $ruleObj.Program
                        )
                        [void]$results.Add($firewallRule)
                    }
                }
                catch {
                    Write-Warning "Error retrieving firewall rules from $server`: $($_.Exception.Message)"
                }
            }
            else {
                Write-TimestampedMessage "Operation canceled for $server"
            }
        }
        Write-Progress -Activity 'Retrieving Firewall Rules' -Completed
    }
    end {
        Write-TimestampedMessage 'Returning results for Get-ADHostFirewallRulesExport function.'
        return $results
    }
}
#EndRegion '.\Public\Get-ADHostFirewallRulesExport.ps1' 235
#Region '.\Public\Get-ADHostFirewallStatusReport.ps1' -1

<#
    .SYNOPSIS
        Retrieves firewall status and manageability information for specified hosts.
    .DESCRIPTION
        This function retrieves firewall status and evaluates remote manageability
        for a list of specified computers. It determines if each host is remotely
        accessible via WS-Management (WinRM) and retrieves firewall configuration settings
        for accessible hosts. If a host is unreachable, "N/A" values are assigned to firewall properties.
    .PARAMETER ComputerName
        Specifies one or more computer names from which to retrieve firewall status
        and manageability information. The function expects an array of computer names.
    .EXAMPLE
        Get-ADHostFirewallStatusReport -ComputerName "Server01"
        Retrieves firewall status and manageability details for "Server01".
    .EXAMPLE
        $activeServers = Get-ADHostManageability -DaysInactive 30 -Servers
        Get-ADHostFirewallStatusReport -ComputerName $activeServers.ComputerName
        Retrieves firewall status for all Active Directory servers that were active within the last 30 days.
    .EXAMPLE
        Get-ADHostFirewallStatusReport -ComputerName @("Workstation01", "Workstation02")
        Retrieves firewall status and manageability details for the specified workstations.
    .OUTPUTS
        PSCustomObject. Each object contains:
 
        - ComputerName : The name of the remote host.
        - IPAddress : The detected network address (if available).
        - IsRemotable : Indicates if the host is reachable via WS-Management.
        - Domain_Enabled : Firewall status for the Domain profile.
        - Domain_LogFileName : The log file path for the Domain profile.
        - Domain_LogMaxSizeKilobytes : The maximum log file size for the Domain profile.
        - Domain_LogAllowed : Indicates if allowed traffic is logged in the Domain profile.
        - Domain_LogBlocked : Indicates if blocked traffic is logged in the Domain profile.
        - Private_Enabled : Firewall status for the Private profile.
        - Private_LogFileName : The log file path for the Private profile.
        - Private_LogMaxSizeKilobytes: The maximum log file size for the Private profile.
        - Private_LogAllowed : Indicates if allowed traffic is logged in the Private profile.
        - Private_LogBlocked : Indicates if blocked traffic is logged in the Private profile.
        - Public_Enabled : Firewall status for the Public profile.
        - Public_LogFileName : The log file path for the Public profile.
        - Public_LogMaxSizeKilobytes : The maximum log file size for the Public profile.
        - Public_LogAllowed : Indicates if allowed traffic is logged in the Public profile.
        - Public_LogBlocked : Indicates if blocked traffic is logged in the Public profile.
    .NOTES
        - This function requires administrative privileges to execute.
        - Hosts must have WS-Management (WinRM) enabled to retrieve firewall settings remotely.
        - If a host is unreachable, firewall properties are set to "N/A".
        - The function updates progress dynamically as hosts are processed.
    .TROUBLESHOOTING NOTE
        - Ensure that the firewall allows remote management.
        - If a host is reported as unreachable, confirm network connectivity and the
          WS-Management service status.
        - If WinRM is disabled, enable it using: `Enable-PSRemoting -Force`
    .COMPONENT
        Windows Defender Firewall with Advanced Security (NetSecurity module)
    .LINK
        https://docs.microsoft.com/en-us/powershell/module/netsecurity/get-netfirewallprofile
    .LINK
        https://docs.microsoft.com/en-us/powershell/module/netsecurity/get-netfirewallrule
#>



function Get-ADHostFirewallStatusReport {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName
    )
    begin {
        Write-TimestampedMessage 'Initializing Get-ADHostFirewallStatusReport function.'
        Write-Progress -Activity "Initializing" -Status "Preparing to retrieve firewall information..." -PercentComplete 0
    }
    process {
        if ($PSCmdlet.ShouldProcess("Hosts", "Retrieve manageability and firewall status")) {
            try {
                $totalHosts = $ComputerName.Count
                $count = 0
                $results = foreach ($host in $ComputerName) {
                    $count++
                    $percentComplete = [math]::Round(($count / $totalHosts) * 100)
                    Write-TimestampedMessage "Processing: $host"
                    # Test manageability using Test-WSMan
                    $testRemoting = Test-WSMan -ComputerName $host -ErrorAction SilentlyContinue
                    $isRemotable = $null -ne $testRemoting
                    if ($isRemotable) {
                        try {
                            # ✅ **Update progress BEFORE firewall query**
                            Write-Progress -Activity "Retrieving Firewall Status" -Status "Querying $host firewall settings..." -PercentComplete $percentComplete
                            Write-TimestampedMessage "Host $host is remotely accessible. Retrieving firewall status..."
                            $fwResult = Get-ADHostFirewallStatus -ComputerName $host
                            $fw = $fwResult[0]  # Expecting a single object
                            [PSCustomObject]@{
                                ComputerName                = $host
                                IPAddress                   = $testRemoting.NetworkAddress
                                IsRemotable                 = $isRemotable
                                Domain_Enabled              = $fw.Domain_Enabled
                                Domain_LogFileName          = $fw.Domain_LogFileName
                                Domain_LogMaxSizeKilobytes  = $fw.Domain_LogMaxSizeKilobytes
                                Domain_LogAllowed           = $fw.Domain_LogAllowed
                                Domain_LogBlocked           = $fw.Domain_LogBlocked
                                # Private
                                Private_Enabled             = $fw.Private_Enabled
                                Private_LogFileName         = $fw.Private_LogFileName
                                Private_LogMaxSizeKilobytes = $fw.Private_LogMaxSizeKilobytes
                                Private_LogAllowed          = $fw.Private_LogAllowed
                                Private_LogBlocked          = $fw.Private_LogBlocked
                                # Public
                                Public_Enabled              = $fw.Public_Enabled
                                Public_LogFileName          = $fw.Public_LogFileName
                                Public_LogMaxSizeKilobytes  = $fw.Public_LogMaxSizeKilobytes
                                Public_LogAllowed           = $fw.Public_LogAllowed
                                Public_LogBlocked           = $fw.Public_LogBlocked
                            }
                        }
                        catch {
                            throw "Error retrieving firewall status for $host`: $($_.Exception.Message)"
                        }
                    }
                    else {
                        Write-TimestampedMessage "Host $host is NOT remotely accessible. Assigning 'N/A' values."
                        [PSCustomObject]@{
                            ComputerName                = $host
                            IPAddress                   = 'N/A'
                            IsRemotable                 = $isRemotable
                            Domain_Enabled              = 'N/A'
                            Domain_LogFileName          = 'N/A'
                            Domain_LogMaxSizeKilobytes  = 'N/A'
                            Domain_LogAllowed           = 'N/A'
                            Domain_LogBlocked           = 'N/A'
                            # Private
                            Private_Enabled             = 'N/A'
                            Private_LogFileName         = 'N/A'
                            Private_LogMaxSizeKilobytes = 'N/A'
                            Private_LogAllowed          = 'N/A'
                            Private_LogBlocked          = 'N/A'
                            # Public
                            Public_Enabled              = 'N/A'
                            Public_LogFileName          = 'N/A'
                            Public_LogMaxSizeKilobytes  = 'N/A'
                            Public_LogAllowed           = 'N/A'
                            Public_LogBlocked           = 'N/A'
                        }
                    }
                }
                Write-Progress -Activity "Processing Hosts" -Status "Completed processing hosts." -Completed
            }
            catch {
                Write-Progress -Activity "Processing Hosts" -Status "Error encountered. See logs." -Completed
                throw "Error retrieving host manageability and firewall status: $($_.Exception.Message)"
            }
        }
        else {
            Write-TimestampedMessage "Operation canceled by ShouldProcess."
        }
    }
    end {
        Write-Progress -Activity "Finalizing" -Status "Returning results..." -PercentComplete 100 -Completed
        Write-TimestampedMessage "End of Get-ADHostFirewallStatusReport function."
        return $results
    }
}

#EndRegion '.\Public\Get-ADHostFirewallStatusReport.ps1' 166
#Region '.\Public\Get-ADHostManageability.ps1' -1

<#
    .SYNOPSIS
        Retrieves Active Directory computers and determines their manageability.
    .DESCRIPTION
        Queries Active Directory for computers based on the specified criteria.
        Checks whether each computer is remotely manageable via WS-Man.
    .PARAMETER DaysInactive
        The number of days since the last logon timestamp to consider a computer active.
    .PARAMETER Servers
        If specified, filters results to include only servers.
    .PARAMETER Workstations
        If specified, filters results to include only workstations.
    .PARAMETER OperatingSystemSearchString
        A custom operating system search string to filter results.
    .PARAMETER SearchBase
        The LDAP search base to limit the query scope.
    .EXAMPLE
        Get-ADHostManageability -DaysInactive 60 -Servers
        Retrieves all servers active within the last 60 days.
    .EXAMPLE
        Get-ADHostManageability -Workstations -OperatingSystemSearchString "Windows 10"
        Retrieves all Windows 10 workstations that are considered active.
    .OUTPUTS
        PSCustomObject. Each object contains host details and manageability status.
    .NOTES
        Requires Active Directory module and administrative privileges.
#>

function Get-ADHostManageability {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([PSCustomObject])]
    param(
        [Parameter()]
        [int]$DaysInactive = 90,
        [Parameter()]
        [switch]$Servers,
        [Parameter()]
        [switch]$Workstations,
        [Parameter()]
        [string]$OperatingSystemSearchString,
        [Parameter()]
        [string]$SearchBase
    )
    begin {
        Write-TimestampedMessage 'Initializing Get-ADHostManageability function.'
        Write-Progress -Activity 'Initializing' -Status 'Setting up search filters...' -PercentComplete 0
        $cutoffDate = (Get-Date).AddDays(-$DaysInactive)
        Write-TimestampedMessage "Searching for Active Directory computers active since $cutoffDate"
        # Determine search pattern for OS filtering
        if ($Servers) {
            $searchPattern = '*server*'
        }
        elseif ($Workstations) {
            $searchPattern = '*windows 1*'
        }
        elseif ($OperatingSystemSearchString) {
            $searchPattern = "*$OperatingSystemSearchString*"
        }
        else {
            $searchPattern = '*'
        }
        Write-TimestampedMessage "Operating system search pattern: $searchPattern"
    }
    process {
        if ($PSCmdlet.ShouldProcess('Active Directory Computers', 'Retrieve AD computer objects')) {
            try {
                Write-Progress -Activity 'Querying Active Directory' -Status 'Retrieving computer objects...' -PercentComplete 10
                if ($SearchBase) {
                    Write-TimestampedMessage "Using SearchBase: $SearchBase"
                    $allComputers = Get-ADComputer -SearchBase $SearchBase `
                        -Filter { LastLogonTimeStamp -gt $cutoffDate } `
                        -Properties ipv4Address, OperatingSystem, LastLogonTimeStamp `
                        -ErrorAction Stop
                }
                else {
                    Write-TimestampedMessage "Querying all AD computers with OS filter: $searchPattern"
                    $allComputers = Get-ADComputer -Filter {
                        (LastLogonTimeStamp -gt $cutoffDate) -and
                        (OperatingSystem -like $searchPattern)
                    } -Properties ipv4Address, OperatingSystem, LastLogonTimeStamp `
                        -ErrorAction Stop
                }
                Write-TimestampedMessage "Found $($allComputers.Count) matching computers."
                if ($allComputers.Count -eq 0) {
                    Write-Progress -Activity 'Querying Active Directory' -Status 'No hosts found. Ending process.' -Completed
                    return
                }
                # Process each AD computer to check manageability
                $totalComputers = $allComputers.Count
                $count = 0
                $results = foreach ($comp in $allComputers) {
                    $count++
                    $percentComplete = [math]::Round(($count / $totalComputers) * 100)
                    Write-Progress -Activity 'Checking Manageability' -Status "Checking WS-Man access for $($comp.Name)..." -PercentComplete $percentComplete
                    Write-TimestampedMessage "Checking WS-Man access for $($comp.Name)"
                    $testRemoting = Test-WSMan -ComputerName $comp.Name -ErrorAction SilentlyContinue
                    [PSCustomObject]@{
                        ComputerName       = $comp.Name
                        IPAddress          = $comp.IPv4Address
                        OperatingSystem    = $comp.OperatingSystem
                        LastLogonTimeStamp = [DateTime]::FromFileTime($comp.LastLogonTimeStamp)
                        IsRemotable        = $null -ne $testRemoting
                    }
                }
                Write-Progress -Activity 'Checking Manageability' -Status 'Completed manageability check.' -Completed
            }
            catch {
                Write-Progress -Activity 'Checking Manageability' -Status 'Error encountered. See logs.' -Completed
                throw "Error retrieving AD computers: $($_.Exception.Message)"
            }
        }
        else {
            Write-TimestampedMessage 'Operation canceled by ShouldProcess.'
        }
    }
    end {
        Write-Progress -Activity 'Finalizing' -Status 'Returning results...' -PercentComplete 100 -Completed
        return $results
    }
}
#EndRegion '.\Public\Get-ADHostManageability.ps1' 123