Governare non è scrivere una regola: è scegliere un effetto che si comporta in un modo preciso e poi dimostrare a un auditor che la regola è viva. Azure Policy fa la prima parte, Defender for Cloud la seconda.
L’effetto decide che cosa succede davvero
Ogni definizione ha un solo effetto. Quelli supportati sono addToNetworkGroup, append, audit, auditIfNotExists, deny, denyAction, deployIfNotExists, disabled, manual, modify e mutate. L’ordine di valutazione spiega comportamenti che altrimenti sembrano bug: prima disabled, poi append e modify — che possono cambiare la richiesta e quindi disinnescare un deny successivo — poi deny, audit, manual, auditIfNotExists, e per ultimo denyAction. Dopo che il resource provider ha risposto con successo, auditIfNotExists e deployIfNotExists rivalutano.
Il punto che si sbaglia più spesso: deny interviene sulle richieste di creazione e aggiornamento. Le risorse esistenti non vengono bloccate né rimosse, diventano non conformi. Se devi rimediare sul parco esistente ti serve un altro effetto, non un deny più severo.
Più assegnazioni sulla stessa risorsa si sommano in modo cumulativo e più restrittivo: ognuna è valutata a sé, quindi due deny in conflitto bloccano la risorsa, e la soluzione è guardare le esclusioni delle assegnazioni, non riscrivere la regola.
Scrivere una definizione: la parte difficile è l’alias
Il metodo: requisito, proprietà della risorsa, alias, effetto, definizione. La proprietà si trova esportando il template di una risorsa già configurata come vuoi tu, dal link “Download a template for automation” del wizard, o da Resource Explorer. Poi la proprietà va mappata su un alias, altrimenti la policy non la può leggere:
az provider show --namespace Microsoft.Storage --expand "resourceTypes/aliases" --query "resourceTypes[].aliases[].name"
(Get-AzPolicyAlias -NamespaceMatch 'Microsoft.Storage').Aliases
mode riguarda soprattutto tag e posizione: con all valuti anche le risorse che non supportano i tag. E parametrizza l’effetto, così la stessa definizione si assegna in audit dove misuri e in deny dove blocchi:
"parameters": { "effectType": { "allowedValues": [ "Deny", "Disabled" ] } },
"policyRule": {
"if": { "field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
"notEquals": "true" },
"then": { "effect": "[parameters('effectType')]" } }
Iniziative: il formato che usa la conformità normativa
Un’iniziativa raggruppa definizioni e le assegni come un oggetto solo. Ogni elemento porta policyDefinitionId, un policyDefinitionReferenceId, i parametri passati dall’iniziativa alla singola definizione, e opzionalmente definitionVersion: se lo lasci fuori, l’assegnazione segue l’ultima versione major e assorbe da sola le minor; se scrivi 1.2.* congeli il comportamento.
Due dettagli che si pagano dopo. I parametri di iniziativa non si cambiano più una volta assegnata, quindi metti sempre un defaultValue. E policyDefinitionGroups con groupNames non è decorazione: è la struttura che la funzione Regulatory Compliance usa per mappare le definizioni sui controlli e sui domini di conformità. Per un’iniziativa di conformità normativa la category nei metadata deve essere “Regulatory Compliance”, e gli oggetti policyMetadata che descrivono i controlli sono in sola lettura: li crea solo Microsoft.
Standard e conformità in Defender for Cloud
Quando abiliti Defender for Cloud, il Microsoft Cloud Security Benchmark è già assegnato: su AWS si aggiunge AWS Foundational Security Best Practices, su GCP il GCP Default. Gli standard normativi qui sono iniziative di Azure Policy: tutto quanto sopra vale anche qui.
Per aggiungerne uno il percorso è questo, e lo scope può essere una sottoscrizione o un management group, un account AWS o un progetto GCP:
Defender for Cloud > Regulatory compliance > Manage compliance policies > scope > Security policies > standard: On
Serve un piano Defender onboardato (qualunque, tranne Defender for Servers Plan 1 e Defender for API Plan 1) e il ruolo Owner o Policy Contributor. Assegna allo scope più alto applicabile, così i dati si aggregano.
Le tre cose che sorprendono: uno standard assegnato a uno scope senza risorse pertinenti non compare nel dashboard; i controlli non valutabili automaticamente restano grigi e vanno attestati a mano allegando evidenza; gli assessment girano circa ogni 12 ore, quindi dopo una remediation il numero non cambia subito. Sui permessi c’è una trappola: Reader vede i dati di conformità, Security Reader no; il minimo indicato è Resource Policy Contributor più Security Admin. Da lì scarichi il report PDF o CSV, imposti l’export continuo verso Event Hubs o Log Analytics e agganci una Logic App al cambio di stato di un assessment.
Raccomandazioni e standard personalizzati
Le raccomandazioni personalizzate scritte in KQL funzionano su Azure, AWS e GCP ma richiedono il piano Defender CSPM; senza CSPM resta la strada legacy basata su Azure Policy, aperta a tutti. Servono Owner per creare uno standard e Security Admin per le raccomandazioni. Il percorso è Environment settings, sottoscrizione, Security policies, Create custom recommendation.
Lo schema di output non è negoziabile: sette colonne e una colonna di salute con due soli valori validi, maiuscoli.
| extend HealthStatus = iff(condition, 'UNHEALTHY', 'HEALTHY')
| project Id, Name, Environment, Identifiers, AdditionalData, Record, HealthStatus
Modifica solo la condizione. Le proprietà si leggono da Record.properties.*, il tipo di risorsa si confronta con =~, e soprattutto: devi restituire tutte le risorse in scope, perché una risorsa omessa non viene letta come conforme ma come assenza di dati. Nella via legacy, invece, crei un’iniziativa Azure Policy con "ASC":"true" nei metadata e aggiungi severità e passi di remediation con la proprietà securityCenter.
Sulle raccomandazioni esistenti: le disable rules sono in deprecazione, si usano le esenzioni; le governance rules assegnano proprietario e scadenza, e una raccomandazione pesa sul Secure Score solo quando è scaduta.