Azure Security Best Practices

Complete guide to implementing enterprise-grade security in Azure environments with Zero Trust principles and practical configurations

October 7, 2025 15 min read Sam Adhikari
Azure Security Architecture

As organizations increasingly migrate to Azure, implementing robust security measures becomes critical. This comprehensive guide covers essential Azure security best practices, from Identity and Access Management to advanced threat protection, helping you build a secure cloud infrastructure that meets enterprise standards.

Table of Contents

  1. 1. Identity and Access Management
  2. 2. Network Security Configuration
  3. 3. Data Protection and Encryption
  4. 4. Advanced Threat Protection
  5. 5. Security Monitoring and Compliance
  6. 6. Security Automation with PowerShell

1. Identity and Access Management

Azure AD Identity Management

Zero Trust Foundation

Identity is the new security perimeter. Implementing strong identity controls is the foundation of any secure Azure environment.

Conditional Access Policies

Conditional Access is Azure AD's way of bringing signals together, making decisions, and enforcing organizational policies.

# Create Conditional Access Policy for High-Risk Sign-ins
$policy = @{
    displayName = "Block High-Risk Sign-ins"
    state = "enabled"
    conditions = @{
        signInRiskLevels = @("high")
        applications = @{
            includeApplications = @("All")
        }
        users = @{
            includeUsers = @("All")
            excludeUsers = @("emergency-access-account@company.com")
        }
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("block")
    }
}

# Apply the policy using Microsoft Graph PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter $policy

Multi-Factor Authentication

Enable MFA for all users, especially administrators, to add an extra layer of security.

# Enable MFA for all users
$mfaPolicy = @{
    displayName = "Require MFA for All Users"
    state = "enabled"
    conditions = @{
        applications = @{
            includeApplications = @("All")
        }
        users = @{
            includeUsers = @("All")
            excludeUsers = @("emergency-access-account@company.com")
        }
    }
    grantControls = @{
        operator = "OR"
        builtInControls = @("mfa")
    }
}

New-MgIdentityConditionalAccessPolicy -BodyParameter $mfaPolicy

2. Network Security Configuration

Azure Network Security

Network security is crucial for protecting your Azure resources. Implement network segmentation, firewalls, and monitoring to create defense in depth.

Network Security Groups (NSGs)

# Create Network Security Group with restrictive rules
$resourceGroup = "rg-production"
$location = "East US"
$nsgName = "nsg-web-tier"

# Create NSG
$nsg = New-AzNetworkSecurityGroup -ResourceGroupName $resourceGroup -Location $location -Name $nsgName

# Allow HTTPS only
$httpsRule = New-AzNetworkSecurityRuleConfig -Name "Allow-HTTPS" -Description "Allow HTTPS" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 1000 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationAddressPrefix * -DestinationPortRange 443

# Deny all other traffic
$denyAllRule = New-AzNetworkSecurityRuleConfig -Name "Deny-All" -Description "Deny all other traffic" `
    -Access Deny -Protocol * -Direction Inbound -Priority 4000 `
    -SourceAddressPrefix * -SourcePortRange * `
    -DestinationAddressPrefix * -DestinationPortRange *

# Update NSG with rules
$nsg.SecurityRules.Add($httpsRule)
$nsg.SecurityRules.Add($denyAllRule)
Set-AzNetworkSecurityGroup -NetworkSecurityGroup $nsg

Security Warning

Always follow the principle of least privilege when configuring NSG rules. Start with deny-all and only allow necessary traffic.

Azure Firewall Configuration

# Deploy Azure Firewall with threat intelligence
$firewallName = "fw-hub-eastus"
$publicIPName = "pip-firewall"

# Create public IP for firewall
$publicIP = New-AzPublicIpAddress -ResourceGroupName $resourceGroup -Location $location `
    -Name $publicIPName -AllocationMethod Static -Sku Standard

# Create Azure Firewall
$firewall = New-AzFirewall -Name $firewallName -ResourceGroupName $resourceGroup `
    -Location $location -VirtualNetworkName "vnet-hub" -PublicIpName $publicIPName `
    -ThreatIntelMode "Alert"

# Configure application rules
$appRule = New-AzFirewallApplicationRule -Name "Allow-Microsoft-Services" `
    -Description "Allow access to Microsoft services" `
    -SourceAddress "10.0.0.0/8" `
    -TargetFqdn "*.microsoft.com","*.azure.com","*.office.com" `
    -Protocol "https:443"

