Functions/Remove-NsgSubnetAssociation.ps1

function Remove-NsgSubnetAssociation {
<#
    .SYNOPSIS
        Removes the NSG association from a subnet in an Azure Virtual Network.
 
    .DESCRIPTION
        This function disassociates a Network Security Group (NSG) from a specified subnet
        within a Virtual Network. After running this function, the subnet will no longer
        have an NSG attached to it.
 
    .PARAMETER VirtualNetworkName
        The name of the Virtual Network containing the subnet.
 
    .PARAMETER SubnetName
        The name of the subnet to remove the NSG association from.
 
    .PARAMETER ResourceGroupName
        The name of the Resource Group containing the Virtual Network.
 
    .PARAMETER AzSubscription
        Optional. The Azure subscription to target. If not specified, uses the current context.
 
    .PARAMETER Force
        Optional. If specified, skips the confirmation prompt.
 
    .EXAMPLE
        Remove-NsgSubnetAssociation -VirtualNetworkName "MyVNet" -SubnetName "MySubnet" -ResourceGroupName "MyResourceGroup"
 
        Removes the NSG association from MySubnet in MyVNet after confirmation.
 
    .EXAMPLE
        Remove-NsgSubnetAssociation -VirtualNetworkName "MyVNet" -SubnetName "MySubnet" -ResourceGroupName "MyResourceGroup" -Force
 
        Removes the NSG association from MySubnet in MyVNet without confirmation.
 
    .EXAMPLE
        Remove-NsgSubnetAssociation -VirtualNetworkName "MyVNet" -SubnetName "MySubnet" -ResourceGroupName "MyResourceGroup" -AzSubscription "MySubscription"
 
        Removes the NSG association from MySubnet in a specific subscription.
 
    .INPUTS
        Input is from command line or called from a script.
 
    .OUTPUTS
        Outputs the updated subnet configuration to the pipeline.
 
    .NOTES
        Author: Lars Panzerbjørn
        Creation Date: 2026.01.21
#>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param
    (
        [Parameter(
            Mandatory,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            HelpMessage = 'Name of the virtual network containing the subnet')]
        [string]$VirtualNetworkName,

        [Parameter(
            Mandatory,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            HelpMessage = 'Name of the subnet to remove the NSG association from')]
        [string]$SubnetName,

        [Parameter(
            Mandatory,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            HelpMessage = 'Resource group name containing the virtual network')]
        [string]$ResourceGroupName,

        [Parameter(
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            HelpMessage = 'Which Azure subscription would you like to target?')]
        [Alias('AzSub')]
        [string]$AzSubscription,

        [Parameter(
            HelpMessage = 'Skip confirmation prompt')]
        [switch]$Force
    )

    BEGIN{
        Write-Verbose "Beginning $($MyInvocation.Mycommand)"

        # 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{
        Write-Verbose "Processing $($MyInvocation.Mycommand)"

        TRY{
            # Set subscription context if specified
            IF($AzSubscription) {
                Write-Verbose "Setting Azure context to subscription: $AzSubscription"
                $Subscription = Get-AzSubscription -SubscriptionName $AzSubscription
                Set-AzContext -SubscriptionId $Subscription.Id | Out-Null
            }

            # Get the virtual network
            Write-Verbose "Getting virtual network: $VirtualNetworkName in resource group: $ResourceGroupName"
            $VNet = Get-AzVirtualNetwork -Name $VirtualNetworkName -ResourceGroupName $ResourceGroupName -ErrorAction Stop

            # Get the subnet
            Write-Verbose "Getting subnet: $SubnetName"
            $Subnet = $VNet.Subnets | Where-Object { $_.Name -eq $SubnetName }

            IF(-not $Subnet) {
                throw "Subnet '$SubnetName' not found in virtual network '$VirtualNetworkName'"
            }

            # Check if the subnet has an NSG associated
            IF(-not $Subnet.NetworkSecurityGroup) {
                Write-Host "`nSubnet '$SubnetName' does not have an NSG associated." -ForegroundColor Yellow
                return $null
            }

            # Get current NSG details for display
            $CurrentNsgId = $Subnet.NetworkSecurityGroup.Id
            $NsgIdParts = $CurrentNsgId -split '/'
            $CurrentNsgName = $NsgIdParts[-1]
            $CurrentNsgRg = $NsgIdParts[4]

            Write-Host "`nCurrent NSG Association:" -ForegroundColor Cyan
            Write-Host " Virtual Network: $VirtualNetworkName" -ForegroundColor Yellow
            Write-Host " Subnet: $SubnetName" -ForegroundColor Yellow
            Write-Host " NSG: $CurrentNsgName" -ForegroundColor Yellow
            Write-Host " NSG Resource Group: $CurrentNsgRg`n" -ForegroundColor Yellow

            # Confirm the action
            $ConfirmMessage = "Remove NSG '$CurrentNsgName' from subnet '$SubnetName' in VNet '$VirtualNetworkName'?"

            IF($Force -or $PSCmdlet.ShouldProcess($SubnetName, "Remove NSG association")) {
                Write-Verbose "Removing NSG association from subnet: $SubnetName"

                # Remove the NSG association
                $Subnet.NetworkSecurityGroup = $null

                # Update the virtual network
                Write-Verbose "Updating virtual network configuration"
                $UpdatedVNet = Set-AzVirtualNetwork -VirtualNetwork $VNet -ErrorAction Stop

                # Get the updated subnet to confirm
                $UpdatedSubnet = $UpdatedVNet.Subnets | Where-Object { $_.Name -eq $SubnetName }

                Write-Host "Successfully removed NSG association from subnet '$SubnetName'." -ForegroundColor Green

                # Return the updated subnet information
                $Result = [PSCustomObject]@{
                    VirtualNetworkName    = $VirtualNetworkName
                    SubnetName            = $SubnetName
                    ResourceGroupName     = $ResourceGroupName
                    PreviousNsg           = $CurrentNsgName
                    CurrentNsg            = IF($UpdatedSubnet.NetworkSecurityGroup) { $UpdatedSubnet.NetworkSecurityGroup.Id } else { $null }
                    Status                = 'NSG Removed'
                }

                return $Result
            }
            else {
                Write-Host "Operation cancelled by user." -ForegroundColor Yellow
            }
        }
        CATCH{
            Write-Error "Error removing NSG association: $($_.Exception.Message)"
            throw
        }
    }

    END{
        Write-Verbose "Ending $($MyInvocation.Mycommand)"
    }
}

# Example usage (uncomment to test):
# Remove-NsgSubnetAssociation -VirtualNetworkName "MyVNet" -SubnetName "MySubnet" -ResourceGroupName "MyResourceGroup"
# Remove-NsgSubnetAssociation -VirtualNetworkName "MyVNet" -SubnetName "MySubnet" -ResourceGroupName "MyResourceGroup" -Force