mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
YAML-driven test cases for the core Azure Policy compiler. These cover alias resolution, field conditions, logical operators, type coercion, count expressions, template functions, effect compilation, and policy definition parsing. 24 files, each a self-contained scenario exercised by the test runner in the companion code PR. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2130 lines
61 KiB
YAML
2130 lines
61 KiB
YAML
# Versioned alias normalization tests.
|
|
#
|
|
# Each test verifies that the normalizer picks the correct ARM path based on
|
|
# the api_version. Resources contain DIFFERENT values at each possible path
|
|
# so that if the wrong path is selected, the "equals" check fails and the
|
|
# test fails with an unexpected undefined / wrong effect.
|
|
#
|
|
# Aliases file: versioned_aliases.json (Microsoft.Test/versionedResources)
|
|
#
|
|
# Alias | Old API (2015) | New API (2020) | Default (no match)
|
|
# ----------------------|----------------------------------------|----------------------------------|-------------------
|
|
# accountType | properties.accountType | sku.name | properties.accountType
|
|
# config.threshold | properties.config.properties.threshold | properties.config.threshold | properties.config.threshold
|
|
# tier | properties.pricingTier | sku.tier | sku.tier
|
|
# operationMode | properties.legacyMode | properties.mode | properties.mode
|
|
# | (2015-01-01 only) | (2020-01-01 only) |
|
|
# | properties.settings.mode (2017-06-01) | |
|
|
|
|
aliases: versioned_aliases.json
|
|
|
|
cases:
|
|
# =========================================================================
|
|
# 1. simpleProp — no versioning, baseline sanity check
|
|
# =========================================================================
|
|
- note: simple_prop_baseline
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/simpleProp",
|
|
"equals": "hello"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
simpleProp: "hello"
|
|
want_effect: "Deny"
|
|
|
|
# =========================================================================
|
|
# 2. accountType — properties.accountType (old) vs sku.name (new)
|
|
# Resource has DIFFERENT values at each path.
|
|
# =========================================================================
|
|
|
|
# Old API → should read properties.accountType = "Premium_LRS"
|
|
- note: accountType_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Premium_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2015-06-15"
|
|
want_effect: "Deny"
|
|
|
|
# Old API — verify wrong value is NOT matched
|
|
- note: accountType_old_api_wrong_value
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Standard_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2015-06-15"
|
|
want_undefined: true
|
|
|
|
# New API → should read sku.name = "Standard_LRS"
|
|
- note: accountType_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Standard_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
# New API — verify wrong value is NOT matched
|
|
- note: accountType_new_api_wrong_value
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Premium_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2020-01-01"
|
|
want_undefined: true
|
|
|
|
# No API version → default path = properties.accountType
|
|
- note: accountType_no_api_version
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Premium_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
want_effect: "Deny"
|
|
|
|
# Unknown API version → falls back to default = properties.accountType
|
|
- note: accountType_unknown_api_version
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Premium_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2099-01-01"
|
|
want_effect: "Deny"
|
|
|
|
# =========================================================================
|
|
# 3. accountType with fully-qualified alias name in policy
|
|
# Tests both compiler FQ→short resolution AND normalizer versioned path.
|
|
# =========================================================================
|
|
|
|
- note: fq_accountType_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Premium_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2015-06-15"
|
|
want_effect: "Deny"
|
|
|
|
- note: fq_accountType_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/accountType",
|
|
"equals": "Standard_LRS"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
name: "Standard_LRS"
|
|
properties:
|
|
accountType: "Premium_LRS"
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
# =========================================================================
|
|
# 4. config.threshold — extra properties nesting in old API
|
|
# Old: properties.config.properties.threshold = 200
|
|
# New: properties.config.threshold = 100
|
|
# =========================================================================
|
|
|
|
- note: config_threshold_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/config.threshold",
|
|
"equals": 200
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
config:
|
|
threshold: 100
|
|
properties:
|
|
threshold: 200
|
|
api_version: "2015-06-15"
|
|
want_effect: "Deny"
|
|
|
|
- note: config_threshold_old_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/config.threshold",
|
|
"equals": 100
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
config:
|
|
threshold: 100
|
|
properties:
|
|
threshold: 200
|
|
api_version: "2015-06-15"
|
|
want_undefined: true
|
|
|
|
- note: config_threshold_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/config.threshold",
|
|
"equals": 100
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
config:
|
|
threshold: 100
|
|
properties:
|
|
threshold: 200
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: config_threshold_new_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/config.threshold",
|
|
"equals": 200
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
config:
|
|
threshold: 100
|
|
properties:
|
|
threshold: 200
|
|
api_version: "2020-01-01"
|
|
want_undefined: true
|
|
|
|
# =========================================================================
|
|
# 5. tier — properties.pricingTier (old) vs sku.tier (new)
|
|
# =========================================================================
|
|
|
|
- note: tier_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/tier",
|
|
"equals": "Premium"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
tier: "Standard"
|
|
properties:
|
|
pricingTier: "Premium"
|
|
api_version: "2015-06-15"
|
|
want_effect: "Deny"
|
|
|
|
- note: tier_old_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/tier",
|
|
"equals": "Standard"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
tier: "Standard"
|
|
properties:
|
|
pricingTier: "Premium"
|
|
api_version: "2015-06-15"
|
|
want_undefined: true
|
|
|
|
- note: tier_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/tier",
|
|
"equals": "Standard"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
tier: "Standard"
|
|
properties:
|
|
pricingTier: "Premium"
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: tier_new_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/tier",
|
|
"equals": "Premium"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
sku:
|
|
tier: "Standard"
|
|
properties:
|
|
pricingTier: "Premium"
|
|
api_version: "2020-01-01"
|
|
want_undefined: true
|
|
|
|
# =========================================================================
|
|
# 6. operationMode — three distinct paths across versions
|
|
# 2015-01-01: properties.legacyMode = "compat"
|
|
# 2017-06-01: properties.settings.mode = "advanced"
|
|
# 2020-01-01: properties.mode = "standard"
|
|
# default: properties.mode = "standard"
|
|
# =========================================================================
|
|
|
|
- note: operationMode_oldest_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "compat"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
api_version: "2015-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: operationMode_oldest_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "standard"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
api_version: "2015-01-01"
|
|
want_undefined: true
|
|
|
|
- note: operationMode_middle_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "advanced"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
api_version: "2017-06-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: operationMode_middle_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "compat"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
api_version: "2017-06-01"
|
|
want_undefined: true
|
|
|
|
- note: operationMode_newest_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "standard"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: operationMode_default_no_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "standard"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
want_effect: "Deny"
|
|
|
|
# =========================================================================
|
|
# 7. FQ alias with operationMode (mixed-case short name + versioned paths)
|
|
# =========================================================================
|
|
|
|
- note: fq_operationMode_middle_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/versionedResources/operationMode",
|
|
"equals": "advanced"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/versionedResources
|
|
name: testResource
|
|
properties:
|
|
legacyMode: "compat"
|
|
mode: "standard"
|
|
settings:
|
|
mode: "advanced"
|
|
api_version: "2017-06-01"
|
|
want_effect: "Deny"
|
|
|
|
# =========================================================================
|
|
# PART 2 — Complex resource type: Microsoft.Test/complexResources
|
|
#
|
|
# Alias catalog for this type includes:
|
|
# - Sub-resource array: rules[*] (with properties flattening)
|
|
# - Nested sub-resource array: rules[*].filters[*] (2-level flattening)
|
|
# - Primitive array: allowedIPs[*] (strings, no properties wrapper)
|
|
# - Sub-resource element fields: rules[*].action, rules[*].protocol, etc.
|
|
# - Versioned sub-resource field: rules[*].priority (old vs new path)
|
|
# - Nested object alias: config.retryCount (versioned), config.timeout
|
|
# - Deeply nested alias: settings.encryption.enabled (versioned)
|
|
# - Primitive array inside sub-resource: rules[*].targets[*]
|
|
# =========================================================================
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 8. Scalar baseline — enabled field
|
|
# -----------------------------------------------------------------------
|
|
- note: complex_enabled_baseline
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: true
|
|
want_effect: "Deny"
|
|
|
|
- note: complex_enabled_false
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: false
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 9. Sub-resource array: count rules[*]
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_basic
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]"
|
|
},
|
|
"greater": 2
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
- name: r3
|
|
properties:
|
|
action: "Allow"
|
|
want_effect: "Deny"
|
|
|
|
- note: count_rules_not_enough
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]"
|
|
},
|
|
"greater": 5
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 10. Sub-resource with where — access flattened properties
|
|
# rules[*].action lives under properties wrapper in ARM.
|
|
# After normalization, it should be at rules[*].action directly.
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_where_action
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"equals": 2
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
- name: r3
|
|
properties:
|
|
action: "Allow"
|
|
want_effect: "Deny"
|
|
|
|
- note: count_rules_where_action_zero
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Block"
|
|
}
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 11. Sub-resource where with allOf — multiple flattened fields
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_where_allOf
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].direction", "equals": "Inbound" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].protocol", "equals": "Tcp" }
|
|
]
|
|
}
|
|
},
|
|
"greaterOrEquals": 1
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
- name: r2
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Outbound"
|
|
protocol: "Tcp"
|
|
- name: r3
|
|
properties:
|
|
action: "Deny"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
want_effect: "Deny"
|
|
|
|
- note: count_rules_where_allOf_no_match
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].direction", "equals": "Inbound" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].protocol", "equals": "Udp" }
|
|
]
|
|
}
|
|
},
|
|
"greaterOrEquals": 1
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 12. Sub-resource where with anyOf
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_where_anyOf_destPort
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"anyOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "22" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "3389" }
|
|
]
|
|
}
|
|
},
|
|
"notEquals": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
destPort: "443"
|
|
- name: r2
|
|
properties:
|
|
destPort: "22"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 13. Sub-resource where with not
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_where_not
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"not": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Deny"
|
|
}
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 14. Primitive array: count allowedIPs[*]
|
|
# Unlike rules[*], allowedIPs is a plain string array — no properties
|
|
# flattening.
|
|
# -----------------------------------------------------------------------
|
|
- note: count_allowedIPs_basic
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/allowedIPs[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/allowedIPs[*]",
|
|
"like": "10.*"
|
|
}
|
|
},
|
|
"equals": 2
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
allowedIPs:
|
|
- "10.0.0.1"
|
|
- "192.168.1.1"
|
|
- "10.0.0.2"
|
|
want_effect: "Deny"
|
|
|
|
- note: count_allowedIPs_empty
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/allowedIPs[*]"
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
allowedIPs: []
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 15. Primitive array inside sub-resource: rules[*].targets[*]
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_targets_nested
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].targets[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].targets[*]",
|
|
"like": "*.prod.*"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
targets:
|
|
- "app.prod.east"
|
|
- "app.dev.east"
|
|
- name: r2
|
|
properties:
|
|
action: "Allow"
|
|
targets:
|
|
- "api.dev.west"
|
|
want_effect: "Deny"
|
|
|
|
- note: count_rules_targets_nested_no_prod
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].targets[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].targets[*]",
|
|
"like": "*.prod.*"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
targets:
|
|
- "app.dev.east"
|
|
- name: r2
|
|
properties:
|
|
action: "Allow"
|
|
targets:
|
|
- "api.staging.west"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 16. Nested sub-resource arrays: rules[*].filters[*]
|
|
# filters[*] is a sub-resource (has properties wrapper) nested inside
|
|
# rules[*] — two-level properties flattening.
|
|
# -----------------------------------------------------------------------
|
|
- note: nested_sub_resource_count
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*].name",
|
|
"equals": "source-ip"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
filters:
|
|
- properties:
|
|
name: "source-ip"
|
|
value: "10.0.0.0/8"
|
|
- properties:
|
|
name: "protocol"
|
|
value: "tcp"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
filters:
|
|
- properties:
|
|
name: "dest-port"
|
|
value: "22"
|
|
want_effect: "Deny"
|
|
|
|
- note: nested_sub_resource_count_no_match
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*].name",
|
|
"equals": "geo-location"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
filters:
|
|
- properties:
|
|
name: "source-ip"
|
|
value: "10.0.0.0/8"
|
|
want_undefined: true
|
|
|
|
- note: nested_sub_resource_filter_value
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].filters[*].name", "equals": "protocol" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].filters[*].value", "equals": "tcp" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"equals": 1
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
filters:
|
|
- properties:
|
|
name: "protocol"
|
|
value: "tcp"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
filters:
|
|
- properties:
|
|
name: "protocol"
|
|
value: "udp"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 17. Count in allOf: type check + sub-resource count
|
|
# -----------------------------------------------------------------------
|
|
- note: count_in_allOf_with_type_check
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].direction", "equals": "Inbound" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Inbound"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
direction: "Outbound"
|
|
want_effect: "Deny"
|
|
|
|
- note: count_in_allOf_wrong_type
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Other/resources" },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 18. Nested object: config.retryCount — versioned
|
|
# Old API: properties.config.properties.retryCount
|
|
# New API: properties.config.retryCount
|
|
# -----------------------------------------------------------------------
|
|
- note: config_retryCount_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/config.retryCount",
|
|
"greater": 3
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
config:
|
|
retryCount: 2
|
|
properties:
|
|
retryCount: 5
|
|
api_version: "2015-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: config_retryCount_old_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/config.retryCount",
|
|
"greater": 3
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
config:
|
|
retryCount: 2
|
|
properties:
|
|
retryCount: 1
|
|
api_version: "2015-01-01"
|
|
want_undefined: true
|
|
|
|
- note: config_retryCount_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/config.retryCount",
|
|
"greater": 3
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
config:
|
|
retryCount: 5
|
|
properties:
|
|
retryCount: 1
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: config_retryCount_new_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/config.retryCount",
|
|
"greater": 3
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
config:
|
|
retryCount: 2
|
|
properties:
|
|
retryCount: 99
|
|
api_version: "2020-01-01"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 19. Non-versioned nested object: config.timeout
|
|
# -----------------------------------------------------------------------
|
|
- note: config_timeout_baseline
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/config.timeout",
|
|
"greater": 30
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
config:
|
|
timeout: 60
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 20. Deep nested alias: settings.encryption.enabled — versioned
|
|
# Old API (2015): properties.encryptionEnabled (flat)
|
|
# New API (2020): properties.settings.encryption.enabled (deep)
|
|
# -----------------------------------------------------------------------
|
|
- note: encryption_enabled_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/settings.encryption.enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
encryptionEnabled: true
|
|
settings:
|
|
encryption:
|
|
enabled: false
|
|
api_version: "2015-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: encryption_enabled_old_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/settings.encryption.enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
encryptionEnabled: false
|
|
settings:
|
|
encryption:
|
|
enabled: true
|
|
api_version: "2015-01-01"
|
|
want_undefined: true
|
|
|
|
- note: encryption_enabled_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/settings.encryption.enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
encryptionEnabled: false
|
|
settings:
|
|
encryption:
|
|
enabled: true
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: encryption_enabled_new_api_wrong
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/settings.encryption.enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
encryptionEnabled: true
|
|
settings:
|
|
encryption:
|
|
enabled: false
|
|
api_version: "2020-01-01"
|
|
want_undefined: true
|
|
|
|
- note: encryption_enabled_no_api_default
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/settings.encryption.enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
encryptionEnabled: false
|
|
settings:
|
|
encryption:
|
|
enabled: true
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 21. FQ alias: fully-qualified with complex resource
|
|
# -----------------------------------------------------------------------
|
|
- note: fq_config_retryCount_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/config.retryCount",
|
|
"greater": 3
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
config:
|
|
retryCount: 2
|
|
properties:
|
|
retryCount: 5
|
|
api_version: "2015-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: fq_encryption_enabled_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"field": "Microsoft.Test/complexResources/settings.encryption.enabled",
|
|
"equals": true
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
encryptionEnabled: false
|
|
settings:
|
|
encryption:
|
|
enabled: true
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 22. Complex NSG-like: type + count + where(allOf) + anyOf inside where
|
|
# -----------------------------------------------------------------------
|
|
- note: complex_nsg_like_deny_ssh
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].direction", "equals": "Inbound" },
|
|
{
|
|
"anyOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "22" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "*" }
|
|
]
|
|
},
|
|
{
|
|
"anyOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].sourcePort", "equals": "*" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].sourcePort", "equals": "0" }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: allowHTTPS
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
destPort: "443"
|
|
sourcePort: "*"
|
|
- name: allowSSH
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
destPort: "22"
|
|
sourcePort: "*"
|
|
want_effect: "Deny"
|
|
|
|
- note: complex_nsg_like_no_ssh
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].direction", "equals": "Inbound" },
|
|
{
|
|
"anyOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "22" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "*" }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: allowHTTPS
|
|
properties:
|
|
action: "Allow"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
destPort: "443"
|
|
sourcePort: "*"
|
|
- name: denySSH
|
|
properties:
|
|
action: "Deny"
|
|
direction: "Inbound"
|
|
protocol: "Tcp"
|
|
destPort: "22"
|
|
sourcePort: "*"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 23. Value count with nested field count across sub-resources
|
|
# "For each required port, at least one Allow rule must exist"
|
|
# -----------------------------------------------------------------------
|
|
- note: value_count_nested_field_count_rules
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": "[parameters('requiredPorts')]",
|
|
"name": "port",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "[current('port')]" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"equals": "[length(parameters('requiredPorts'))]"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
parameters:
|
|
requiredPorts:
|
|
- "443"
|
|
- "80"
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: allowHTTPS
|
|
properties:
|
|
action: "Allow"
|
|
destPort: "443"
|
|
- name: allowHTTP
|
|
properties:
|
|
action: "Allow"
|
|
destPort: "80"
|
|
- name: denySSH
|
|
properties:
|
|
action: "Deny"
|
|
destPort: "22"
|
|
want_effect: "Deny"
|
|
|
|
- note: value_count_nested_field_count_rules_missing
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"value": "[parameters('requiredPorts')]",
|
|
"name": "port",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].destPort", "equals": "[current('port')]" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"equals": "[length(parameters('requiredPorts'))]"
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
parameters:
|
|
requiredPorts:
|
|
- "443"
|
|
- "80"
|
|
- "8080"
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: allowHTTPS
|
|
properties:
|
|
action: "Allow"
|
|
destPort: "443"
|
|
- name: allowHTTP
|
|
properties:
|
|
action: "Allow"
|
|
destPort: "80"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 24. Empty sub-resource array — count = 0
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_empty_array
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]"
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules: []
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 25. Missing sub-resource array — count = 0
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_missing_array
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]"
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: true
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 26. Sub-resource where accessing root-level field (name on element)
|
|
# The "name" field lives at the element root, not under properties.
|
|
# -----------------------------------------------------------------------
|
|
- note: count_rules_where_name_field
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].name",
|
|
"like": "allow*"
|
|
}
|
|
},
|
|
"equals": 2
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: allowHTTPS
|
|
properties:
|
|
action: "Allow"
|
|
- name: allowHTTP
|
|
properties:
|
|
action: "Allow"
|
|
- name: denySSH
|
|
properties:
|
|
action: "Deny"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 27. Mixed: scalar alias AND count in allOf
|
|
# -----------------------------------------------------------------------
|
|
- note: enabled_and_rule_count_allOf
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/enabled", "equals": true },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"greater": 3
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: true
|
|
rules:
|
|
- name: r1
|
|
properties: { action: "Allow" }
|
|
- name: r2
|
|
properties: { action: "Allow" }
|
|
- name: r3
|
|
properties: { action: "Allow" }
|
|
- name: r4
|
|
properties: { action: "Allow" }
|
|
want_effect: "Deny"
|
|
|
|
- note: enabled_false_short_circuits
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/enabled", "equals": true },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Allow"
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: false
|
|
rules:
|
|
- name: r1
|
|
properties: { action: "Allow" }
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 28. Count in not — negated sub-resource count
|
|
# -----------------------------------------------------------------------
|
|
- note: not_count_rules
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"not": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]"
|
|
},
|
|
"lessOrEquals": 2
|
|
}
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties: { action: "Allow" }
|
|
- name: r2
|
|
properties: { action: "Allow" }
|
|
- name: r3
|
|
properties: { action: "Deny" }
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 29. Sub-resource element without properties wrapper
|
|
# Some elements may just have top-level fields, no properties.
|
|
# The normalizer should handle this gracefully.
|
|
# -----------------------------------------------------------------------
|
|
- note: sub_resource_no_properties_wrapper
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].name",
|
|
"equals": "direct"
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: "direct"
|
|
action: "Allow"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 30. Deep nesting: allOf > anyOf > not > count
|
|
# -----------------------------------------------------------------------
|
|
- note: deep_logical_with_count
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{
|
|
"anyOf": [
|
|
{
|
|
"not": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Deny"
|
|
}
|
|
},
|
|
"greaterOrEquals": 1
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Allow"
|
|
want_effect: "Deny"
|
|
|
|
- note: deep_logical_with_count_has_deny
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{
|
|
"anyOf": [
|
|
{
|
|
"not": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].action",
|
|
"equals": "Deny"
|
|
}
|
|
},
|
|
"greaterOrEquals": 1
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
want_undefined: true
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 31. Missing field in sub-resource element — exists false
|
|
# -----------------------------------------------------------------------
|
|
- note: exists_false_in_sub_resource
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].protocol",
|
|
"exists": false
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
protocol: "Tcp"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
want_effect: "Deny"
|
|
|
|
- note: exists_true_in_sub_resource_all_present
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].protocol",
|
|
"exists": false
|
|
}
|
|
},
|
|
"equals": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
protocol: "Tcp"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
protocol: "Udp"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 32. Versioned sub-resource field: rules[*].priority
|
|
# Old API (2015): properties.rules[*].properties.priority
|
|
# New API (2020): properties.rules[*].properties.prio
|
|
# The "priority" alias always maps to a field under the element's
|
|
# properties wrapper — the difference is the field NAME changes
|
|
# across versions.
|
|
#
|
|
# NOTE: Versioned paths for sub-resource element fields are handled
|
|
# at the structural level (the alias defines the full ARM path).
|
|
# After normalization, the alias short name "priority" should resolve
|
|
# correctly. We place DIFFERENT values at "priority" vs "prio" in the
|
|
# elements to test path selection.
|
|
# -----------------------------------------------------------------------
|
|
|
|
# Versioned sub-resource element fields use per-element alias resolution
|
|
# during normalization. After structural flattening, the normalizer remaps
|
|
# element-level field names when the selected API path differs from the
|
|
# alias short name (e.g., "prio" → "priority" for new API versions).
|
|
- note: rules_priority_old_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].priority",
|
|
"greater": 100
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
priority: 200
|
|
prio: 50
|
|
api_version: "2015-01-01"
|
|
want_effect: "Deny"
|
|
|
|
- note: rules_priority_new_api
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].priority",
|
|
"greater": 100
|
|
}
|
|
},
|
|
"greater": 0
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
priority: 50
|
|
prio: 200
|
|
api_version: "2020-01-01"
|
|
want_effect: "Deny"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 33. Three-level nesting: allOf > count(rules) > where > count(filters)
|
|
# > where > allOf (filter name + value)
|
|
# -----------------------------------------------------------------------
|
|
- note: three_level_nesting_count_in_allof
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{ "field": "Microsoft.Test/complexResources/enabled", "equals": true },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].action", "equals": "Allow" },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*]",
|
|
"where": {
|
|
"allOf": [
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].filters[*].name", "equals": "protocol" },
|
|
{ "field": "Microsoft.Test/complexResources/rules[*].filters[*].value", "equals": "tcp" }
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: true
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
filters:
|
|
- properties:
|
|
name: "protocol"
|
|
value: "tcp"
|
|
- properties:
|
|
name: "source-ip"
|
|
value: "10.0.0.0/8"
|
|
- name: r2
|
|
properties:
|
|
action: "Deny"
|
|
filters:
|
|
- properties:
|
|
name: "protocol"
|
|
value: "udp"
|
|
want_effect: "Deny"
|
|
|
|
- note: three_level_nesting_enabled_false
|
|
policy_rule: |
|
|
{
|
|
"if": {
|
|
"allOf": [
|
|
{ "field": "type", "equals": "Microsoft.Test/complexResources" },
|
|
{ "field": "Microsoft.Test/complexResources/enabled", "equals": true },
|
|
{
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*]",
|
|
"where": {
|
|
"count": {
|
|
"field": "Microsoft.Test/complexResources/rules[*].filters[*]"
|
|
},
|
|
"greater": 0
|
|
}
|
|
},
|
|
"greater": 0
|
|
}
|
|
]
|
|
},
|
|
"then": { "effect": "Deny" }
|
|
}
|
|
resource:
|
|
type: Microsoft.Test/complexResources
|
|
name: myResource
|
|
properties:
|
|
enabled: false
|
|
rules:
|
|
- name: r1
|
|
properties:
|
|
action: "Allow"
|
|
filters:
|
|
- properties: { name: "x", value: "y" }
|
|
want_undefined: true
|