14

Quickly Find Azure VMs Without NSG Protection

Network Security Groups (NSGs) are a key security feature in Azure, acting as virtual firewalls to control network traffic to…

Network Security Groups (NSGs) are a key security feature in Azure, acting as virtual firewalls to control network traffic to and from your Virtual Machines (VMs). Without NSGs attached to either a VM’s network interface or its subnet, your VM can be exposed to unwanted traffic and potential security risks.

To help Azure administrators and cloud engineers identify such risks, I created AzVM-NSGSecurityAudit — a simple PowerShell script that scans your Azure subscriptions and lists all VMs not protected by any NSG.

🚨 Why This Script Matters

In complex Azure environments, it’s easy to overlook whether every VM has proper NSG protection. While Azure provides tools to configure and enforce NSGs, misconfigurations happen. This script helps:

  • Identify VMs without any NSG attached either at the network interface or subnet level
  • Provide quick visibility across all subscriptions in your tenant
  • Support security reviews, compliance checks, or manual audits

🤖 What AzVM-NSGSecurityAudit Does

The script:

  • Connects to your Azure account and enumerates all subscriptions
  • Retrieves all Network Security Groups (NSGs) and collects their associated network interfaces and subnets
  • Retrieves all VMs and inspects their primary network interface and subnet
  • Flags VMs which have neither the NIC nor subnet protected by an NSG
  • Outputs a simple list of unprotected VMs, including VM name, resource group, and subscription name

⚙️ How to Use It

  1. Ensure you have the Azure PowerShell module installed and are logged in (Connect-AzAccount).
  2. Run the script locally or integrate it into automation runbooks.
  3. Review the output in the console or extend it to export as CSV or JSON as needed.

📋 Script

Link: https://github.com/simon-vedder/powershell/scripts/audits/AzVM-NSGSecurityAudit.ps1

<#
.TITLE
    AzVM-NSGSecurityAudit

.SYNOPSIS
    Find all Virtual Machines which are not protected by an NSG.

.DESCRIPTION
    This PowerShell script will find every Virtual Machine within your tenant to find resources without an assigned Network Security Group.
    It will not review if the rules within the NSG are secure.
    
.TAGS
    PowerShell, Diagnostic, Security

.MINROLE
    Reader

.PERMISSIONS
    Microsoft.Resources/subscriptions/read
    Microsoft.Network/networkSecurityGroups/read
    Microsoft.Compute/virtualMachines/read

.AUTHOR
    Simon Vedder

.VERSION
    1.0

.CHANGELOG
    1.0 - Initial release

.LASTUPDATE
    2025-06-21

.NOTES

.USAGE
  - Run locally or use it in your runbook while changing Connect-AzAccount to use a non-interactive authentication
  
#>

$unprotectedVMs = @()
$protectedVMs = @() #optional

Write-Host "AzVM-NSGSecurityAudit"
Write-Host "- by Simon Vedder -"
Write-Host "---------------------"
Write-Host (Get-Date)

#LOGIN
Connect-AzAccount | Out-Null

# Get all Subscriptions
try {
    Write-Host "Retrieve Azure Subscriptions"
    $subscriptions = Get-AzSubscription -ErrorAction Stop
    Write-Host "Subscriptions successfully retrieved" -ForegroundColor Green
}
catch {
    Write-Error "Error: $($_.Exception.Message)"
}

# Run code in each subscription
foreach($sub in $subscriptions)
{
    # Change the subscription
    Set-AzContext -SubscriptionId $sub.Id | Out-Null
    Write-Host ""
    Write-Host "SubscriptionName: $($sub.Name)"
    
    # Get all NSGs within the subscription
    try {
        Write-Host "1. Get all NSGs"
        $NSGs = Get-AzNetworkSecurityGroup -ErrorAction Stop
    }
    catch {
        Write-Error "Error: $($_.Exception.Message)"
    }
    if (-not $NSGs) {
        Write-Warning "No Azure Network Security Groups found in the current context."
        return
    }
    else {
        Write-Host "NetworkSecurityGroups successfully retrieved" -ForegroundColor Green
    }
    # Set variables with NSG Ids that are assigned to NICs and Subnets
    $protectedNICs = $NSGs.NetworkInterfaces.Id
    $protectedSubnets = $NSGs.Subnets.Id


    # Get all VMs within the Subscription
    try {
        Write-Host "2. Get all VMs"
        $VMs = Get-AzVM
    }
    catch {
        Write-Error "Error: $($_.Exception.Message)"
    }
    if (-not $VMs) {
        Write-Warning "No Azure Virtual Machines found in the current context."
        return
    }
    else {
        Write-Host "VirtualMachines successfully retrieved" -ForegroundColor Green
    }

    

    #Begin the check
    Write-Host "- Start protection check: "
    foreach ($VM in $VMs)
    {
        # Set variables with the VMs NIC and SubnetId
        $NIC = Get-AzNetworkInterface -Name ($VM.NetworkProfile.NetworkInterfaces[0].Id.Split('/')[-1]) -ResourceGroupName $VM.ResourceGroupName
        $subnetId = $NIC.IpConfigurations[0].Subnet.Id

        # Check if the the protected NICs and protectedSubnets contain the VMs resources (NIC & Subnet)
        if ($protectedNICs -notcontains $NIC.Id -and $protectedSubnets -notcontains $subnetId)
        {
            Write-Host "VM found: $($VM.Name)" -ForegroundColor Cyan

            $unprotectedVMs += [PSCustomObject]@{
                UnprotectedVM   = $VM.Name
                ResourceGroup   = $VM.ResourceGroupName
                Subscription    = $sub.Name 
            }
        }
        else {
            $protectedVMs += [PSCustomObject]@{
                ProtectedVM     = $VM.Name
                ResourceGroup   = $VM.ResourceGroupName
                Subscription    = $sub.Name 
            }
        }
    }
}

#Output
if ($unprotectedVMs.Count -gt 0)
{
    Write-Host ""
    Write-Host "--- Overview ---"
    $unprotectedVMs | Format-Table -AutoSize
    $protectedVMs | Format-Table -AutoSize #uncomment if you want to see protected VMS
}
else {
    Write-Host "Every VM is protected by a NSG!" -ForegroundColor Green
}

✅ Output

💡 Conclusion

AzVM-NSGSecurityAudit is a straightforward, effective tool to improve your Azure security posture by ensuring all your VMs have NSG protection. It’s a great first step in cloud security hygiene, especially useful for administrators managing multiple subscriptions.

Feel free to use and modify the script to fit your environment and extend it as needed.

Simon

Cloud Engineer focused on Azure, Terraform & PowerShell. Passionate about automation, efficient solutions, and sharing real-world cloud projects and insights. ⸻ Let me know if you’d like to make it more casual, technical, or personalized.

Leave a Reply

Your email address will not be published. Required fields are marked *