$appRuleCollection = New-AzFirewallApplicationRuleCollection -Name "Microsoft-Services" `
    -Priority 100 -ActionType Allow -Rule $appRule

Set-AzFirewall -AzureFirewall $firewall

3. Data Protection and Encryption

Data Encryption Azure

Protecting data at rest and in transit is fundamental to Azure security. Implement encryption, key management, and data classification.

Azure Key Vault Implementation

# Create Azure Key Vault with advanced security features
$keyVaultName = "kv-prod-secrets-$(Get-Random)"
$resourceGroup = "rg-production"
$location = "East US"

# Create Key Vault with soft delete and purge protection
$keyVault = New-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $resourceGroup `
    -Location $location -EnableSoftDelete -EnablePurgeProtection `
    -EnabledForDiskEncryption -EnabledForTemplateDeployment

# Configure access policy for current user
$userObjectId = (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0]
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -ObjectId $userObjectId `
    -PermissionsToKeys all -PermissionsToSecrets all -PermissionsToCertificates all

# Create a key for disk encryption
$key = Add-AzKeyVaultKey -VaultName $keyVaultName -Name "DiskEncryptionKey" `
    -Destination Software -KeyOps encrypt,decrypt

Write-Output "Key Vault created: $($keyVault.VaultUri)"
Write-Output "Disk encryption key: $($key.Id)"

Disk Encryption

# Enable Azure Disk Encryption on VMs
$vmName = "vm-web-prod-01"
$keyVaultResourceId = "/subscriptions/{subscription-id}/resourceGroups/rg-production/providers/Microsoft.KeyVault/vaults/$keyVaultName"
$keyVaultUrl = "https://$keyVaultName.vault.azure.net/"

# Enable encryption for OS and data disks
Set-AzVMDiskEncryptionExtension -ResourceGroupName $resourceGroup -VMName $vmName `
    -DiskEncryptionKeyVaultUrl $keyVaultUrl -DiskEncryptionKeyVaultId $keyVaultResourceId `
    -VolumeType "All" -Force

# Verify encryption status
Get-AzVmDiskEncryptionStatus -ResourceGroupName $resourceGroup -VMName $vmName

Best Practice

Always enable encryption for both OS and data disks. Use customer-managed keys for sensitive workloads requiring additional control.

4. Advanced Threat Protection

Azure Security Center

Microsoft Defender for Cloud provides advanced threat protection across your Azure environment with AI-powered security analytics.

Enable Microsoft Defender for Cloud

# Enable Microsoft Defender for Cloud on subscription
$subscriptionId = (Get-AzContext).Subscription.Id

# Enable Defender for Servers
Set-AzSecurityPricing -Name "VirtualMachines" -PricingTier "Standard"

# Enable Defender for Storage
Set-AzSecurityPricing -Name "StorageAccounts" -PricingTier "Standard"

# Enable Defender for SQL
Set-AzSecurityPricing -Name "SqlServers" -PricingTier "Standard"

# Enable Defender for Key Vault
Set-AzSecurityPricing -Name "KeyVaults" -PricingTier "Standard"

# Configure security contacts
$securityContact = @{
    Email = "security-team@company.com"
    Phone = "+1-555-0123"
    AlertNotifications = "On"
    AlertsToAdmins = "On"
}

Set-AzSecurityContact @securityContact

Write-Output "Microsoft Defender for Cloud enabled for subscription: $subscriptionId"

Security Recommendations Automation

# Get and remediate security recommendations
$recommendations = Get-AzSecurityTask | Where-Object {$_.State -eq "Active"}

foreach ($recommendation in $recommendations) {
    Write-Output "Recommendation: $($recommendation.SecurityTaskName)"
    Write-Output "Severity: $($recommendation.SubState)"
    Write-Output "Resource: $($recommendation.ResourceId)"
    Write-Output "Description: $($recommendation.Description)"
    Write-Output "---"
}

# Auto-remediate specific recommendations
$highPriorityTasks = $recommendations | Where-Object {$_.SubState -eq "High"}

foreach ($task in $highPriorityTasks) {
    switch ($task.SecurityTaskName) {
        "Enable Network Security Groups" {
            # Implement NSG automation
            Write-Output "Implementing NSG for: $($task.ResourceId)"
        }
        "Enable disk encryption" {
            # Implement disk encryption automation
            Write-Output "Enabling encryption for: $($task.ResourceId)"
        }
        default {
            Write-Output "Manual review required for: $($task.SecurityTaskName)"
        }
    }
}

5. Security Monitoring and Compliance

Azure Monitor Dashboard

Continuous monitoring and compliance tracking are essential for maintaining security posture and meeting regulatory requirements.

Azure Sentinel Configuration

# Deploy Azure Sentinel workspace
$workspaceName = "law-sentinel-prod"
$sentinelResourceGroup = "rg-security"

# Create Log Analytics workspace for Sentinel
$workspace = New-AzOperationalInsightsWorkspace -ResourceGroupName $sentinelResourceGroup `
    -Name $workspaceName -Location $location -Sku "PerGB2018"

# Enable Sentinel on the workspace
$workspaceId = $workspace.ResourceId
$sentinelSolution = @{
    Name = "SecurityInsights($workspaceName)"
    ResourceGroupName = $sentinelResourceGroup
    Location = $location
    WorkspaceName = $workspaceName
}

# Configure data connectors
$connectors = @(
    "AzureActiveDirectory",
    "AzureSecurityCenter", 
    "AzureActivity",
    "SecurityEvents"
)

