- name
- azure-security-analyzer
- description
- Analyze Azure resource configurations against security best practices using Azure MCP bestpractices service. Produces per-resource security assessment with severity ratings and recommendations. Use during template generation before deployment confirmation.
- metadata
- {"argument-hint":"Resource types and their configurations from the ARM template","user-invocable":true}
# Azure Security Analyzer
Analyze Azure resource configurations against Microsoft security best practices and produce a per-resource security assessment report.
## When to Use
- During template generation (invoked by the template generator before deployment confirmation)
- To audit an existing ARM template for security gaps
- When user asks "is this secure?" or "check security" for a deployment
- Post-deployment security review
## Verification Integrity Rules (CRITICAL)
**Every claim in the security report MUST be verifiable against the ARM template.** Never fabricate, assume, or misrepresent security status.
### Rule 1: Cite Exact Evidence
Every "✅ Applied" status **MUST cite the exact ARM template property path and its value** that proves the control is in place. If you cannot point to a specific property in the template JSON, you cannot mark it as applied.
```markdown
# ✅ CORRECT — cites exact property
| HTTPS-only | 🔴 Critical | ✅ Applied | `properties.httpsOnly: true` | Explicitly set in template |
# ❌ WRONG — no evidence from template
| Disk encryption | 🔴 Critical | ✅ Applied | `managedDisk.storageAccountType` | This property is the performance tier, NOT encryption |
```
### Rule 2: Distinguish Explicit Config vs Platform Defaults
Azure provides some security controls by default (e.g., SSE at rest on managed disks). These are NOT the same as explicitly configured controls.
| Status | Icon | Meaning | When to Use |
|--------|------|---------|-------------|
| **✅ Applied** | ✅ | Explicitly configured in the ARM template | Property exists in template JSON with secure value |
| **🔄 Platform Default** | 🔄 | Azure provides this automatically, NOT in template | Control exists due to Azure platform behavior, not template config |
| **⚠️ Not applied** | ⚠️ | Control is missing and should be considered | Property absent from template, no platform default covers it |
| **❌ Misconfigured** | ❌ | Property exists but set to an insecure value | Property in template with wrong/insecure value |
```markdown
# ✅ CORRECT — distinguishes platform default from explicit config
| SSE at rest (managed disks) | 🟡 Medium | 🔄 Platform Default | Not in template | Azure encrypts all managed disks at rest with platform-managed keys automatically |
| Encryption at host (ADE) | 🟡 Medium | ⚠️ Not applied | `securityProfile.encryptionAtHost` absent | Not enabled — would encrypt temp disks and caches too |
# ❌ WRONG — claims explicit config for a platform default
| Managed disk encryption | 🔴 Critical | ✅ Applied | `managedDisk.storageAccountType` | WRONG: storageAccountType is performance tier, not encryption |
```
### Rule 3: Never Use Misleading Framing
Describe security status **accurately and literally**. Do not soften or reframe risks.
```markdown
# ❌ WRONG — misleading framing
| No open SSH to internet | 🔴 Critical | ✅ Applied |
# This is misleading: the VM HAS a public IP with port 22 open.
# IP-restriction reduces risk but port 22 IS internet-reachable.
# ✅ CORRECT — accurate framing
| SSH not open to 0.0.0.0/0 | 🔴 Critical | ✅ Applied | `sourceAddressPrefix: 175.x.x.x/32` | Restricted to single IP |
| VM is internet-facing (public IP + port 22) | 🟠 High | ⚠️ Risk accepted | Public IP attached to NIC | Port 22 reachable from internet (IP-restricted). Safer: Azure Bastion |
```
**Specific rules:**
- If a VM has a public IP → it IS internet-facing. Always flag this.
- If any port is open, even IP-restricted → state the port IS open and note the restriction as mitigation, not as "closed."
- If encryption is platform-default → say "platform default", not "applied."
- If a property is absent from the template → say "not configured", not "applied" based on assumptions.
### Rule 4: Verify Before Reporting
Before generating the security report, perform this verification checklist:
1. **For each "✅ Applied" entry**: Search the ARM template JSON for the cited property. If not found → change status.
2. **For each security claim**: Confirm the ARM property cited actually controls what you claim. (e.g., `storageAccountType` is NOT encryption)
3. **For network exposure**: Check if any `publicIPAddress` resource is attached to a NIC. If yes → VM is internet-facing, period.
4. **For encryption claims**: Distinguish between SSE (automatic), ADE (explicit extension), and encryption at host (explicit VM property).
5. **Cross-check property paths**: Use the correct ARM template property paths, not invented or approximate ones.
### Rule 5: When Uncertain, Mark as Unknown
If you cannot determine a security status with certainty:
```markdown
| {check} | {severity} | ❓ Unknown | {property} | Unable to verify — property path unclear or resource type not in checklist |
```
**Never guess. Never fabricate. When in doubt, flag it.**
## Procedure
### 1. Extract Resources from Template
Parse the ARM template or requirements to identify all resources and their configurations:
```markdown
Input: ARM template JSON or resource configuration list
Extract for each resource:
- Resource type (e.g., Microsoft.Storage/storageAccounts)
- Resource name
- All security-relevant properties
- Current configuration values
```
### 2. Query Azure MCP Best Practices (Per Resource)
**For EACH resource type**, query the Azure MCP `bestpractices` service to get security recommendations:
```
Tool: mcp_azure_mcp_search
Intent: "bestpractices {resource-type}"
Examples:
- "bestpractices Microsoft.Storage/storageAccounts"
- "bestpractices Microsoft.Web/sites"
- "bestpractices Microsoft.Sql/servers"
- "bestpractices Microsoft.DocumentDB/databaseAccounts"
- "bestpractices Microsoft.KeyVault/vaults"
- "bestpractices Microsoft.ContainerApp/containerApps"
```
Parse the MCP response for:
- Security recommendations with severity levels
- Configuration best practices
- Compliance alignment notes (CIS, SOC2, PCI-DSS)
- Microsoft Defender for Cloud recommendations
### 3. Check Resource-Specific Security Properties
Cross-reference MCP recommendations against the template configuration.
**Storage Accounts (Microsoft.Storage/storageAccounts):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | HTTPS-only transfer | `supportsHttpsTrafficOnly` | 🔴 Critical | `true` |
| 2 | TLS 1.2 minimum | `minimumTlsVersion` | 🔴 Critical | `TLS1_2` |
| 3 | Disable public blob access | `allowBlobPublicAccess` | 🟠 High | `false` |
| 4 | Blob soft delete | `deleteRetentionPolicy.enabled` | 🟠 High | `true` |
| 5 | Container soft delete | `containerDeleteRetentionPolicy.enabled` | 🟡 Medium | `true` |
| 6 | Disable shared key access | `allowSharedKeyAccess` | � High | `false` |
| 7 | Network rules / firewall | `networkAcls.defaultAction` | 🟡 Medium | `Deny` (prod) |
| 8 | Private endpoint | Private endpoint resource | 🟡 Medium | Configured (prod) |
| 9 | Infrastructure encryption | `encryption.requireInfrastructureEncryption` | 🔵 Low | `true` |
| 10 | Immutability policy | `immutableStorageWithVersioning` | 🔵 Low | Enabled (compliance) |
**Function Apps / App Services (Microsoft.Web/sites):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | HTTPS-only | `httpsOnly` | 🔴 Critical | `true` |
| 2 | TLS 1.2 minimum | `siteConfig.minTlsVersion` | 🔴 Critical | `1.2` |
| 3 | Managed identity | `identity.type` | 🔴 Critical | `SystemAssigned` |
| 4 | Identity-based storage access | `AzureWebJobsStorage__accountName` (not `AzureWebJobsStorage`) | 🔴 Critical | Identity-based |
| 5 | RBAC for storage | Role assignments for MI → Storage | 🔴 Critical | Storage Blob Data Owner + Storage Account Contributor |
| 6 | FTP disabled | `siteConfig.ftpsState` | 🟠 High | `Disabled` |
| 7 | Remote debugging off | `siteConfig.remoteDebuggingEnabled` | 🟠 High | `false` |
| 8 | Latest runtime version | `siteConfig.linuxFxVersion` or `netFrameworkVersion` | 🟠 High | Latest stable |
| 9 | App Insights connected | `APPINSIGHTS_INSTRUMENTATIONKEY` in appSettings | 🟡 Medium | Set |
| 8 | Health check enabled | `siteConfig.healthCheckPath` | 🟡 Medium | Set |
| 9 | CORS not wildcard | `siteConfig.cors.allowedOrigins` | 🟡 Medium | Not `*` |
| 10 | VNet integration | `virtualNetworkSubnetId` | 🟡 Medium | Configured (prod) |
| 11 | IP restrictions | `siteConfig.ipSecurityRestrictions` | 🔵 Low | Configured |
| 12 | HTTP/2 enabled | `siteConfig.http20Enabled` | 🔵 Low | `true` |
**SQL Servers (Microsoft.Sql/servers):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | TDE enabled | `transparentDataEncryption.status` | 🔴 Critical | `Enabled` |
| 2 | AAD-only auth | `administrators.azureADOnlyAuthentication` | 🔴 Critical | `true` |
| 3 | Minimal TLS 1.2 | `minimalTlsVersion` | 🔴 Critical | `1.2` |
| 4 | Auditing enabled | `auditingSettings.state` | 🟠 High | `Enabled` |
| 5 | Advanced threat protection | `securityAlertPolicies.state` | 🟠 High | `Enabled` |
| 6 | Firewall rules restrictive | `firewallRules` | 🟠 High | No `0.0.0.0/0` (prod) |
| 7 | Vulnerability assessment | `vulnerabilityAssessments` | 🟡 Medium | Enabled |
| 8 | Private endpoint | Private endpoint resource | 🟡 Medium | Configured (prod) |
| 9 | Long-term backup retention | `backupLongTermRetentionPolicies` | 🟡 Medium | Configured |
| 10 | Connection policy | `connectionPolicies.connectionType` | 🔵 Low | `Redirect` |
**Cosmos DB (Microsoft.DocumentDB/databaseAccounts):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | Firewall configured | `ipRules` or `virtualNetworkRules` | 🔴 Critical | Not empty |
| 2 | Disable key-based metadata writes | `disableKeyBasedMetadataWriteAccess` | 🟠 High | `true` |
| 3 | RBAC-based access | `disableLocalAuth` | 🟠 High | `true` (prefer RBAC) |
| 4 | Continuous backup | `backupPolicy.type` | 🟡 Medium | `Continuous` |
| 5 | Customer-managed keys | `keyVaultKeyUri` | 🟡 Medium | Set (prod) |
| 6 | Diagnostic logging | Diagnostic setting resource | 🟡 Medium | Enabled |
| 7 | Private endpoint | Private endpoint resource | 🟡 Medium | Configured (prod) |
**Key Vault (Microsoft.KeyVault/vaults):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | Soft delete enabled | `enableSoftDelete` | 🔴 Critical | `true` (now default) |
| 2 | Purge protection | `enablePurgeProtection` | 🔴 Critical | `true` |
| 3 | RBAC authorization | `enableRbacAuthorization` | 🟠 High | `true` |
| 4 | Network rules | `networkAcls.defaultAction` | 🟡 Medium | `Deny` (prod) |
| 5 | Private endpoint | Private endpoint resource | 🟡 Medium | Configured (prod) |
| 6 | Diagnostic logging | Diagnostic setting resource | 🟡 Medium | Enabled |
**Container Apps (Microsoft.App/containerApps):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | Ingress HTTPS only | `ingress.transport` | 🔴 Critical | `http` with `allowInsecure: false` |
| 2 | Managed identity | `identity.type` | 🟠 High | `SystemAssigned` |
| 3 | Min replicas > 0 (prod) | `scale.minReplicas` | 🟡 Medium | `> 0` for prod |
| 4 | VNet integration | Container Apps Environment | 🟡 Medium | Configured |
| 5 | Secret management | `secrets` with Key Vault refs | 🟡 Medium | Key Vault references |
**Virtual Machines (Microsoft.Compute/virtualMachines):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | Password authentication disabled | `osProfile.linuxConfiguration.disablePasswordAuthentication` | 🔴 Critical | `true` |
| 2 | SSH key configured | `osProfile.linuxConfiguration.ssh.publicKeys` | 🔴 Critical | At least one key |
| 3 | Internet exposure (public IP) | Check if NIC references `publicIPAddress` | 🟠 High | No public IP (use Bastion). If public IP present → flag as internet-facing |
| 4 | NSG SSH not open to 0.0.0.0/0 | NSG `securityRules[].sourceAddressPrefix` for port 22 | 🔴 Critical | Not `*` or `Internet` — must be specific IP/CIDR |
| 5 | Encryption at host | `securityProfile.encryptionAtHost` | 🟡 Medium | `true` (encrypts temp disks + caches) |
| 6 | Azure Disk Encryption (ADE) | ADE VM extension resource | 🟡 Medium | Extension present |
| 7 | SSE at rest (managed disks) | Platform default — NOT a template property | 🔵 Info | 🔄 Platform Default — Azure encrypts all managed disks automatically. Do NOT mark as "✅ Applied" |
| 8 | Boot diagnostics | `diagnosticsProfile.bootDiagnostics.enabled` | 🟡 Medium | `true` |
| 9 | Managed identity | `identity.type` | 🟡 Medium | `SystemAssigned` (if VM accesses Azure resources) |
| 10 | Automatic OS updates | `osProfile.linuxConfiguration.patchSettings.patchMode` | 🔵 Low | `AutomaticByPlatform` |
| 11 | Trusted launch | `securityProfile.securityType` | 🟡 Medium | `TrustedLaunch` with vTPM and secure boot |
**⚠️ VM Internet Exposure Rule:** If ANY `Microsoft.Network/publicIPAddresses` resource is attached to the VM's NIC, the VM IS internet-facing. Always flag this explicitly, even if NSG rules restrict source IPs. An IP-restricted port is still internet-reachable — the restriction is a mitigation, not a closure.
**Network Security Groups (Microsoft.Network/networkSecurityGroups):**
| # | Check | Property | Severity | Secure Value |
|---|-------|----------|----------|--------------|
| 1 | No rules with source `*` or `Internet` on management ports (22, 3389) | `securityRules[].sourceAddressPrefix` | 🔴 Critical | Specific IP/CIDR only |
View on GitHub