Desafio 51: Arquitetura de Monitoramento de Segurança – Cenário de Detecção End-to-End
Habilidades do exame cobertas
- Projetar arquiteturas completas de monitoramento de segurança para ambientes corporativos
- Definir fontes de log e configurar data connectors
- Criar regras de análise (scheduled, NRT, fusion)
- Construir playbooks de automação com Logic Apps
- Projetar workbooks e dashboards de segurança
- Escrever consultas KQL para detecções comuns de ameaças
- Implementar estratégias de retenção de dados e otimização de custos
Cenário
A Contoso Ltd é uma empresa de serviços financeiros com 5.000 funcionários passando por uma transformação para a nuvem. Eles possuem uma infraestrutura híbrida com Active Directory on-premises, cargas de trabalho no Azure, Microsoft 365 e um footprint SaaS crescente. O CISO encarregou você de projetar e implementar uma arquitetura abrangente de monitoramento de segurança usando o Microsoft Sentinel.
Seu design deve detectar as seguintes categorias de ameaças:
- Ataques de força bruta contra contas de usuário
- Impossible travel em logins indicando roubo de credenciais
- Escalação de privilégios via atribuições de role não autorizadas
- Exfiltração de dados do SharePoint/OneDrive
- Indicadores de malware/ransomware em endpoints
- Abuso de recursos em nuvem (cryptomining, implantações não autorizadas)
Pré-requisitos
- Assinatura do Azure com role Owner ou Contributor
- Licenciamento Microsoft 365 E5 ou equivalente em segurança
- Azure CLI com extensões
sentinelemonitor - Role Microsoft Sentinel Contributor
- Role Logic Apps Contributor (para playbooks)
- Conhecimento de KQL e framework MITRE ATT&CK
Tarefa 1: Projetar a arquitetura de monitoramento e implantar o workspace
Defina a arquitetura de monitoramento de segurança da Contoso com design de workspace adequado.
# Architecture design variables
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
RG_NAME="rg-contoso-soc-architecture"
LOCATION="eastus"
WORKSPACE_NAME="law-contoso-soc"
# Create resource group
az group create --name $RG_NAME --location $LOCATION
# Create Log Analytics workspace with appropriate retention
az monitor log-analytics workspace create \
--workspace-name $WORKSPACE_NAME \
--resource-group $RG_NAME \
--location $LOCATION \
--retention-time 90 \
--sku PerGB2018
# Enable Sentinel
az sentinel onboarding-state create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--name "default"
# Get workspace ID for later use
WORKSPACE_ID=$(az monitor log-analytics workspace show \
--workspace-name $WORKSPACE_NAME \
--resource-group $RG_NAME \
--query id -o tsv)
# Configure data retention tiers
# Hot tier: 90 days (default interactive queries)
# Archive tier: 2 years (compliance requirement for financial services)
az monitor log-analytics workspace table update \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--name "SecurityEvent" \
--retention-time 90 \
--total-retention-time 730
az monitor log-analytics workspace table update \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--name "SigninLogs" \
--retention-time 90 \
--total-retention-time 730
az monitor log-analytics workspace table update \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--name "AuditLogs" \
--retention-time 90 \
--total-retention-time 730
Visão geral da arquitetura:
| Componente | Finalidade | Volume de Dados (est.) |
|---|---|---|
| Logs de Sign-in/Audit do Entra ID | Detecção de ameaças de identidade | ~2 GB/dia |
| Atividade do Microsoft 365 | Ameaças em Email/SharePoint/Teams | ~5 GB/dia |
| Logs de Azure Activity | Abuso de recursos em nuvem | ~500 MB/dia |
| Defender for Endpoint | Malware em endpoint/EDR | ~3 GB/dia |
| Defender for Cloud | Alertas de postura em nuvem | ~200 MB/dia |
| Diagnósticos do Azure Key Vault | Monitoramento de acesso a credenciais | ~100 MB/dia |
| Logs do Azure Firewall | Ameaças em nível de rede | ~1 GB/dia |
| AD on-premises (via AMA) | Monitoramento de identidade híbrida | ~1 GB/dia |
| Total estimado de ingestão | ~13 GB/dia |
Tarefa 2: Configurar data connectors para todas as fontes de log
Habilite os data connectors que alimentam a arquitetura de monitoramento da Contoso.
# Microsoft Entra ID connector (Sign-in and Audit logs)
az sentinel data-connector create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "AzureActiveDirectory" \
--aad \
--tenant-id $(az account show --query tenantId -o tsv) \
--data-types-alerts-state "Enabled" \
--data-types-sign-in-logs-state "Enabled" \
--data-types-audit-logs-state "Enabled"
# Microsoft Defender XDR connector
az sentinel data-connector create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "MicrosoftThreatProtection" \
--microsoft-threat-protection \
--tenant-id $(az account show --query tenantId -o tsv) \
--data-types-incidents-state "Enabled" \
--data-types-alerts-state "Enabled"
# Microsoft Defender for Cloud connector
az sentinel data-connector create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "AzureSecurityCenter" \
--asc \
--subscription-id $SUBSCRIPTION_ID \
--data-types-alerts-state "Enabled"
# Azure Activity connector
az sentinel data-connector create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "AzureActivity" \
--azure-activity \
--subscription-id $SUBSCRIPTION_ID \
--data-types-azure-activity-state "Enabled"
# Microsoft 365 connector (Exchange, SharePoint, Teams)
az sentinel data-connector create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--data-connector-id "Office365" \
--office365 \
--tenant-id $(az account show --query tenantId -o tsv) \
--data-types-exchange-state "Enabled" \
--data-types-share-point-state "Enabled" \
--data-types-teams-state "Enabled"
# Configure diagnostic settings for Key Vault
az monitor diagnostic-settings create \
--name "send-to-sentinel" \
--resource "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME/providers/Microsoft.KeyVault/vaults/kv-contoso-prod" \
--workspace $WORKSPACE_ID \
--logs '[{"category":"AuditEvent","enabled":true},{"category":"AllMetrics","enabled":false}]' \
2>/dev/null || echo "Key Vault diagnostic settings - configure after KV is created"
# Configure diagnostic settings for Azure Firewall
az monitor diagnostic-settings create \
--name "send-to-sentinel" \
--resource "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME/providers/Microsoft.Network/azureFirewalls/fw-contoso-hub" \
--workspace $WORKSPACE_ID \
--logs '[{"category":"AzureFirewallApplicationRule","enabled":true},{"category":"AzureFirewallNetworkRule","enabled":true},{"category":"AzureFirewallDnsProxy","enabled":true}]' \
2>/dev/null || echo "Firewall diagnostic settings - configure after FW is created"
Verificar status dos conectores:
# List all configured data connectors
az sentinel data-connector list \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--query "[].{Name:name, Kind:kind}" -o table
Tarefa 3: Criar regras de análise para detecção de ameaças
Implemente regras de detecção para cada categoria de ameaça nos requisitos da Contoso.
Detecção 1: Detecção de ataque de força bruta
az sentinel alert-rule create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--rule-id "detect-brute-force" \
--scheduled \
--name "Brute Force Attack Detected" \
--description "Detects multiple failed sign-in attempts followed by a success from the same IP" \
--severity "High" \
--enabled true \
--query "let failureThreshold = 10;
let timeWindow = 15m;
let successWindow = 1h;
// Find IPs with many failures
let bruteForceAttempts = SigninLogs
| where TimeGenerated > ago(successWindow)
| where ResultType != 0
| summarize
FailureCount = count(),
FailedAccounts = make_set(UserPrincipalName, 20),
FirstFailure = min(TimeGenerated),
LastFailure = max(TimeGenerated)
by IPAddress
| where FailureCount >= failureThreshold;
// Check if any succeeded after failures
let successfulLogins = SigninLogs
| where TimeGenerated > ago(successWindow)
| where ResultType == 0
| project SuccessTime=TimeGenerated, UserPrincipalName, IPAddress,
AppDisplayName, DeviceDetail, Location=LocationDetails;
bruteForceAttempts
| join kind=inner (successfulLogins) on IPAddress
| where SuccessTime > LastFailure
| project IPAddress, FailureCount, FailedAccounts,
CompromisedUser=UserPrincipalName, SuccessTime,
AppDisplayName, FirstFailure, LastFailure" \
--query-frequency "PT10M" \
--query-period "PT1H" \
--trigger-operator "GreaterThan" \
--trigger-threshold 0 \
--tactics "CredentialAccess" \
--techniques "T1110"
Detecção 2: Impossible travel
az sentinel alert-rule create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--rule-id "detect-impossible-travel" \
--scheduled \
--name "Impossible Travel Detected" \
--description "Detects sign-ins from geographically distant locations within an impossible timeframe" \
--severity "Medium" \
--enabled true \
--query "let maxTimeDiffMinutes = 60;
let maxDistanceKm = 500;
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType == 0
| extend City = tostring(LocationDetails.city),
State = tostring(LocationDetails.state),
Country = tostring(LocationDetails.countryOrRegion),
Latitude = toreal(LocationDetails.geoCoordinates.latitude),
Longitude = toreal(LocationDetails.geoCoordinates.longitude)
| where isnotempty(Latitude) and isnotempty(Longitude)
| sort by UserPrincipalName, TimeGenerated asc
| serialize
| extend PrevTime = prev(TimeGenerated, 1),
PrevLatitude = prev(Latitude, 1),
PrevLongitude = prev(Longitude, 1),
PrevCity = prev(City, 1),
PrevCountry = prev(Country, 1),
PrevUser = prev(UserPrincipalName, 1)
| where UserPrincipalName == PrevUser
| extend TimeDiffMinutes = datetime_diff('minute', TimeGenerated, PrevTime)
| where TimeDiffMinutes <= maxTimeDiffMinutes and TimeDiffMinutes > 0
// Haversine distance approximation
| extend DistanceKm = 6371 * acos(
sin(radians(Latitude)) * sin(radians(PrevLatitude)) +
cos(radians(Latitude)) * cos(radians(PrevLatitude)) *
cos(radians(PrevLongitude - Longitude)))
| where DistanceKm > maxDistanceKm
| project TimeGenerated, UserPrincipalName,
CurrentLocation=strcat(City, ', ', Country),
PreviousLocation=strcat(PrevCity, ', ', PrevCountry),
TimeDiffMinutes, DistanceKm, IPAddress" \
--query-frequency "PT15M" \
--query-period "PT2H" \
--trigger-operator "GreaterThan" \
--trigger-threshold 0 \
--tactics "InitialAccess" \
--techniques "T1078"
Detecção 3: Escalação de privilégios via atribuição de role não autorizada
az sentinel alert-rule create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--rule-id "detect-privilege-escalation" \
--nrt \
--name "Unauthorized Privileged Role Assignment (NRT)" \
--description "Near real-time detection of privileged role assignments outside PIM or approved processes" \
--severity "High" \
--enabled true \
--query "let privilegedRoles = dynamic([
'Global Administrator', 'Privileged Role Administrator',
'Security Administrator', 'Exchange Administrator',
'SharePoint Administrator', 'User Administrator',
'Application Administrator', 'Cloud Application Administrator']);
AuditLogs
| where TimeGenerated > ago(5m)
| where OperationName == 'Add member to role'
| extend TargetRole = tostring(TargetResources[0].displayName)
| where TargetRole in (privilegedRoles)
// Exclude PIM-activated assignments
| where OperationName != 'Add eligible member to role in PIM'
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName),
TargetUser = tostring(TargetResources[0].userPrincipalName),
InitiatedByIP = tostring(InitiatedBy.user.ipAddress)
// Exclude approved service accounts
| where InitiatedBy !in ('pim-service@contoso.com', 'identity-governance@contoso.com')
| project TimeGenerated, InitiatedBy, InitiatedByIP,
TargetUser, TargetRole, OperationName,
AdditionalDetails" \
--tactics "PrivilegeEscalation" "Persistence" \
--techniques "T1078.004" "T1098"
Detecção 4: Exfiltração de dados do SharePoint/OneDrive
az sentinel alert-rule create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--rule-id "detect-data-exfiltration" \
--scheduled \
--name "Anomalous Data Download from SharePoint/OneDrive" \
--description "Detects unusually large file downloads that may indicate data exfiltration" \
--severity "Medium" \
--enabled true \
--query "let lookback = 14d;
let threshold_multiplier = 3;
// Calculate baseline download volume per user
let baseline = OfficeActivity
| where TimeGenerated > ago(lookback) and TimeGenerated < ago(1d)
| where Operation in ('FileDownloaded', 'FileSyncDownloadedFull')
| where OfficeWorkload in ('SharePoint', 'OneDrive')
| summarize
AvgDailyDownloads = count() / 14.0,
AvgDailyBytes = sum(toint(OfficeObjectId)) / 14.0
by UserId;
// Today's activity
let today_activity = OfficeActivity
| where TimeGenerated > ago(1d)
| where Operation in ('FileDownloaded', 'FileSyncDownloadedFull')
| where OfficeWorkload in ('SharePoint', 'OneDrive')
| summarize
TodayDownloads = count(),
UniqueFiles = dcount(OfficeObjectId),
Sites = make_set(Site_Url, 10)
by UserId;
today_activity
| join kind=inner (baseline) on UserId
| where TodayDownloads > AvgDailyDownloads * threshold_multiplier
| where TodayDownloads > 50
| project UserId, TodayDownloads, AvgDailyDownloads,
AnomalyRatio = round(TodayDownloads / AvgDailyDownloads, 1),
UniqueFiles, Sites" \
--query-frequency "PT1H" \
--query-period "PT14D" \
--trigger-operator "GreaterThan" \
--trigger-threshold 0 \
--tactics "Exfiltration" \
--techniques "T1567"
Detecção 5: Abuso de recursos em nuvem (indicadores de cryptomining)
az sentinel alert-rule create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--rule-id "detect-cryptomining" \
--scheduled \
--name "Potential Cryptomining - Unusual VM Deployments" \
--description "Detects bulk VM creation or GPU VM deployments that may indicate cryptomining" \
--severity "High" \
--enabled true \
--query "let cryptoVmSizes = dynamic(['Standard_NC', 'Standard_ND', 'Standard_NV',
'Standard_HB', 'Standard_HC', 'Standard_F72s']);
// Detect bulk VM creation
let bulkDeployment = AzureActivity
| where TimeGenerated > ago(1h)
| where OperationNameValue == 'Microsoft.Compute/virtualMachines/write'
| where ActivityStatusValue == 'Success'
| summarize
VMCount = count(),
ResourceGroups = make_set(ResourceGroup, 5),
VMNames = make_set(Resource, 10)
by Caller, bin(TimeGenerated, 15m)
| where VMCount > 5;
// Detect GPU VM creation
let gpuDeployment = AzureActivity
| where TimeGenerated > ago(1h)
| where OperationNameValue == 'Microsoft.Compute/virtualMachines/write'
| where ActivityStatusValue == 'Success'
| where Properties_d has_any (cryptoVmSizes)
| project TimeGenerated, Caller, ResourceGroup, Resource,
VMSize = tostring(parse_json(tostring(Properties_d)).responseBody);
union
(bulkDeployment | extend DetectionType = 'BulkVMDeployment'),
(gpuDeployment | extend DetectionType = 'GPUVMDeployment')" \
--query-frequency "PT15M" \
--query-period "PT1H" \
--trigger-operator "GreaterThan" \
--trigger-threshold 0 \
--tactics "Impact" \
--techniques "T1496"
Tarefa 4: Construir playbooks de automação com Logic Apps
Crie um playbook Logic App que enriquece e responde a incidentes de força bruta.
# Create Logic App for brute-force response
az logic workflow create \
--resource-group $RG_NAME \
--name "playbook-brute-force-response" \
--location $LOCATION \
--definition '{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"contentVersion": "1.0.0.0",
"triggers": {
"Microsoft_Sentinel_incident": {
"type": "ApiConnectionWebhook",
"inputs": {
"body": {
"callback_url": "@{listCallbackUrl()}"
},
"host": {
"connection": {
"name": "@parameters($connections)['azuresentinel']['connectionId']"
}
},
"path": "/incident-creation"
}
}
},
"actions": {
"Get_incident_entities": {
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters($connections)['azuresentinel']['connectionId']"
}
},
"method": "post",
"path": "/entities/@{triggerBody()?['object']?['properties']?['relatedAnalyticRuleIds']}"
},
"runAfter": {}
},
"Block_IP_in_named_location": {
"type": "Http",
"inputs": {
"method": "POST",
"uri": "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations",
"body": {
"@odata.type": "#microsoft.graph.ipNamedLocation",
"displayName": "Auto-blocked: Brute force source",
"isTrusted": false,
"ipRanges": [
{
"@odata.type": "#microsoft.graph.iPv4CidrRange",
"cidrAddress": "@{body('Get_incident_entities')?['IPs']?[0]}/32"
}
]
}
},
"runAfter": {"Get_incident_entities": ["Succeeded"]}
},
"Send_Teams_notification": {
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters($connections)['teams']['connectionId']"
}
},
"method": "post",
"path": "/v1.0/teams/SOC-Alerts/channels/General/messages",
"body": {
"content": "🚨 Brute Force Alert: @{triggerBody()?['object']?['properties']?['title']} - Severity: @{triggerBody()?['object']?['properties']?['severity']}"
}
},
"runAfter": {"Block_IP_in_named_location": ["Succeeded"]}
},
"Add_comment_to_incident": {
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters($connections)['azuresentinel']['connectionId']"
}
},
"method": "post",
"path": "/comment",
"body": {
"incidentArmId": "@triggerBody()?['object']?['id']",
"message": "Automated response: Source IP blocked in Conditional Access. Teams notification sent to SOC channel."
}
},
"runAfter": {"Send_Teams_notification": ["Succeeded"]}
}
}
}
}'
Vincular o playbook a uma automation rule:
PLAYBOOK_ID=$(az logic workflow show \
--resource-group $RG_NAME \
--name "playbook-brute-force-response" \
--query id -o tsv)
az sentinel automation-rule create \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--automation-rule-id "auto-brute-force-response" \
--name "Auto-Respond to Brute Force" \
--order 1 \
--triggering-logic \
is-enabled=true \
triggers-on="Incidents" \
triggers-when="Created" \
conditions='[{
"conditionType": "Property",
"conditionProperties": {
"propertyName": "IncidentRelatedAnalyticRuleIds",
"operator": "Contains",
"propertyValues": ["detect-brute-force"]
}
}]' \
--actions '[{
"actionType": "RunPlaybook",
"order": 1,
"actionConfiguration": {
"logicAppResourceId": "'$PLAYBOOK_ID'",
"tenantId": "'$(az account show --query tenantId -o tsv)'"
}
}]'
Tarefa 5: Projetar workbooks e dashboards
Crie um workbook de operações de segurança para a equipe SOC.
Passos no Portal (workbooks são melhor criados no portal):
- Navegue até Microsoft Sentinel → Workbooks → + Add workbook
- Clique em Edit para entrar no modo de edição
- Adicione os seguintes componentes:
Dashboard de Operações SOC - Consultas KQL
Componente 1: Resumo de Incidentes (últimas 24h)
SecurityIncident
| where TimeGenerated > ago(24h)
| summarize
Total = count(),
Critical = countif(Severity == "High"),
Medium = countif(Severity == "Medium"),
Low = countif(Severity == "Low"),
Open = countif(Status == "New" or Status == "Active"),
Closed = countif(Status == "Closed")
| project Total, Critical, Medium, Low, Open, Closed
Componente 2: Tendência de alertas (7 dias)
SecurityAlert
| where TimeGenerated > ago(7d)
| summarize AlertCount = count() by bin(TimeGenerated, 1h), AlertSeverity
| render timechart
Componente 3: Usuários mais visados
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType != 0
| summarize FailedAttempts = count(),
UniqueIPs = dcount(IPAddress),
LastAttempt = max(TimeGenerated)
by UserPrincipalName
| top 10 by FailedAttempts desc
| project UserPrincipalName, FailedAttempts, UniqueIPs, LastAttempt
Componente 4: Mapa geográfico de ameaças
SigninLogs
| where TimeGenerated > ago(24h)
| where RiskLevelDuringSignIn in ('high', 'medium')
| extend Latitude = toreal(LocationDetails.geoCoordinates.latitude),
Longitude = toreal(LocationDetails.geoCoordinates.longitude),
Country = tostring(LocationDetails.countryOrRegion)
| where isnotempty(Latitude)
| summarize RiskySignIns = count() by Country, Latitude, Longitude
| top 20 by RiskySignIns desc
Componente 5: Cobertura MITRE ATT&CK
SecurityAlert
| where TimeGenerated > ago(30d)
| extend Tactics = parse_json(ExtendedProperties).Tactics
| mv-expand Tactic = Tactics
| summarize AlertCount = count() by tostring(Tactic)
| sort by AlertCount desc
| render barchart
Componente 6: Tempo Médio de Resposta (MTTR)
SecurityIncident
| where TimeGenerated > ago(30d)
| where Status == "Closed"
| extend CreatedTime = CreatedTime,
ClosedTime = ClosedTime
| extend MTTR_hours = datetime_diff('hour', ClosedTime, CreatedTime)
| summarize
AvgMTTR = avg(MTTR_hours),
MedianMTTR = percentile(MTTR_hours, 50),
P95_MTTR = percentile(MTTR_hours, 95)
by bin(TimeGenerated, 1d)
| render timechart
- Salve o workbook como "Contoso SOC Operations Dashboard"
- Fixe na página de visão geral do Sentinel
Tarefa 6: Implementar otimização de custos e gerenciamento de dados
Configure regras de coleta de dados e otimização de custos para operações sustentáveis.
# Create a Data Collection Rule (DCR) to filter noisy logs
az monitor data-collection rule create \
--resource-group $RG_NAME \
--name "dcr-contoso-filtering" \
--location $LOCATION \
--data-flows '[{
"streams": ["Microsoft-SecurityEvent"],
"destinations": ["law-contoso-soc"],
"transformKql": "source | where EventID in (4624, 4625, 4648, 4672, 4720, 4722, 4723, 4724, 4725, 4726, 4732, 4733, 4740, 4756, 4757, 4767, 4769, 4771, 4776, 5136, 5145)"
}]' \
--destinations '{
"logAnalytics": [{
"workspaceResourceId": "'$WORKSPACE_ID'",
"name": "law-contoso-soc"
}]
}'
# Configure Basic Logs tier for high-volume, low-priority tables
az monitor log-analytics workspace table update \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--name "ContainerLog" \
--plan "Basic"
az monitor log-analytics workspace table update \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--name "AppTraces" \
--plan "Basic"
# Set up daily cap as cost safeguard (10x normal to catch anomalies only)
az monitor log-analytics workspace update \
--workspace-name $WORKSPACE_NAME \
--resource-group $RG_NAME \
--set properties.workspaceCapping.dailyQuotaGb=130
Resumo de otimização de custos:
| Estratégia | Implementação | Economia |
|---|---|---|
| Filtragem por DCR | Ingerir apenas EventIDs relevantes para segurança | ~40% em SecurityEvent |
| Tier Basic Logs | Tabelas de baixa prioridade com custo reduzido | ~60% em logs de container/app |
| Tier Archive | Dados de compliance após 90 dias | ~80% vs. retenção interativa |
| Cap diário | Rede de segurança com 10x a ingestão normal | Previne surpresas na conta |
| Tier de compromisso | Compromisso de 100 GB/dia | ~20% de desconto |
Quebre & Conserte
Cenário 1: A regra de análise gera muitos falsos positivos
A regra de impossible travel dispara mais de 50 alertas diários, a maioria de usuários VPN que parecem se conectar do gateway VPN e de sua localização residencial simultaneamente.
Mostrar solução
Causa raiz: Conexões VPN criam eventos de login tanto da localização real do usuário quanto da localização do gateway VPN.
Correção: Adicione os IPs do gateway VPN a uma lista de exclusão na consulta KQL:
// Add this filter before the distance calculation
let vpnGatewayIPs = dynamic(["203.0.113.0/24", "198.51.100.0/24"]);
let trustedLocations = dynamic(["Contoso HQ", "Contoso DC"]);
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType == 0
// Exclude VPN and trusted locations
| where not(ipv4_is_in_any_range(IPAddress, vpnGatewayIPs))
| where not(tostring(LocationDetails.city) in (trustedLocations))
// ... rest of the impossible travel query
Considere também aumentar o maxTimeDiffMinutes de 60 para 90 para compensar a sobreposição de sessões.
Cenário 2: Playbook falha com erro "Forbidden"
O playbook de resposta a força bruta é acionado mas falha na etapa "Block IP" com erro 403 Forbidden.
Mostrar solução
Causa raiz: A managed identity do Logic App não possui permissões da Graph API para criar named locations.
Correção:
- Habilite a managed identity atribuída pelo sistema no Logic App:
az logic workflow identity assign \--resource-group $RG_NAME \--name "playbook-brute-force-response" \--system-assigned
- Conceda à managed identity a permissão
Policy.ReadWrite.ConditionalAccess:LOGIC_APP_OBJECT_ID=$(az logic workflow show \--resource-group $RG_NAME \--name "playbook-brute-force-response" \--query identity.principalId -o tsv)# Grant Graph API permission (requires admin consent)az ad app permission grant \--id $LOGIC_APP_OBJECT_ID \--api 00000003-0000-0000-c000-000000000000 \--scope "Policy.ReadWrite.ConditionalAccess" - Alternativamente, use uma API connection com uma conta de serviço que tenha a role Conditional Access Administrator
Cenário 3: Custos de ingestão de dados sobem inesperadamente
Os custos mensais saltam de $3.000 para $12.000 devido a um aumento repentino no volume de ingestão de logs.
Mostrar solução
Causa raiz: Uma configuração de diagnóstico mal feita está enviando logs verbosos de debug de todos os recursos.
Correção:
- Identifique a fonte do aumento de ingestão:
Usage| where TimeGenerated > ago(7d)| summarize IngestionGB = sum(Quantity) / 1000.0 by DataType, bin(TimeGenerated, 1d)| where IngestionGB > 1| sort by IngestionGB desc
- Encontre o recurso específico inundando logs:
AzureDiagnostics| where TimeGenerated > ago(1d)| summarize Count = count(), SizeGB = sum(_BilledSize) / (1024*1024*1024) by ResourceProvider, ResourceType| sort by SizeGB desc
- Remova ou corrija a configuração de diagnóstico incorreta
- Defina um cap diário como proteção imediata:
az monitor log-analytics workspace update \--workspace-name $WORKSPACE_NAME \--resource-group $RG_NAME \--set properties.workspaceCapping.dailyQuotaGb=20
- Implemente filtragem por DCR para o recurso ruidoso
Verificação de Conhecimento
1. Qual tipo de regra de análise do Sentinel é mais apropriado para detectar escalação de privilégios que requer alerta imediato em 1-2 minutos?
2. Qual função KQL é usada para calcular a distância geográfica entre duas localizações de login para detecção de impossible travel?
3. Para reduzir custos de ingestão em tabelas de alto volume mas raramente consultadas, qual plano do Log Analytics você deve usar?
4. Um playbook Logic App precisa bloquear um endereço IP no Conditional Access. Qual permissão a managed identity deve ter?
5. Em uma Data Collection Rule (DCR), qual é a finalidade da propriedade transformKql?
Limpeza
# Delete automation rules
az sentinel automation-rule delete \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--automation-rule-id "auto-brute-force-response" --yes
# Delete analytics rules
for RULE_ID in "detect-brute-force" "detect-impossible-travel" "detect-privilege-escalation" "detect-data-exfiltration" "detect-cryptomining"; do
az sentinel alert-rule delete \
--resource-group $RG_NAME \
--workspace-name $WORKSPACE_NAME \
--rule-id $RULE_ID --yes
done
# Delete Logic App playbook
az logic workflow delete \
--resource-group $RG_NAME \
--name "playbook-brute-force-response" --yes
# Delete the resource group (removes everything)
az group delete --name $RG_NAME --yes --no-wait