foreach ($connector in $connectors) {
    Write-Output "Configuring data connector: $connector"
    # Configure each connector based on requirements
}

Write-Output "Azure Sentinel deployed: $($workspace.PortalUrl)"

Security Alert Rules

# Create security alert rules in Sentinel
$alertRules = @(
    @{
        Name = "Suspicious Sign-in Activity"
        Query = @"
SigninLogs
| where TimeGenerated > ago(1h)
| where RiskLevelDuringSignIn == "high" or RiskLevelAggregated == "high"
| where ResultType == 0
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, Location, RiskDetail
"@
        Severity = "High"
        Frequency = "PT1H"
    },
    @{
        Name = "Failed Authentication Attempts"
        Query = @"
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType != 0
| summarize FailedAttempts = count() by UserPrincipalName, IPAddress
| where FailedAttempts > 5
"@
        Severity = "Medium"
        Frequency = "PT1H"
    }
)

foreach ($rule in $alertRules) {
    Write-Output "Creating alert rule: $($rule.Name)"
    # Create the alert rule using ARM template or REST API
    # This would typically involve creating an ARM template deployment
}

6. Security Automation with PowerShell

PowerShell Automation

Automate security tasks to ensure consistent application of security policies and rapid response to threats.

Automated Security Assessment

# Comprehensive Azure security assessment script
function Invoke-AzureSecurityAssessment {
    param(
        [string]$SubscriptionId,
        [string]$OutputPath = "./security-assessment.html"
    )
    
    # Set context
    Set-AzContext -SubscriptionId $SubscriptionId
    
    $assessment = @{
        SubscriptionId = $SubscriptionId
        AssessmentDate = Get-Date
        Findings = @()
    }
    
    # Check 1: Virtual Machines without encryption
    Write-Output "Checking VM disk encryption..."
    $vms = Get-AzVM
    foreach ($vm in $vms) {
        $encryptionStatus = Get-AzVmDiskEncryptionStatus -ResourceGroupName $vm.ResourceGroupName -VMName $vm.Name
        if ($encryptionStatus.OsVolumeEncrypted -ne "Encrypted") {
            $assessment.Findings += @{
                Type = "VM Disk Encryption"
                Severity = "High"
                Resource = $vm.Name
                Issue = "OS disk not encrypted"
                Recommendation = "Enable Azure Disk Encryption"
            }
        }
    }
    
    # Check 2: Storage accounts without encryption
    Write-Output "Checking storage account encryption..."
    $storageAccounts = Get-AzStorageAccount
    foreach ($account in $storageAccounts) {
        if (-not $account.Encryption.Services.Blob.Enabled) {
            $assessment.Findings += @{
                Type = "Storage Encryption"
                Severity = "High"
                Resource = $account.StorageAccountName
                Issue = "Blob encryption not enabled"
                Recommendation = "Enable storage service encryption"
            }
        }
    }
    
    # Check 3: Network Security Groups with permissive rules
    Write-Output "Checking NSG configurations..."
    $nsgs = Get-AzNetworkSecurityGroup
    foreach ($nsg in $nsgs) {
        $permissiveRules = $nsg.SecurityRules | Where-Object {
            $_.SourceAddressPrefix -eq "*" -and 
            $_.DestinationPortRange -eq "*" -and 
            $_.Access -eq "Allow"
        }
        if ($permissiveRules) {
            $assessment.Findings += @{
                Type = "Network Security"
                Severity = "Medium"
                Resource = $nsg.Name
                Issue = "Overly permissive NSG rules"
                Recommendation = "Implement principle of least privilege"
            }
        }
    }
    
    # Generate HTML report
    $html = @"



    Azure Security Assessment Report
    


    

Azure Security Assessment Report

Subscription: $($assessment.SubscriptionId)

Assessment Date: $($assessment.AssessmentDate)

Total Findings: $($assessment.Findings.Count)

"@ foreach ($finding in $assessment.Findings) { $cssClass = $finding.Severity.ToLower() $html += @"

$($finding.Type) - $($finding.Severity)

Resource: $($finding.Resource)

Issue: $($finding.Issue)

Recommendation: $($finding.Recommendation)

"@ } $html += "" $html | Out-File -FilePath $OutputPath -Encoding UTF8 Write-Output "Security assessment completed. Report saved to: $OutputPath" return $assessment } # Run the assessment $assessment = Invoke-AzureSecurityAssessment -SubscriptionId "your-subscription-id"

Conclusion

Implementing comprehensive Azure security requires a multi-layered approach covering identity, network, data, and application security. The practices and scripts outlined in this guide provide a solid foundation for building secure Azure environments.

Remember that security is an ongoing process. Regularly review and update your security configurations, monitor for new threats, and stay informed about the latest Azure security features and best practices.

Need Help with Azure Security?

Let's discuss how to implement these security best practices in your Azure environment.

Get in Touch
Back to Blog Home