# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Complex Policies Test Suite
# Tests realistic, multi-layer Azure Policy definitions covering combinations of
# operators, logical combinators, expressions, fields, counts, and effects.

cases:
  # =========================================================================
  # Require HTTPS for storage accounts
  # =========================================================================

  - note: require_https_storage
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "equals": "Microsoft.Storage/storageAccounts"
            },
            {
              "field": "properties.supportsHttpsTrafficOnly",
              "notEquals": true
            }
          ]
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Storage/storageAccounts"
      properties:
        supportsHttpsTrafficOnly: false
    want_effect: "deny"

  # =========================================================================
  # Allowed locations with parameterized effect
  # =========================================================================

  - note: allowed_locations_parameterized
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "location",
              "notIn": "[parameters('allowedLocations')]"
            },
            {
              "field": "location",
              "notEquals": "global"
            },
            {
              "field": "type",
              "notEquals": "Microsoft.AzureActiveDirectory/b2cDirectories"
            }
          ]
        },
        "then": {
          "effect": "[parameters('effect')]"
        }
      }
    parameters:
      allowedLocations:
        - "eastus"
        - "westus"
      effect: "deny"
    resource:
      type: "Microsoft.Compute/virtualMachines"
      location: "northeurope"
    want_effect: "deny"

  # =========================================================================
  # Require tags with parameter-driven enforcement
  # =========================================================================

  - note: require_tag_environment
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "notEquals": "Microsoft.Resources/subscriptions"
            },
            {
              "field": "[concat('tags[', parameters('tagName'), ']')]",
              "exists": false
            }
          ]
        },
        "then": {
          "effect": "deny",
          "details": {
            "message": "Required tag is missing"
          }
        }
      }
    parameters:
      tagName: "environment"
    resource:
      type: "Microsoft.Compute/virtualMachines"
      tags: {}
    want_effect: "deny"

  # =========================================================================
  # NSG rule restriction — deny risky inbound ports
  # =========================================================================

  - note: deny_risky_inbound_nsg
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "equals": "Microsoft.Network/networkSecurityGroups/securityRules"
            },
            {
              "field": "properties.direction",
              "equals": "Inbound"
            },
            {
              "field": "properties.access",
              "equals": "Allow"
            },
            {
              "anyOf": [
                {
                  "field": "properties.destinationPortRange",
                  "in": ["22", "3389", "*"]
                },
                {
                  "field": "properties.sourceAddressPrefix",
                  "in": ["*", "Internet", "0.0.0.0/0"]
                }
              ]
            }
          ]
        },
        "then": {
          "effect": "deny",
          "details": {
            "message": "Risky inbound NSG rules are not allowed"
          }
        }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups/securityRules"
      properties:
        direction: "Inbound"
        access: "Allow"
        destinationPortRange: "22"
        sourceAddressPrefix: "*"
    want_effect: "deny"

  # =========================================================================
  # Modify — add tags if missing
  # =========================================================================

  - note: modify_add_tags
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "equals": "Microsoft.Compute/virtualMachines"
            },
            {
              "anyOf": [
                { "field": "tags.environment", "exists": false },
                { "field": "tags.costCenter", "exists": false }
              ]
            }
          ]
        },
        "then": {
          "effect": "modify",
          "details": {
            "roleDefinitionIds": [
              "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
            ],
            "operations": [
              {
                "operation": "addOrReplace",
                "field": "tags['environment']",
                "value": "[if(empty(field('tags.environment')), 'unknown', field('tags.environment'))]"
              },
              {
                "operation": "addOrReplace",
                "field": "tags['costCenter']",
                "value": "[if(empty(field('tags.costCenter')), 'unassigned', field('tags.costCenter'))]"
              }
            ]
          }
        }
      }
    resource:
      type: "Microsoft.Compute/virtualMachines"
      tags:
        environment: "prod"
    want_effect: "modify"

  # =========================================================================
  # Count — deny if too many open NSG rules
  # =========================================================================

  - note: deny_excessive_open_nsg_rules
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "equals": "Microsoft.Network/networkSecurityGroups"
            },
            {
              "count": {
                "field": "securityRules[*]",
                "where": {
                  "allOf": [
                    {
                      "field": "securityRules[*].access",
                      "equals": "Allow"
                    },
                    {
                      "field": "securityRules[*].direction",
                      "equals": "Inbound"
                    },
                    {
                      "field": "securityRules[*].sourceAddressPrefix",
                      "equals": "*"
                    }
                  ]
                }
              },
              "greater": 0
            }
          ]
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Network/networkSecurityGroups"
      securityRules:
        -
          access: "Allow"
          direction: "Inbound"
          sourceAddressPrefix: "*"
    want_effect: "deny"

  # =========================================================================
  # AuditIfNotExists — require diagnostics settings
  # =========================================================================

  - note: audit_diagnostics_settings
    policy_rule: |
      {
        "if": {
          "field": "type",
          "equals": "Microsoft.KeyVault/vaults"
        },
        "then": {
          "effect": "auditIfNotExists",
          "details": {
            "type": "Microsoft.Insights/diagnosticSettings",
            "existenceCondition": {
              "allOf": [
                {
                  "field": "properties.logs.enabled",
                  "equals": true
                },
                {
                  "field": "properties.logs.retentionPolicy.enabled",
                  "equals": true
                },
                {
                  "field": "properties.logs.retentionPolicy.days",
                  "greaterOrEquals": 90
                }
              ]
            }
          }
        }
      }
    resource:
      type: "Microsoft.KeyVault/vaults"
    host_await:
      - key:
          operation: "lookup_related_resources"
          type: "Microsoft.Insights/diagnosticSettings"
        response: null
    want_effect: "auditIfNotExists"

  # =========================================================================
  # DeployIfNotExists — deploy monitoring agent
  # =========================================================================

  - note: deploy_monitoring_agent
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "equals": "Microsoft.Compute/virtualMachines"
            },
            {
              "field": "properties.storageProfile.imageReference.publisher",
              "equals": "Canonical"
            }
          ]
        },
        "then": {
          "effect": "deployIfNotExists",
          "details": {
            "type": "Microsoft.Compute/virtualMachines/extensions",
            "roleDefinitionIds": [
              "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"
            ],
            "existenceCondition": {
              "allOf": [
                {
                  "field": "properties.publisher",
                  "equals": "Microsoft.Azure.Monitor"
                },
                {
                  "field": "properties.type",
                  "equals": "AzureMonitorLinuxAgent"
                }
              ]
            },
            "deployment": {
              "properties": {
                "mode": "incremental",
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "parameters": {},
                  "resources": []
                }
              }
            }
          }
        }
      }
    resource:
      type: "Microsoft.Compute/virtualMachines"
      properties:
        storageProfile:
          imageReference:
            publisher: "Canonical"
    host_await:
      - response: null
    want_effect: "deployIfNotExists"

  # =========================================================================
  # Value count with complex where — required tags
  # =========================================================================

  - note: required_tags_value_count
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "field": "type",
              "notEquals": "Microsoft.Resources/subscriptions"
            },
            {
              "count": {
                "value": "[parameters('requiredTags')]",
                "name": "tagName",
                "where": {
                  "field": "[concat('tags[', current('tagName'), ']')]",
                  "exists": true
                }
              },
              "notEquals": "[length(parameters('requiredTags'))]"
            }
          ]
        },
        "then": {
          "effect": "deny",
          "details": {
            "message": "Not all required tags are present"
          }
        }
      }
    parameters:
      requiredTags:
        - "environment"
        - "costCenter"
        - "owner"
    resource:
      type: "Microsoft.Compute/virtualMachines"
      tags:
        environment: "prod"
        costCenter: "12345"
    want_effect: "deny"

  # =========================================================================
  # Multi-resource type policy with not
  # =========================================================================

  - note: multi_type_with_not
    policy_rule: |
      {
        "if": {
          "allOf": [
            {
              "not": {
                "anyOf": [
                  { "field": "type", "equals": "Microsoft.Resources/subscriptions" },
                  { "field": "type", "equals": "Microsoft.Resources/subscriptions/resourceGroups" },
                  { "field": "type", "equals": "Microsoft.Authorization/roleAssignments" }
                ]
              }
            },
            {
              "field": "location",
              "notIn": "[parameters('allowedLocations')]"
            }
          ]
        },
        "then": {
          "effect": "[parameters('effect')]",
          "details": {
            "message": "Resource location is not in the allowed list"
          }
        }
      }
    parameters:
      allowedLocations:
        - "eastus"
        - "westus"
        - "centralus"
      effect: "deny"
    resource:
      type: "Microsoft.Compute/virtualMachines"
      location: "southeastasia"
    want_effect: "deny"

  # =========================================================================
  # Condition-free policy (always true if block is trivially satisfied)
  # =========================================================================

  - note: trivial_allOf_empty
    policy_rule: |
      {
        "if": {
          "allOf": []
        },
        "then": { "effect": "audit" }
      }
    resource:
      type: "anything"
    want_effect: "audit"

  # =========================================================================
  # Exists mixed with value comparisons
  # =========================================================================

  - note: exists_and_value_check
    policy_rule: |
      {
        "if": {
          "allOf": [
            { "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
            { "field": "properties.networkAcls", "exists": true },
            { "field": "properties.networkAcls.defaultAction", "notEquals": "Deny" }
          ]
        },
        "then": { "effect": "deny" }
      }
    resource:
      type: "Microsoft.Storage/storageAccounts"
      properties:
        networkAcls:
          defaultAction: "Allow"
    want_effect: "deny"
