Preventive Controls — Azure · AWS · GCP — Side-by-Side Reference
Deny policies are preventive guardrails — they block actions even if an identity has allow permissions. Unlike allow policies (which grant access), deny policies override everything. They're the "seatbelt" of cloud security — you can have a driver's license, but the car won't start if the seatbelt isn't fastened.
Block accidental deletion of production resources
Prevent public exposure of storage buckets
Enforce encryption on all storage accounts
Restrict VM creation to approved regions only
Block creation of resources without required tags
Prevent disabling of audit logging
Even if an admin account is compromised, certain destructive actions are blocked
Organization-wide guardrails cannot be bypassed by project owners
| Scenario | Allow Policy | Deny Policy | Result |
|---|---|---|---|
| User has Storage Admin | ✅ Yes | ❌ Deny: No public buckets | DENIED |
| User has Org Admin | ✅ Yes | ❌ Deny: No deletion of audit logs | DENIED |
| User has no permissions | ❌ No | — (none) | DENIED |
| User has Storage Admin | ✅ Yes | — (none) | ALLOWED |
Key insight: Even Organization Admins cannot override a deny policy. Deny always wins.
| Azure | AWS | GCP | |
|---|---|---|---|
| Top-level scope | Management Group | Organization (SCPs) | Organization (Org Policies) |
| Mid-level scope | Subscription | OU / Account | Folder |
| Low-level scope | Resource Group | Account / Permission Boundary | Project |
| Inheritance | Top-down (Management Group → Subscription → RG) | Top-down (Org → OU → Account) | Top-down (Org → Folder → Project) |
| Can lower-level override? | No | No | No |
constraints/compute.restrictProtocolForwardingCreationForTypes)| Evaluation Step | Azure | AWS | GCP |
|---|---|---|---|
| 1 | Authentication | Authentication | Authentication |
| 2 | RBAC check (allow) | SCP check (DENY) | IAM Deny check (DENY) |
| 3 | Azure Policy check (DENY) | Permission Boundary check (DENY) | Org Policy check (DENY) |
| 4 | Request forwarded to Resource Provider | IAM Policy check (ALLOW) | IAM Allow check (ALLOW) |
| 5 | — | Resource-based policy check (ALLOW) | VPC Service Controls (DENY) |
Common pattern: All three check DENY first, then ALLOW. If deny fires, the request stops immediately.
{
"if": {
"allOf": [{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
}, {
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
"equals": true
}]
},
"then": {
"effect": "deny"
}
}{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": ["s3:PutBucketPublicAccessBlock"],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"s3:PublicAccessBlockConfiguration": "true"
}
}
}]
}# Org Policy Constraint (Console) Constraint: storage.uniformBucketLevelAccess Policy: Enforce Scope: Organization / Folder / Project # OR — IAM Deny Policy (gcloud) gcloud iam policies create deny-public-buckets \ --attachment-point=organizations/ORG_ID \ --deny-rule=storage.buckets.setIamPolicy
{
"if": {
"allOf": [{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
}, {
"not": {
"field": "Microsoft.Compute/virtualMachines/sku.name",
"in": ["Standard_B2s", "Standard_D2s_v5"]
}
}]
},
"then": { "effect": "deny" }
}{
"Version": "2012-10-17",
"Statement": [{
"Sid": "BlockExpensiveEC2",
"Effect": "Deny",
"Action": ["ec2:RunInstances"],
"Resource": "arn:aws:ec2:*:*:\instance/*",
"Condition": {
"StringNotEquals": {
"ec2:InstanceType": ["t3.micro", "t3.small"]
}
}
}]
}# Org Policy: restrict machine types Constraint: compute.restrictMachineTypes Policy: Allow List Allowed values: - n2-standard-2 - n2-standard-4 - e2-medium - e2-standard-2
Azure Resource Locks:
# CLI: az lock create --name ProdNoDelete \ --resource-group prod-rg \ --lock-type CanNotDelete # CanNotDelete = authorized users can read/modify but NOT delete # ReadOnly = authorized users can read but NOT modify/delete
SCP: Deny deletion of production resources
{
"Sid": "DenyDeleteProd",
"Effect": "Deny",
"Action": [
"ec2:TerminateInstances",
"s3:DeleteBucket",
"rds:DeleteDBInstance"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/env": "prod"
}
}
}IAM Deny Policy (new):
# Deny specific destructive actions
gcloud iam policies create no-delete-prod \
--attachment-point=cloudresourcemanager.googleapis.com/folders/PROD_FOLDER_ID \
--kind=denypolicies \
--deny-rule=compute.instances.delete \
--deny-rule=compute.disks.delete \
--deny-rule=storage.buckets.delete| Azure | AWS | GCP |
|---|---|---|
| Console: Policy → Definitions Or: Management Groups → select MG → Policies |
Console: AWS Organizations → Policies → SCPs Or: IAM → Policies → Create Policy (Permission Boundaries) |
Console: IAM & Admin → Organization Policies Or: IAM & Admin → IAM → Deny (for new IAM Deny Policies) |
| Azure | AWS | GCP |
|---|---|---|
|
+ Create Policy Definition Location: Management Group or Subscription Name: e.g., "Deny Public Storage" Category: Storage or Custom Policy rule: Paste JSON with "effect": "deny"
|
+ Create Policy Type: Service Control Policy Name: e.g., "Block-S3-Public-Access" Description: Prevents public S3 buckets Policy: Paste JSON with "Effect": "Deny"
|
+ Select Constraint (for org policies) Filter: Search by service (e.g., "storage") Select: e.g., storage.uniformBucketLevelAccessCustom Constraint (optional): YAML with CEL OR: + Create Deny Policy (for IAM Deny) |
| Azure | AWS | GCP |
|---|---|---|
|
Assign Policy → Select Scope • Management Group • Subscription • Resource Group Exclusions: You can exclude specific subscriptions or RGs |
Attach SCP: • Root (entire org) • Specific OU (e.g., prod OU) • Specific Account Note: SCPs don't apply to the management account |
Set Policy: • Organization • Folder • Project Policy value: Enforce (block) or Allow List (allow only listed) |
| Azure | AWS | GCP |
|---|---|---|
|
• Use Policy Compliance tab • Create a test resource that violates policy → see denied • Azure Activity Log shows the deny evaluation • az policy state list for CLI audit
|
• Use IAM Policy Simulator • Try restricted action → AccessDenied • CloudTrail logs show explicit deny• aws iam simulate-principal-policy
|
• Use Policy Analyzer to test permissions • Try restricted action → PERMISSION_DENIED• Cloud Audit Logs show policy denial • gcloud org-policies describe to verify
|
# Create policy definition az policy definition create \ --name deny-public-storage \ --rules policy.json # Assign to subscription az policy assignment create \ --policy deny-public-storage \ --scope /subscriptions/SUB_ID # Check compliance az policy state list \ --policy-assignment deny-public-storage
# Create SCP aws organizations create-policy \ --name BlockPublicBuckets \ --type SERVICE_CONTROL_POLICY \ --content file://scp.json # Attach to OU aws organizations attach-policy \ --policy-id p-xxxxx \ --target-id ou-xxxxx # Simulate aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::... \ --action-names s3:CreateBucket
# Set org policy constraint gcloud org-policies set-policy policy.yaml \ --organization=ORG_ID # Describe constraint gcloud org-policies describe \ constraints/storage.uniformBucketLevelAccess \ --organization=ORG_ID # Create IAM deny policy gcloud iam policies create deny-prod-delete \ --attachment-point=folders/PROD_FOLDER \ --kind=denypolicies \ --deny-rule=compute.instances.delete
| Goal | Azure | AWS | GCP |
|---|---|---|---|
| Block all public buckets | Azure Policy (Deny) | SCP (Deny s3:PutBucketAcl) | Org Policy (storage.uniformBucketLevelAccess) |
| Restrict regions | Azure Policy (allowedLocations) | SCP (Condition: aws:RequestedRegion) | Org Policy (constraints/gcp.resourceLocations) |
| Enforce resource tags | Azure Policy (require tag + deny) | SCP (Condition: aws:RequestTag) | Org Policy (custom constraint + CEL) |
| Prevent IAM changes | Azure Policy (Deny Action) | SCP (Deny iam:*) | IAM Deny Policy |
| Block VM deletion | Resource Lock (CanNotDelete) | SCP + Tag condition | IAM Deny Policy + Deletion Protection |
| Prevent data exfiltration | Azure Firewall / Private Endpoints | VPC Endpoint Policies | VPC Service Controls |