Back to Blog
5 min read

Azure Security Center and Microsoft Defender: Unified Cloud Security

Azure Security Center (now part of Microsoft Defender for Cloud) provides unified security management and threat protection across hybrid cloud workloads. Let’s explore how to configure and leverage its capabilities.

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.