Skip to content
Back to Blog
1 min read

Azure Security Center and Microsoft Defender: Unified Cloud Security

I wrote “Azure Security Center and Microsoft Defender: Unified Cloud Security” to share practical, production-minded guidance on this topic.

Understanding the Components

Microsoft Defender for Cloud includes:

  • CSPM (Cloud Security Posture Management): Free tier with security recommendations
  • CWP (Cloud Workload Protection): Paid plans for threat detection

Defender plans available:

  • Defender for Servers
  • Defender for App Service
  • Defender for Storage
  • Defender for SQL
  • Defender for Kubernetes
  • Defender for Container Registries
  • Defender for Key Vault

Enabling Defender Plans

# Enable Defender for Servers
az security pricing create \
    --name VirtualMachines \
    --tier Standard

# Enable Defender for Storage
az security pricing create \
    --name StorageAccounts \
    --tier Standard

# Enable Defender for SQL
az security pricing create \
    --name SqlServers \
    --tier Standard

# Enable Defender for Kubernetes
az security pricing create \
    --name KubernetesService \
    --tier Standard

# Check enabled plans
az security pricing list --output table

Security Policies and Initiatives

Apply security policies via Azure Policy:

# Assign Azure Security Benchmark initiative
az policy assignment create \
    --name "Azure Security Benchmark" \
    --scope /subscriptions/<subscription-id> \
    --policy-set-definition /providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8

# Create custom security policy
az policy definition create \
    --name "Require-SQL-TDE" \
    --display-name "Require TDE on SQL Databases" \
    --description "Ensures Transparent Data Encryption is enabled" \
    --rules '{
        "if": {
            "allOf": [
                {
                    "field": "type",
                    "equals": "Microsoft.Sql/servers/databases"
                },
                {
                    "field": "Microsoft.Sql/servers/databases/transparentDataEncryption.status",
                    "notEquals": "Enabled"
                }
            ]
        },
        "then": {
            "effect": "deny"
        }
    }'

Working with Recommendations

Query recommendations programmatically:

from azure.identity import DefaultAzureCredential
from azure.mgmt.security import SecurityCenter

credential = DefaultAzureCredential()
client = SecurityCenter(credential, subscription_id, asc_location="centralus")

# Get all recommendations
recommendations = client.recommendations.list()

for rec in recommendations:
    if rec.state == "Unhealthy":
        print(f"Resource: {rec.resource_details.id}")
        print(f"Recommendation: {rec.display_name}")
        print(f"Severity: {rec.severity}")
        print(f"Remediation: {rec.remediation_description}")
        print("---")

# Get recommendation by ID
rec = client.recommendations.get(
    resource_id="/subscriptions/.../resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm1",
    recommendation_id="recommendation-guid"
)

Automated Remediation

Create workflow automation for auto-remediation:

