Functions/Get-NsgAssociatedVnets.ps1

function Get-NsgAssociatedVnets {
    <#
    .SYNOPSIS
        Gets all VNETs associated with an Azure Network Security Group.
 
    .DESCRIPTION
        This function takes an Azure NSG and returns information about all VNETs
        that have subnets or network interfaces associated with the NSG.
 
    .PARAMETER NsgName
        The name of the Network Security Group.
 
    .PARAMETER ResourceGroupName
        The name of the Resource Group containing the NSG.
 
    .PARAMETER Nsg
        An existing NSG object (from Get-AzNetworkSecurityGroup).
 
    .EXAMPLE
        Get-NsgAssociatedVnets -NsgName "MyNSG" -ResourceGroupName "MyResourceGroup"
 
    .EXAMPLE
        $nsg = Get-AzNetworkSecurityGroup -Name "MyNSG" -ResourceGroupName "MyResourceGroup"
        Get-NsgAssociatedVnets -Nsg $nsg
 
    .INPUTS
        Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup
 
    .OUTPUTS
        PSCustomObject
        Returns information about VNETs associated with the NSG.
 
    .NOTES
        Author: Lars Panzerbjørn
        Creation Date: 2026.01.29
    #>


    [CmdletBinding(DefaultParameterSetName = 'ByName')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [string]$NsgName,

        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [string]$ResourceGroupName,

        [Parameter(Mandatory, ParameterSetName = 'ByObject', ValueFromPipeline = $true)]
        [Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup]$Nsg
    )

    BEGIN{
        # Check if Az.Network module is available
        IF(-not (Get-Module -ListAvailable -Name Az.Network)) {
            throw "Az.Network module is not installed. Please install it using: Install-Module -Name Az.Network"
        }

        # Import the module if not already loaded
        IF(-not (Get-Module -Name Az.Network)) {
            Import-Module Az.Network
        }
    }

    PROCESS{
        TRY{
            # Get the NSG object if not provided
            IF($PSCmdlet.ParameterSetName -eq 'ByName') {
                Write-Verbose "Retrieving NSG: $NsgName from Resource Group: $ResourceGroupName"
                $Nsg = Get-AzNetworkSecurityGroup -Name $NsgName -ResourceGroupName $ResourceGroupName -ErrorAction Stop
            }

            Write-Host "`nNSG: $($Nsg.Name)" -ForegroundColor Cyan
            Write-Host "Resource Group: $($Nsg.ResourceGroupName)" -ForegroundColor Cyan
            Write-Host "Location: $($Nsg.Location)`n" -ForegroundColor Cyan

            $vnetInfo = @()

            # Check subnets associated with the NSG
            IF($Nsg.Subnets -and $Nsg.Subnets.Count -gt 0) {
                Write-Host "Associated Subnets ($($Nsg.Subnets.Count)):" -ForegroundColor Green

                foreach ($subnetRef in $Nsg.Subnets) {
                    # Parse the subnet resource ID to extract VNET and subscription info
                    $subnetId = $subnetRef.Id
                    $idParts = $subnetId -split '/'

                    # Extract subscription, resource group, VNET, and subnet name
                    $subscriptionId = $idParts[2]
                    $subnetRg = $idParts[4]
                    $vnetName = $idParts[8]
                    $subnetName = $idParts[10]

                    Write-Host " - Subnet: $subnetName" -ForegroundColor Yellow
                    Write-Host " VNET: $vnetName" -ForegroundColor Yellow
                    Write-Host " Resource Group: $subnetRg" -ForegroundColor Yellow
                    Write-Host " Subscription: $subscriptionId`n" -ForegroundColor Yellow

                    # Add to collection
                    $vnetInfo += [PSCustomObject]@{
                        Type              = 'Subnet'
                        VNetName          = $vnetName
                        SubnetName        = $subnetName
                        ResourceGroup     = $subnetRg
                        SubscriptionId    = $subscriptionId
                        ResourceId        = $subnetId
                    }
                }
            } else {
                Write-Host "No subnets are associated with this NSG.`n" -ForegroundColor Yellow
            }

            # Check network interfaces associated with the NSG
            IF($Nsg.NetworkInterfaces -and $Nsg.NetworkInterfaces.Count -gt 0) {
                Write-Host "Associated Network Interfaces ($($Nsg.NetworkInterfaces.Count)):" -ForegroundColor Green

                foreach ($nicRef in $Nsg.NetworkInterfaces) {
                    $nicId = $nicRef.Id
                    $idParts = $nicId -split '/'

                    $subscriptionId = $idParts[2]
                    $nicRg = $idParts[4]
                    $nicName = $idParts[8]

                    Write-Host " - NIC: $nicName (Resource Group: $nicRg)`n" -ForegroundColor Yellow

                    # Optionally, get the NIC details to find its VNET
                    TRY{
                        $nic = Get-AzNetworkInterface -ResourceGroupName $nicRg -Name $nicName -ErrorAction Stop

                        foreach ($ipConfig in $nic.IpConfigurations) {
                            IF($ipConfig.Subnet) {
                                $subnetId = $ipConfig.Subnet.Id
                                $subnetIdParts = $subnetId -split '/'
                                $vnetName = $subnetIdParts[8]
                                $subnetName = $subnetIdParts[10]

                                Write-Host " Connected to Subnet: $subnetName" -ForegroundColor Magenta
                                Write-Host " VNET: $vnetName`n" -ForegroundColor Magenta

                                $vnetInfo += [PSCustomObject]@{
                                    Type              = 'NetworkInterface'
                                    VNetName          = $vnetName
                                    SubnetName        = $subnetName
                                    NICName           = $nicName
                                    ResourceGroup     = $nicRg
                                    SubscriptionId    = $subscriptionId
                                    ResourceId        = $nicId
                                }
                            }
                        }
                    } CATCH{
                        Write-Warning "Could not retrieve details for NIC: $nicName - $($_.Exception.Message)"
                    }
                }
            } else {
                Write-Host "No network interfaces are directly associated with this NSG.`n" -ForegroundColor Yellow
            }

            # Return the collection of VNET associations
            IF($vnetInfo.Count -gt 0) {
                Write-Host "`nSummary - Unique VNETs:" -ForegroundColor Cyan
                $uniqueVnets = $vnetInfo | Select-Object -Property VNetName, ResourceGroup -Unique
                $uniqueVnets | Format-Table -AutoSize

                return $vnetInfo
            } else {
                Write-Host "No VNET associations found for this NSG." -ForegroundColor Red
                return $null
            }

        } CATCH{
            Write-Error "Error retrieving NSG associations: $($_.Exception.Message)"
            throw
        }
    }
}

# Example usage (uncomment to test):
# Get-NsgAssociatedVnets -NsgName "MyNSG" -ResourceGroupName "MyResourceGroup"
# Or pipe an NSG object:
# Get-AzNetworkSecurityGroup -Name "MyNSG" -ResourceGroupName "MyRG" | Get-NsgAssociatedVnets