Back to Blog
Azure
4 min read

Azure Security Assessment Recommendations Checklist

AzureSecurityComplianceAssessmentBest Practices

After running security assessments on many Azure environments, patterns emerge. Here are the most common issues we find and how to address them.

Storage Account Issues

Public Blob Access Enabled

Risk: Containers can be accidentally made public, exposing data.

Check:

Get-AzStorageAccount | ForEach-Object {
  [PSCustomObject]@{
    Name = $_.StorageAccountName
    AllowBlobPublicAccess = $_.AllowBlobPublicAccess
  }
}

Fix:

resource "azurerm_storage_account" "this" {
  allow_nested_items_to_be_public = false
}

No Soft Delete

Risk: Accidental deletion is permanent.

Check:

$ctx = (Get-AzStorageAccount -ResourceGroupName "rg" -Name "storage").Context
Get-AzStorageBlobServiceProperty -StorageAccount $ctx |
  Select-Object DeleteRetentionPolicy

Fix: Enable 30-day soft delete for blobs and containers.

HTTP Traffic Allowed

Risk: Data can be intercepted in transit.

Check: Look for enable_https_traffic_only = false or missing.

Fix: Always set https_traffic_only_enabled = true.

Key Vault Issues

No Purge Protection

Risk: Deleted secrets are gone forever; ransomware can destroy your keys.

Check:

Get-AzKeyVault | Select-Object VaultName, EnablePurgeProtection

Fix:

resource "azurerm_key_vault" "this" {
  purge_protection_enabled = true
  soft_delete_retention_days = 90
}

Public Network Access

Risk: Secrets accessible from internet.

Check: Look for key vaults without network rules.

Fix: Use private endpoints or IP-based access rules.

SQL Server Issues

Public Endpoint Enabled

Risk: Database accessible from internet (even with firewall).

Check:

Get-AzSqlServer | ForEach-Object {
  $rules = Get-AzSqlServerFirewallRule -ServerName $_.ServerName -ResourceGroupName $_.ResourceGroupName
  [PSCustomObject]@{
    Server = $_.ServerName
    AllowAzureIPs = ($rules | Where-Object { $_.FirewallRuleName -eq "AllowAllWindowsAzureIps" }) -ne $null
    PublicRules = ($rules | Where-Object { $_.StartIpAddress -ne "0.0.0.0" }).Count
  }
}

Fix: Use private endpoints and disable public access.

No Azure AD Admin

Risk: Only SQL authentication available.

Check:

Get-AzSqlServerActiveDirectoryAdministrator -ServerName "sql-server" -ResourceGroupName "rg"

Fix: Set an Azure AD admin and use Azure AD authentication.

App Service Issues

HTTPS Not Enforced

Risk: Users can access over unencrypted HTTP.

Check:

Get-AzWebApp | Select-Object Name, HttpsOnly

Fix:

resource "azurerm_linux_web_app" "this" {
  https_only = true
}

TLS 1.0/1.1 Enabled

Risk: Weak encryption protocols.

Check:

Get-AzWebApp | ForEach-Object {
  $config = Get-AzWebAppConfiguration -ResourceGroupName $_.ResourceGroup -Name $_.Name
  [PSCustomObject]@{
    Name = $_.Name
    MinTlsVersion = $config.MinTlsVersion
  }
}

Fix: Set minimum TLS to 1.2.

FTP Enabled

Risk: Insecure deployment method.

Check: Look for ftps_state = "AllAllowed".

Fix: Set ftps_state = "Disabled" or "FtpsOnly".

Virtual Machine Issues

No Disk Encryption

Risk: Data at rest unprotected.

Check:

Get-AzVM | ForEach-Object {
  $status = Get-AzVmDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name
  [PSCustomObject]@{
    VM = $_.Name
    OsDiskEncrypted = $status.OsVolumeEncrypted
    DataDisksEncrypted = $status.DataVolumesEncrypted
  }
}

Fix: Enable Azure Disk Encryption or use platform-managed encryption.

Public IP Attached

Risk: Direct internet exposure.

Check:

Get-AzVM | ForEach-Object {
  $nic = Get-AzNetworkInterface -ResourceId $_.NetworkProfile.NetworkInterfaces[0].Id
  $pip = $nic.IpConfigurations[0].PublicIpAddress
  [PSCustomObject]@{
    VM = $_.Name
    HasPublicIP = $pip -ne $null
  }
}

Fix: Use Azure Bastion, VPN, or Azure Firewall instead.

Network Issues

NSG Flow Logs Disabled

Risk: No visibility into network traffic.

Check:

Get-AzNetworkWatcher | Get-AzNetworkWatcherFlowLog

Fix: Enable NSG flow logs to Storage or Log Analytics.

Overly Permissive NSG Rules

Risk: Unnecessary network exposure.

Check: Look for rules with:

  • Source: Any/0.0.0.0/0
  • Port range: * or large ranges

Fix: Restrict to specific IPs and ports.

Identity Issues

No Conditional Access

Risk: Access from any device/location.

Check: Review Conditional Access policies in Entra admin center.

Fix: Implement basic policies:

  • Block legacy authentication
  • Require MFA for admins
  • Require compliant devices for corporate apps

Global Admins Without MFA

Risk: Complete tenant compromise from password theft.

Check:

Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'" |
  Get-MgDirectoryRoleMember

Fix: Enforce MFA for all admin roles.

Quick Assessment Script

# Storage accounts
Get-AzStorageAccount | Where-Object { $_.AllowBlobPublicAccess -eq $true } |
  Select-Object StorageAccountName

# Key Vaults without purge protection
Get-AzKeyVault | Where-Object { $_.EnablePurgeProtection -ne $true } |
  Select-Object VaultName

# App Services without HTTPS
Get-AzWebApp | Where-Object { $_.HttpsOnly -ne $true } |
  Select-Object Name

# VMs with public IPs
Get-AzPublicIpAddress | Where-Object { $_.IpConfiguration -ne $null } |
  Select-Object Name, IpAddress

Need a security assessment of your Azure environment? Get in touch - we help organisations identify and fix security gaps.

Need help with your Azure environment?

Get in touch for a free consultation.

Get in Touch