Public/Asa/Get-PwAsaAnalysis.ps1

function Get-PwAsaAnalysis {
    [CmdletBinding()]
    <#
        .SYNOPSIS
            Performs config analysis on ASA from config file or backup.
    #>


    Param (
        [Parameter(Mandatory = $True, Position = 0, ParameterSetName = 'config')]
        [ValidateNotNullOrEmpty()]
        [string]$ConfigPath,

        [Parameter(Mandatory = $True, Position = 0, ParameterSetName = 'backup')]
        [string]$BackupPath
    )

    # It's nice to be able to see what cmdlet is throwing output isn't it?
    $VerbosePrefix = "Get-PwAsaAnalysis:"

    if ($BackupPath) {
        if (Test-Path $BackupPath) {
            $BackupDirectory = Get-ChildItem -Path $BackupPath | Split-Path -Parent
            $BackupName = (Get-ChildItem -Path $BackupPath).BaseName
            $DestinationDirectory = Join-Path -Path $BackupDirectory -ChildPath $BackupName

            $CurrentFiles = Get-ChildItem -Path $BackupDirectory

            # Expand archives
            Expand-Archive -Path $BackupPath -DestinationPath $BackupDirectory

            $NewFiles = Get-ChildItem -Path $BackupDirectory
            $NewFolder = $NewFiles | Where-Object { $CurrentFiles.Name -notcontains $_.Name }
            Rename-Item -Path $NewFolder -NewName $BackupName
            Write-Verbose "$VerbosePrefix $($NewFolder.FullName)"

            $ExcelPath = Join-Path -Path $DestinationDirectory -ChildPath "$BackupName`.xlsx"
            $ConfigPath = Join-Path -Path $DestinationDirectory -ChildPath "running-config.cfg"
        } else {
            Throw "BackupPath not found: $BackupPath"
        }
    } elseif ($ConfigPath) {
        if (Test-Path $ConfigPath) {
            #$BackupDirectory = Get-ChildItem -Path $ConfigPath | Split-Path -Parent
            $BackupName = (Get-ChildItem -Path $ConfigPath).BaseName
            $DestinationDirectory = Get-ChildItem -Path $ConfigPath | Split-Path -Parent
            #$NewDirectory = New-Item -Path $DestinationDirectory -ItemType Directory

            $ExcelPath = Join-Path -Path $DestinationDirectory -ChildPath "$BackupName`.xlsx"
            #$CopyConfigPath = Join-Path -Path $NewDirectory -ChildPath 'running-config.txt'
            #$CopyItem = Copy-Item -Path $ConfigPath -Destination $CopyConfigPath
        } else {
            Throw "ConfigPath not found: $ConfigPath"
        }
    }

    Write-Verbose "$VerbosePrefix OutputPath is $ExcelPath"

    #region asa
    #####################################################

    Write-Verbose "$VerbosePrefix Getting Access Policies"
    $AccessPolicies = Get-PwAsaSecurityPolicy -ConfigPath $ConfigPath -Verbose:$false
    $Global:AccessPolicies = $AccessPolicies
    Write-Verbose "$VerbosePrefix Found $($AccessPolicies.Count) Access Policies"

    Write-Verbose "$VerbosePrefix Getting Objects"
    $Objects = Get-PwAsaObject -ConfigPath $ConfigPath -Verbose:$false
    $NetworkObjects = $Objects | Where-Object { $_.GetType().Name -eq 'NetworkObject' }
    $ServiceObjects = $Objects | Where-Object { $_.GetType().Name -eq 'ServiceObject' }
    if ($ServiceObjects.Count -eq 0) {
        $ServiceObjects = @()
        $ServiceObjects += New-PwServiceObject -name 'dummy-fake-service'
    }
    $Global:NetworkObjects = $NetworkObjects
    $Global:ServiceObjects = $ServiceObjects
    Write-Verbose "$VerbosePrefix Found $($NetworkObjects.Count) Network Objects"
    Write-Verbose "$VerbosePrefix Found $($ServiceObjects.Count) Service Objects"

    Write-Verbose "$VerbosePrefix Resolving Access Policies"
    $ResolvedAccessPolicies = $AccessPolicies | Resolve-PwSecurityPolicy -NetworkObjects $NetworkObjects -ServiceObjects $ServiceObjects -FirewallType 'asa' -Verbose:$false

    Write-Verbose "Getting Nat Policies"
    $NatPolicies = Get-PwAsaNatPolicy -ConfigPath $ConfigPath -Verbose:$false
    $Global:NatPolicies = $NatPolicies
    Write-Verbose "Resolving Nat Policies"
    $ResolvedNatPolicies = $NatPolicies | Resolve-PwNatPolicy -NetworkObjects $NetworkObjects -ServiceObjects $ServiceObjects -FirewallType 'asa' -Verbose:$false

    # remove natexempts
    $InterestingNats = $ResolvedNatPolicies | Where-Object { !($_.NatExempt) }
    $global:InterestingNats = $InterestingNats

    # look for 32 bit only Nats
    $IpRx = [regex] '^(\d+)\.(\d+)\.(\d+)\.(\d+)$'
    $InterestingNats = $InterestingNats | Where-Object { ($_.ResolvedOriginalSource -match '/32') -or ($_.ResolvedOriginalSource -eq '') -or ($IpRx.Match($_.ResolvedOriginalSource).Success) }
    $InterestingNats = $InterestingNats | Where-Object { ($_.ResolvedOriginalDestination -match '/32') -or ($_.ResolvedOriginalDestination -eq '') -or ($IpRx.Match($_.ResolvedOriginalDestination).Success) }
    $InterestingNats = $InterestingNats | Where-Object { ($_.ResolvedTranslatedSource -match '/32') -or ($_.ResolvedTranslatedSource -eq '') -or ($IpRx.Match($_.ResolvedTranslatedSource).Success) }
    $InterestingNats = $InterestingNats | Where-Object { ($_.ResolvedTranslatedDestination -match '/32') -or ($_.ResolvedTranslatedDestination -eq '') -or ($IpRx.Match($_.ResolvedTranslatedDestination).Success) }

    # filter for public IPs
    $PublicNatsOnly = @()
    foreach ($nat in $InterestingNats) {
        $PublicNat = $false
        if ($nat.ResolvedOriginalSource -ne "") {
            if (!(Test-IpInRange -ContainingNetwork 192.168.0.0/16 -ContainedNetwork $nat.ResolvedOriginalSource) -and
                !(Test-IpInRange -ContainingNetwork 172.16.0.0/12 -ContainedNetwork $nat.ResolvedOriginalSource) -and
                !(Test-IpInRange -ContainingNetwork 10.0.0.0/8 -ContainedNetwork $nat.ResolvedOriginalSource)) {
                $PublicNat = $true
            }
        }

        if ($nat.ResolvedOriginalDestination -ne "") {
            if (!(Test-IpInRange -ContainingNetwork 192.168.0.0/16 -ContainedNetwork $nat.ResolvedOriginalDestination) -and
                !(Test-IpInRange -ContainingNetwork 172.16.0.0/12 -ContainedNetwork $nat.ResolvedOriginalDestination) -and
                !(Test-IpInRange -ContainingNetwork 10.0.0.0/8 -ContainedNetwork $nat.ResolvedOriginalDestination)) {
                $PublicNat = $true
            }
        }

        if ($nat.ResolvedTranslatedSource -ne "") {
            if (!(Test-IpInRange -ContainingNetwork 192.168.0.0/16 -ContainedNetwork $nat.ResolvedTranslatedSource) -and
                !(Test-IpInRange -ContainingNetwork 172.16.0.0/12 -ContainedNetwork $nat.ResolvedTranslatedSource) -and
                !(Test-IpInRange -ContainingNetwork 10.0.0.0/8 -ContainedNetwork $nat.ResolvedTranslatedSource)) {
                $PublicNat = $true
            }
        }

        if ($nat.ResolvedTranslatedDestination -ne "") {
            if (!(Test-IpInRange -ContainingNetwork 192.168.0.0/16 -ContainedNetwork $nat.ResolvedTranslatedDestination) -and
                !(Test-IpInRange -ContainingNetwork 172.16.0.0/12 -ContainedNetwork $nat.ResolvedTranslatedDestination) -and
                !(Test-IpInRange -ContainingNetwork 10.0.0.0/8 -ContainedNetwork $nat.ResolvedTranslatedDestination)) {
                $PublicNat = $true
            }
        }

        if ($PublicNat) {
            $PublicNatsOnly += $nat
        }
    }

    # Generate Nat Report
    $NatSummary = @()
    foreach ($nat in $PublicNatsOnly) {

        if (!(Test-IpInRange -ContainingNetwork 192.168.0.0/16 -ContainedNetwork $nat.ResolvedOriginalSource) -and
            !(Test-IpInRange -ContainingNetwork 172.16.0.0/12 -ContainedNetwork $nat.ResolvedOriginalSource) -and
            !(Test-IpInRange -ContainingNetwork 10.0.0.0/8 -ContainedNetwork $nat.ResolvedOriginalSource)) {
            $NatInternalAddress = $nat.ResolvedTranslatedSource -replace '/32', ''
            $NatExternalAddress = $nat.ResolvedOriginalSource -replace '/32', ''
        } else {
            $NatInternalAddress = $nat.ResolvedOriginalSource -replace '/32', ''
            $NatExternalAddress = $nat.ResolvedTranslatedSource -replace '/32', ''
        }

        # Nat Name
        $ObjectLookup = $NetworkObjects | Where-Object { $_.Member -contains "$NatInternalAddress/32" }
        if ($nat.Name) {
            $NatObjectName = $nat.Name
        } elseif ($nat.OriginalSource -match '[a-z][A-Z]') {
            $NatObjectName = $nat.OriginalSource
        } elseif ($nat.TranslatedSource -match '[a-z][A-Z]') {
            $NatObjectName = $nat.TranslatedSource
        } elseif ($ObjectLookup) {
            $NatObjectName = $ObjectLookup.Name
        } else {
            $NatObjectName = $NatInternalAddress
        }

        $AccessLookup = $ResolvedAccessPolicies | Where-Object { ($_.ResolvedDestination -eq "$NatInternalAddress/32") -or ($_.ResolvedDestination -eq "$NatExternalAddress/32") } | Select-Object Source, ResolvedSource, Service, ResolvedService -Unique
        $Sources = ($AccessLookup.Source | Select-Object -Unique)
        foreach ($source in $Sources) {
            $NatSourceName = $source
            $ResolvedSource = $AccessLookup | Where-Object { $_.Source -eq $source } | Select-Object ResolvedSource -Unique

            foreach ($rs in $ResolvedSource) {
                $UniqueServices = $AccessLookup | Where-Object { $_.Source -eq $NatSourceName } | Select-Object Service -Unique
                $NatServiceName = $UniqueServices

                foreach ($uservice in $NatServiceName) {
                    #$NatServiceName = $uservice.Service
                    $ResolvedService = $AccessLookup | Where-Object { ($_.Source -eq $NatSourceName) -and ($_.Service -eq $uservice.Service) } | Select-Object ResolvedService -Unique

                    foreach ($rservice in $ResolvedService) {
                        $new = "" | Select-Object ObjectName, InternalAddress, ExternalAddress, SourceName, SourceAddress, ServiceName, Service
                        $NatSummary += $new
                        $new.ObjectName = $NatObjectName
                        $new.InternalAddress = $NatInternalAddress
                        $new.ExternalAddress = $NatExternalAddress
                        $new.SourceName = $NatSourceName
                        $new.SourceAddress = $rs.ResolvedSource
                        $new.ServiceName = $uservice.Service[0]
                        $new.Service = $rservice.ResolvedService
                    }
                }
            }
        }
    }

    $Interfaces = Get-PwAsaInterface -ConfigPath $ConfigPath
    $SourceInterfaceMap = @{ }
    $DestinationInterfaceMap = @{ }
    foreach ($interface in $Interfaces) {
        if ($interface.AccessList) {
            $AclName = $interface.AccessList
            if ($interface.AccessListDirection -eq 'in') {
                $SourceInterfaceMap.$AclName = $interface.NameIf
            } elseif ($interface.AccessListDirection -eq 'out') {
                $DestinationInterfaceMap.$AclName = $interface.NameIf
            }
        }
    }

    #$BaseName = (Get-ChildItem -Path $ConfigPath).BaseName
    #$ExcelPath = Join-Path -Path (Split-Path -Path $ConfigPath) -ChildPath "$BaseName`.xlsx"
    $NatSummary | Export-Excel -Path $ExcelPath -WorksheetName 'Overview' -Verbose:$false

    $ResolvedNatPolicies | Select-Object Number, Comment, Enabled, SourceInterface, DestinationInterface, `
        OriginalSource, ResolvedOriginalSource,
    OriginalDestination, ResolvedOriginalDestination,
    OriginalService, ResolvedOriginalService,
    TranslatedSource, ResolvedTranslatedSource,
    TranslatedDestination, ResolvedTranslatedDestination,
    TranslatedService, ResolvedTranslatedService,
    SourceTranslationType,
    DestinationTranslationType,
    ProxyArp,
    RouteLookup,
    NatExempt | Export-Excel -Path $ExcelPath -WorksheetName 'NAT' -Verbose:$false

    $ResolvedAccessPolicies | Select-Object AccessList, AclType, Number, Action, `
        #SourceInterface, DestinationInterface,
    @{ Name = 'SourceInterface'; Expression = { $SourceInterfaceMap."$($_.AccessList)" } },
    @{ Name = 'DestinationInterface'; Expression = { $DestinationInterfaceMap."$($_.AccessList)" } },
    @{ Name = 'Source'; Expression = { $_.Source -join ',' } }, ResolvedSource,
    @{ Name = 'Destination'; Expression = { $_.Destination -join ',' } }, ResolvedDestination,
    Protocol,
    SourcePort, ResolvedSourcePort,
    DestinationPort, ResolvedDestinationPort,
    @{ Name = 'SourceService'; Expression = { $_.SourceService -join ',' } },
    ResolvedSourceService,
    @{ Name = 'Service'; Expression = { $_.Service -join ',' } },
    ResolvedService,
    Comment,
    Enabled,
    NewRule,
    Status,
    Notes | Export-Excel -Path $ExcelPath -WorksheetName 'AccessPolicies' -FreezeTopRow -Verbose:$false
    #####################################################
    #endregion asa

}