{
  "definition": {
    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
    "triggers": {
      "When_an_Azure_Security_Center_Recommendation_is_created_or_triggered": {
        "type": "ApiConnectionWebhook",
        "inputs": {
          "body": {
            "callback_url": "@{listCallbackUrl()}"
          },
          "host": {
            "connection": {
              "name": "@parameters('$connections')['ascassessment']['connectionId']"
            }
          },
          "path": "/Microsoft.Security/Recommendations/subscribe"
        }
      }
    },
    "actions": {
      "Condition_Check_Recommendation_Type": {
        "type": "If",
        "expression": {
          "and": [
            {
              "equals": [
                "@triggerBody()?['properties']?['displayName']",
                "Storage account should use a private link connection"
              ]
            }
          ]
        },
        "actions": {
          "Enable_Private_Endpoint": {
            "type": "ApiConnection",
            "inputs": {
              "host": {
                "connection": {
                  "name": "@parameters('$connections')['arm']['connectionId']"
                }
              },
              "method": "put",
              "path": "/subscriptions/@{triggerBody()?['properties']?['resourceDetails']?['id']}/providers/Microsoft.Network/privateEndpoints/storage-pe",
              "body": {
                "location": "eastus",
                "properties": {
                  "privateLinkServiceConnections": [
                    {
                      "name": "storage-connection",
                      "properties": {
                        "privateLinkServiceId": "@{triggerBody()?['properties']?['resourceDetails']?['id']}",
                        "groupIds": ["blob"]
                      }
                    }
                  ],
                  "subnet": {
                    "id": "/subscriptions/.../subnets/private-endpoints"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Defender for Servers

Configure Defender for Servers features:

# Enable auto-provisioning of Log Analytics agent
az security auto-provisioning-setting update \
    --name default \
    --auto-provision On

# Configure vulnerability assessment
az security va sql baseline set \
    --resource-group my-rg \
    --server-name my-sql-server \
    --database-name my-database \
    --workspace-id /subscriptions/.../workspaces/security-workspace

Query server vulnerabilities:

// Find VMs with critical vulnerabilities
SecurityRecommendation
| where RecommendationDisplayName contains "vulnerability"
| where RecommendationState == "Unhealthy"
| extend Severity = tostring(parse_json(ExtendedProperties).Severity)
| where Severity == "Critical"
| project
    ResourceId,
    RecommendationDisplayName,
    Severity,
    RemediationDescription

Defender for Containers

Secure your Kubernetes workloads:

# Enable Defender for AKS
az aks update \
    --name my-aks-cluster \
    --resource-group my-rg \
    --enable-defender

# Check Defender profile
az aks show \
    --name my-aks-cluster \
    --resource-group my-rg \
    --query securityProfile

Monitor container security:

// Container security alerts
SecurityAlert
| where ProviderName == "Azure Security Center"
| where ResourceType == "Kubernetes Service"
| project
    TimeGenerated,
    AlertName,
    AlertSeverity,
    Description,
    RemediationSteps,
    Entities
| order by TimeGenerated desc

Just-In-Time VM Access

Configure JIT access for secure VM management:

from azure.mgmt.security import SecurityCenter

# Get JIT policies
jit_policies = client.jit_network_access_policies.list()

# Create JIT policy
policy = {
    "kind": "Basic",
    "properties": {
        "virtualMachines": [
            {
                "id": "/subscriptions/.../virtualMachines/secure-vm",
                "ports": [
                    {
                        "number": 22,
                        "protocol": "TCP",
                        "allowedSourceAddressPrefix": "*",
                        "maxRequestAccessDuration": "PT3H"
                    },
                    {
                        "number": 3389,
                        "protocol": "TCP",
                        "allowedSourceAddressPrefix": "*",
                        "maxRequestAccessDuration": "PT3H"
                    }
                ]
            }
        ]
    }
}

client.jit_network_access_policies.create_or_update(
    resource_group_name="my-rg",
    asc_location="centralus",
    jit_network_access_policy_name="default",
    body=policy
)

# Request JIT access
request = {
    "virtualMachines": [
        {
            "id": "/subscriptions/.../virtualMachines/secure-vm",
            "ports": [
                {
                    "number": 22,
                    "allowedSourceAddressPrefix": "203.0.113.10",
                    "endTimeUtc": "2021-05-18T15:00:00Z"
                }
            ]
        }
    ]
}

client.jit_network_access_policies.initiate(
    resource_group_name="my-rg",
    asc_location="centralus",
    jit_network_access_policy_name="default",
    body=request
)

Security Score

Track and improve your security score:

# Get secure score
secure_scores = client.secure_scores.list()

for score in secure_scores:
    print(f"Score: {score.current_score}/{score.max_score}")
    print(f"Percentage: {score.percentage}%")

# Get score by control
controls = client.secure_score_controls.list()

for control in controls:
    print(f"Control: {control.display_name}")
    print(f"Score: {control.current_score}/{control.max_score}")
    print(f"Unhealthy resources: {control.unhealthy_resource_count}")

Continuous Export

Export security data to external systems:

# Export to Log Analytics
az security automation create \
    --name "export-to-la" \
    --resource-group security-rg \
    --scopes '[{"scopePath": "/subscriptions/<subscription-id>"}]' \
    --sources '[
        {"eventSource": "Alerts"},
        {"eventSource": "Recommendations"},
        {"eventSource": "SecureScores"}
    ]' \
    --actions '[{
        "actionType": "LogicApp",
        "logicAppResourceId": "/subscriptions/.../logicApps/export-to-la",
        "uri": "https://..."
    }]'

# Export to Event Hub
az security automation create \
    --name "export-to-eventhub" \
    --resource-group security-rg \
    --scopes '[{"scopePath": "/subscriptions/<subscription-id>"}]' \
    --sources '[{"eventSource": "Alerts"}]' \
    --actions '[{
        "actionType": "EventHub",
        "eventHubResourceId": "/subscriptions/.../eventhubs/security-alerts",
        "connectionString": "..."
    }]'

Resources

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.