#requires -Version 7.2
[CmdletBinding()]
param(
    [Parameter(Mandatory)][ValidatePattern('^[0-9a-fA-F-]{36}$')][string]$SubscriptionId,
    [ValidatePattern('^rg-avnm-deep-dive-we$')][string]$ResourceGroupName = 'rg-avnm-deep-dive-we',
    [string]$Location = 'westeurope',
    [ValidateSet('Summary', 'Membership', 'Routing', 'Security', 'Connectivity', 'Cleanup')][string]$Evidence = 'Summary'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$Manager = 'avnm-governance-we'
$Group = 'ng-avnm-mesh-we'
$NetworkApi = '2025-05-01'
$EffectiveSecurityApi = '2025-03-01'
$script:AzCliPath = (Get-Command az -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source

function Az([string[]]$Arguments, [switch]$Json, [switch]$AllowFailure) {
    $output = & $script:AzCliPath @Arguments 2>&1
    $code = $LASTEXITCODE
    if ($code -ne 0 -and -not $AllowFailure) { throw (($output -join "`n") -replace [regex]::Escape($SubscriptionId), '<subscription-redacted>') }
    if ($AllowFailure) { return [pscustomobject]@{ ExitCode = $code; Output = ($output -join "`n") } }
    if ($Json) { return (($output -join "`n") | ConvertFrom-Json -Depth 100) }
    return ($output -join "`n").Trim()
}

function Confirm-Subscription {
    $id = Az @('account', 'show', '--query', 'id', '-o', 'tsv')
    if ($id -ne $SubscriptionId) { throw 'The active subscription does not match the exact supplied subscription.' }
}

function Get-Members {
    $groupId = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Network/networkManagers/$Manager/networkGroups/$Group"
    $q = "networkresources | where type =~ 'microsoft.network/networkgroupmemberships' | mv-expand g=properties.groupMemberships | where tostring(g.networkGroupId) =~ '$groupId' | project vnet=tostring(split(id,'/')[8]) | order by vnet asc"
    $r = Az @('graph', 'query', '-q', $q, '--first', '100', '-o', 'json') -Json
    return @($r.data | Select-Object vnet)
}

function Get-Connectivity {
    $edge = Az @('network', 'nic', 'show', '-g', $ResourceGroupName, '-n', 'nic-avnm-edge-we', '--query', 'ipConfigurations[0].privateIPAddress', '-o', 'tsv')
    $command = "for port in 8080 8443; do timeout 6 bash -c '</dev/tcp/$edge/'`$port >/dev/null 2>&1; rc=`$?; if [ `$rc -eq 0 ]; then echo PORT=`$port RESULT=OPEN; else echo PORT=`$port RESULT=BLOCKED; fi; done; exit 0"
    return Az @('vm', 'run-command', 'invoke', '-g', $ResourceGroupName, '-n', 'vm-avnm-core-we', '--command-id', 'RunShellScript', '--scripts', $command, '--query', 'value[0].message', '-o', 'tsv')
}

Confirm-Subscription
switch ($Evidence) {
    'Summary' {
        $pools = Az @('network', 'manager', 'ipam-pool', 'list', '-g', $ResourceGroupName, '--manager-name', $Manager, '--query', '[].{pool:name,prefix:properties.addressPrefixes[0],state:properties.provisioningState}', '-o', 'json') -Json
        $vnets = Az @('network', 'vnet', 'list', '-g', $ResourceGroupName, '--query', "[].{vnet:name,prefix:addressSpace.ipamPoolPrefixAllocations[0].allocatedAddressPrefixes[0],group:tags.NetworkGroup}", '-o', 'json') -Json
        [pscustomobject]@{ IPAMPools = $pools; VNets = $vnets; DynamicMemberCount = @(Get-Members).Count; Connectivity = (Get-Connectivity) } | ConvertTo-Json -Depth 10
    }
    'Membership' { Get-Members | Format-Table -AutoSize }
    'Routing' {
        $routes = Az @('network', 'nic', 'show-effective-route-table', '-g', $ResourceGroupName, '-n', 'nic-avnm-core-we', '-o', 'json') -Json
        $routes.value |
            Where-Object nextHopType -eq 'ConnectedGroup' |
            ForEach-Object {
                [pscustomobject]@{
                    Prefix  = ($_.addressPrefix -join ',')
                    NextHop = $_.nextHopType
                    State   = $_.state
                }
            } | Sort-Object Prefix | Format-Table -AutoSize
    }
    'Security' {
        $uri = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Network/virtualNetworks/vnet-avnm-edge-we/listNetworkManagerEffectiveSecurityAdminRules?api-version=$EffectiveSecurityApi"
        $result = Az @('rest', '--method', 'post', '--url', $uri, '--headers', 'Content-Type=application/json', '--body', '{}', '-o', 'json') -Json
        $effective = $result.value | Where-Object { $_.properties.destinationPortRanges -contains '8080' } | Select-Object -First 1
        if (-not $effective) { throw 'No effective Security Admin rule was returned for TCP 8080.' }
        $defined = Az @('network', 'manager', 'security-admin-config', 'rule-collection', 'rule', 'show', '-g', $ResourceGroupName, '-n', $Manager, '--configuration-name', 'sac-avnm-central-we', '--rule-collection-name', 'rc-avnm-mesh-we', '--rule-name', 'Deny-Unmanaged-App-Port', '-o', 'json') -Json
        [pscustomobject]@{
            Rule              = $defined.name
            DefinedPriority   = $defined.priority
            EffectiveAccess   = $effective.properties.access
            Direction         = $effective.properties.direction
            Port              = ($effective.properties.destinationPortRanges -join ',')
            EffectiveProtocol = $effective.properties.protocol
        } | Format-Table -AutoSize
    }
    'Connectivity' { Get-Connectivity }
    'Cleanup' {
        $groupExists = Az @('group', 'exists', '-n', $ResourceGroupName, '-o', 'tsv')
        $assignmentScope = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName"
        $assignment = Az @('policy', 'assignment', 'show', '-n', 'avnm-mesh-dynamic-membership', '--scope', $assignmentScope, '-o', 'none') -AllowFailure
        $definition = Az @('policy', 'definition', 'show', '-n', 'avnm-add-tagged-vnets-to-mesh', '--subscription', $SubscriptionId, '-o', 'none') -AllowFailure
        [pscustomobject]@{ ResourceGroupExists = [bool]::Parse($groupExists); PolicyAssignmentExists = ($assignment.ExitCode -eq 0); PolicyDefinitionExists = ($definition.ExitCode -eq 0) } | Format-List
    }
}
